summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/nvidia/tegra/dc.h1
-rw-r--r--src/soc/nvidia/tegra/types.h76
-rw-r--r--src/soc/nvidia/tegra132/Makefile.inc7
-rw-r--r--src/soc/nvidia/tegra132/chip.h4
-rw-r--r--src/soc/nvidia/tegra132/display.c297
-rw-r--r--src/soc/nvidia/tegra132/include/soc/addressmap.h3
-rw-r--r--src/soc/nvidia/tegra132/include/soc/display.h187
-rw-r--r--src/soc/nvidia/tegra132/include/soc/mipi-phy.h46
-rw-r--r--src/soc/nvidia/tegra132/include/soc/mipi_display.h148
-rw-r--r--src/soc/nvidia/tegra132/include/soc/mipi_dsi.h283
-rw-r--r--src/soc/nvidia/tegra132/include/soc/tegra_dsi.h219
-rw-r--r--src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.c213
-rw-r--r--src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.h131
-rw-r--r--src/soc/nvidia/tegra132/mipi-phy.c92
-rw-r--r--src/soc/nvidia/tegra132/mipi.c151
-rw-r--r--src/soc/nvidia/tegra132/mipi_dsi.c431
-rw-r--r--src/soc/nvidia/tegra132/soc.c5
-rw-r--r--src/soc/nvidia/tegra132/tegra_dsi.c874
18 files changed, 2986 insertions, 182 deletions
diff --git a/src/soc/nvidia/tegra/dc.h b/src/soc/nvidia/tegra/dc.h
index ff36a0b589..0d2d7618a9 100644
--- a/src/soc/nvidia/tegra/dc.h
+++ b/src/soc/nvidia/tegra/dc.h
@@ -24,6 +24,7 @@
#ifndef __SOC_NVIDIA_TEGRA_DC_H
#define __SOC_NVIDIA_TEGRA_DC_H
+#include <stddef.h>
/* Register definitions for the Tegra display controller */
diff --git a/src/soc/nvidia/tegra/types.h b/src/soc/nvidia/tegra/types.h
new file mode 100644
index 0000000000..ce0b6bb088
--- /dev/null
+++ b/src/soc/nvidia/tegra/types.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __TEGRA_MISC_TYPES_H__
+#define __TEGRA_MISC_TYPES_H__
+
+#define EFAULT 1
+#define EINVAL 2
+#define ETIMEDOUT 3
+#define ENOSPC 4
+#define ENOSYS 5
+#define EPTR 6
+
+#define IS_ERR_PTR(ptr) \
+ (ptr == (void *)-EPTR)
+
+#ifndef bool
+#define bool int
+#endif
+
+#ifndef false
+#define false 0
+#endif
+
+#ifndef true
+#define true 1
+#endif
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
+
+/*
+ * Divide positive or negative dividend by positive divisor and round
+ * to closest integer. Result is undefined for negative divisors and
+ * for negative dividends if the divisor variable type is unsigned.
+ */
+#define DIV_ROUND_CLOSEST(x, divisor)( \
+{ \
+ typeof(x) __x = x; \
+ typeof(divisor) __d = divisor; \
+ (((typeof(x))-1) > 0 || \
+ ((typeof(divisor))-1) > 0 || (__x) > 0) ? \
+ (((__x) + ((__d) / 2)) / (__d)) : \
+ (((__x) - ((__d) / 2)) / (__d)); \
+} \
+)
+
+#endif /* __TEGRA_MISC_TYPES_H__ */
diff --git a/src/soc/nvidia/tegra132/Makefile.inc b/src/soc/nvidia/tegra132/Makefile.inc
index 117f629b46..a7485fa398 100644
--- a/src/soc/nvidia/tegra132/Makefile.inc
+++ b/src/soc/nvidia/tegra132/Makefile.inc
@@ -66,6 +66,13 @@ ramstage-y += cbmem.c
ramstage-y += cpu.c
ramstage-y += cpu_lib.S
ramstage-y += clock.c
+ramstage-y += display.c
+ramstage-y += tegra_dsi.c
+ramstage-y += mipi_dsi.c
+ramstage-y += mipi.c
+ramstage-y += mipi-phy.c
+ramstage-y += ./jdi_25x18_display/panel-jdi-lpm102a188a.c
+
ramstage-y += soc.c
ramstage-y += spi.c
ramstage-y += i2c.c
diff --git a/src/soc/nvidia/tegra132/chip.h b/src/soc/nvidia/tegra132/chip.h
index 23f2cd8f38..35e72d59e6 100644
--- a/src/soc/nvidia/tegra132/chip.h
+++ b/src/soc/nvidia/tegra132/chip.h
@@ -19,13 +19,9 @@
#ifndef __SOC_NVIDIA_TEGRA132_CHIP_H__
#define __SOC_NVIDIA_TEGRA132_CHIP_H__
-#include <arch/cache.h>
#include <soc/addressmap.h>
#include <stdint.h>
-#define EFAULT 1
-#define EINVAL 2
-
struct soc_nvidia_tegra132_config {
/* Address to monitor if spintable employed. */
uintptr_t spintable_addr;
diff --git a/src/soc/nvidia/tegra132/display.c b/src/soc/nvidia/tegra132/display.c
new file mode 100644
index 0000000000..62cea026e5
--- /dev/null
+++ b/src/soc/nvidia/tegra132/display.c
@@ -0,0 +1,297 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <lib.h>
+#include <stdlib.h>
+#include <delay.h>
+#include <soc/addressmap.h>
+#include <device/device.h>
+#include <device/i2c.h>
+#include <string.h>
+#include <cpu/cpu.h>
+#include <boot/tables.h>
+#include <cbmem.h>
+#include <soc/clock.h>
+#include <soc/nvidia/tegra/dc.h>
+#include <soc/funitcfg.h>
+#include "chip.h"
+#include <soc/display.h>
+
+int dump = 0;
+unsigned long READL(void * p)
+{
+ unsigned long value;
+
+ /*
+ * In case of hard hung on readl(p), we can set dump > 1 to print out
+ * the address accessed.
+ */
+ if (dump > 1)
+ printk(BIOS_SPEW, "readl %p\n", p);
+
+ value = readl(p);
+ if (dump)
+ printk(BIOS_SPEW, "readl %p %08lx\n", p, value);
+ return value;
+}
+
+void WRITEL(unsigned long value, void * p)
+{
+ if (dump)
+ printk(BIOS_SPEW, "writel %p %08lx\n", p, value);
+ writel(value, p);
+}
+
+/* return in 1000ths of a Hertz */
+static int tegra_calc_refresh(const struct soc_nvidia_tegra132_config *config)
+{
+ int refresh;
+ int h_total = htotal(config);
+ int v_total = vtotal(config);
+ int pclk = config->pixel_clock;
+
+ if (!pclk || !h_total || !v_total)
+ return 0;
+ refresh = pclk / h_total;
+ refresh *= 1000;
+ refresh /= v_total;
+ return refresh;
+}
+
+static void print_mode(const struct soc_nvidia_tegra132_config *config)
+{
+ if (config) {
+ int refresh = tegra_calc_refresh(config);
+ printk(BIOS_ERR,
+ "MODE:%dx%d@%d.%03uHz pclk=%d\n",
+ config->xres, config->yres,
+ refresh / 1000, refresh % 1000,
+ config->pixel_clock);
+ }
+}
+
+static int update_display_mode(struct display_controller *disp_ctrl,
+ struct soc_nvidia_tegra132_config *config)
+{
+ print_mode(config);
+
+ printk(BIOS_ERR, "config: xres:yres: %d x %d\n ",
+ config->xres, config->yres);
+ printk(BIOS_ERR, " href_sync:vref_sync: %d x %d\n ",
+ config->href_to_sync, config->vref_to_sync);
+ printk(BIOS_ERR, " hsyn_width:vsyn_width: %d x %d\n ",
+ config->hsync_width, config->vsync_width);
+ printk(BIOS_ERR, " hfnt_porch:vfnt_porch: %d x %d\n ",
+ config->hfront_porch, config->vfront_porch);
+ printk(BIOS_ERR, " hbk_porch:vbk_porch: %d x %d\n ",
+ config->hback_porch, config->vback_porch);
+
+ WRITEL(0x0, &disp_ctrl->disp.disp_timing_opt);
+ WRITEL(0x0, &disp_ctrl->disp.disp_color_ctrl);
+
+ // select DSI
+ WRITEL(DSI_ENABLE, &disp_ctrl->disp.disp_win_opt);
+
+ WRITEL(config->vref_to_sync << 16 | config->href_to_sync,
+ &disp_ctrl->disp.ref_to_sync);
+
+ WRITEL(config->vsync_width << 16 | config->hsync_width,
+ &disp_ctrl->disp.sync_width);
+
+
+ WRITEL((config->vback_porch << 16) | config->hback_porch,
+ &disp_ctrl->disp.back_porch);
+
+ WRITEL((config->vfront_porch << 16) | config->hfront_porch,
+ &disp_ctrl->disp.front_porch);
+
+ WRITEL(config->xres | (config->yres << 16),
+ &disp_ctrl->disp.disp_active);
+
+ /**
+ * We want to use PLLD_out0, which is PLLD / 2:
+ * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv.
+ *
+ * Currently most panels work inside clock range 50MHz~100MHz, and PLLD
+ * has some requirements to have VCO in range 500MHz~1000MHz (see
+ * clock.c for more detail). To simplify calculation, we set
+ * PixelClockDiv to 1 and ShiftClockDiv to 1. In future these values
+ * may be calculated by clock_display, to allow wider frequency range.
+ *
+ * Note ShiftClockDiv is a 7.1 format value.
+ */
+ const u32 shift_clock_div = 1;
+ WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
+ ((shift_clock_div - 1) * 2 + 1) << SHIFT_CLK_DIVIDER_SHIFT,
+ &disp_ctrl->disp.disp_clk_ctrl);
+ printk(BIOS_DEBUG, "%s: PixelClock=%u, ShiftClockDiv=%u\n",
+ __func__, config->pixel_clock, shift_clock_div);
+ return 0;
+}
+
+/*
+ * update_window:
+ * set up window registers and activate window except two:
+ * frame buffer base address register (WINBUF_START_ADDR) and
+ * display enable register (_DISP_DISP_WIN_OPTIONS). This is
+ * becasue framebuffer is not available until payload stage.
+ */
+static void update_window(const struct soc_nvidia_tegra132_config *config)
+{
+ struct display_controller *disp_ctrl =
+ (void *)config->display_controller;
+ u32 val;
+
+ WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
+
+ WRITEL(((config->yres << 16) | config->xres), &disp_ctrl->win.size);
+ WRITEL(((config->yres << 16) |
+ (config->xres * config->framebuffer_bits_per_pixel / 8)),
+ &disp_ctrl->win.prescaled_size);
+
+ val = ALIGN_UP((config->xres * config->framebuffer_bits_per_pixel / 8),
+ 64);
+ WRITEL(val, &disp_ctrl->win.line_stride);
+
+ WRITEL(config->color_depth, &disp_ctrl->win.color_depth);
+ WRITEL(COLOR_BLACK, &disp_ctrl->disp.blend_background_color);
+ WRITEL((V_DDA_INC(0x1000) | H_DDA_INC(0x1000)),
+ &disp_ctrl->win.dda_increment);
+
+ WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd);
+
+ WRITEL(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
+
+ WRITEL(0, &disp_ctrl->win.buffer_addr_mode);
+
+ val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+ PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
+ WRITEL(val, &disp_ctrl->cmd.disp_pow_ctrl);
+
+ val = GENERAL_UPDATE | WIN_A_UPDATE;
+ val |= GENERAL_ACT_REQ | WIN_A_ACT_REQ;
+ WRITEL(val, &disp_ctrl->cmd.state_ctrl);
+}
+
+static int tegra_dc_init(struct display_controller *disp_ctrl)
+{
+ /* do not accept interrupts during initialization */
+ WRITEL(0x00000000, &disp_ctrl->cmd.int_mask);
+ WRITEL(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
+ &disp_ctrl->cmd.state_access);
+ WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
+ WRITEL(0x00000000, &disp_ctrl->win.win_opt);
+ WRITEL(0x00000000, &disp_ctrl->win.byte_swap);
+ WRITEL(0x00000000, &disp_ctrl->win.buffer_ctrl);
+
+ WRITEL(0x00000000, &disp_ctrl->win.pos);
+ WRITEL(0x00000000, &disp_ctrl->win.h_initial_dda);
+ WRITEL(0x00000000, &disp_ctrl->win.v_initial_dda);
+ WRITEL(0x00000000, &disp_ctrl->win.dda_increment);
+ WRITEL(0x00000000, &disp_ctrl->win.dv_ctrl);
+
+ WRITEL(0x01000000, &disp_ctrl->win.blend_layer_ctrl);
+ WRITEL(0x00000000, &disp_ctrl->win.blend_match_select);
+ WRITEL(0x00000000, &disp_ctrl->win.blend_nomatch_select);
+ WRITEL(0x00000000, &disp_ctrl->win.blend_alpha_1bit);
+
+ WRITEL(0x00000000, &disp_ctrl->winbuf.start_addr_hi);
+ WRITEL(0x00000000, &disp_ctrl->winbuf.addr_h_offset);
+ WRITEL(0x00000000, &disp_ctrl->winbuf.addr_v_offset);
+
+ WRITEL(0x00000000, &disp_ctrl->com.crc_checksum);
+ WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[0]);
+ WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[1]);
+ WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[2]);
+ WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[3]);
+ WRITEL(0x00000000, &disp_ctrl->disp.disp_signal_opt0);
+
+ return 0;
+}
+
+void display_startup(device_t dev)
+{
+ struct soc_nvidia_tegra132_config *config = dev->chip_info;
+ struct display_controller *disp_ctrl =
+ (void *)config->display_controller;
+ u32 plld_rate;
+
+ u32 framebuffer_size_mb = config->framebuffer_size / MiB;
+ u32 framebuffer_base_mb= config->framebuffer_base / MiB;
+
+ printk(BIOS_INFO, "%s: entry: disp_ctrl: %p.\n",
+ __func__, disp_ctrl);
+
+ if (disp_ctrl == NULL) {
+ printk(BIOS_ERR, "Error: No dc is assigned by dt.\n");
+ return;
+ }
+
+ if (framebuffer_size_mb == 0){
+ framebuffer_size_mb = ALIGN_UP(config->xres * config->yres *
+ (config->framebuffer_bits_per_pixel / 8), MiB)/MiB;
+ }
+
+ config->framebuffer_size = framebuffer_size_mb * MiB;
+ config->framebuffer_base = framebuffer_base_mb * MiB;
+
+ /*
+ * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
+ * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
+ * update_display_mode() for detail.
+ */
+ /* set default plld */
+ plld_rate = clock_display(config->pixel_clock * 2);
+ if (plld_rate == 0) {
+ printk(BIOS_ERR, "dc: clock init failed\n");
+ return;
+ } else if (plld_rate != config->pixel_clock * 2) {
+ printk(BIOS_WARNING, "dc: plld rounded to %u\n", plld_rate);
+ }
+
+ /* set disp1's clock source to PLLD_OUT0 */
+ clock_configure_source(disp1, PLLD, (plld_rate/KHz)/2);
+
+ /* Init dc */
+ if (tegra_dc_init(disp_ctrl)) {
+ printk(BIOS_ERR, "dc: init failed\n");
+ return;
+ }
+
+ /* Configure dc mode */
+ if (update_display_mode(disp_ctrl, config)) {
+ printk(BIOS_ERR, "dc: failed to configure display mode.\n");
+ return;
+ }
+
+ /* Configure and enable dsi controller and panel */
+ if (dsi_enable(config)) {
+ printk(BIOS_ERR, "%s: failed to enable dsi controllers.\n",
+ __func__);
+ return;
+ }
+
+ /* set up window */
+ update_window(config);
+
+ printk(BIOS_INFO, "%s: display init done.\n", __func__);
+}
+
diff --git a/src/soc/nvidia/tegra132/include/soc/addressmap.h b/src/soc/nvidia/tegra132/include/soc/addressmap.h
index 79135217a0..36b0233873 100644
--- a/src/soc/nvidia/tegra132/include/soc/addressmap.h
+++ b/src/soc/nvidia/tegra132/include/soc/addressmap.h
@@ -35,6 +35,8 @@ enum {
TEGRA_GICC_BASE = 0x50042000,
TEGRA_ARM_DISPLAYA = 0x54200000,
TEGRA_ARM_DISPLAYB = 0x54240000,
+ TEGRA_DSIA_BASE = 0x54300000,
+ TEGRA_DSIB_BASE = 0x54400000,
TEGRA_ARM_SOR = 0x54540000,
TEGRA_ARM_DPAUX = 0x545c0000,
TEGRA_PG_UP_BASE = 0x60000000,
@@ -85,6 +87,7 @@ enum {
TEGRA_SDMMC2_BASE = TEGRA_SDMMC_BASE + 0x0200,
TEGRA_SDMMC3_BASE = TEGRA_SDMMC_BASE + 0x0400,
TEGRA_SDMMC4_BASE = TEGRA_SDMMC_BASE + 0x0600,
+ TEGRA_MIPI_CAL_BASE = 0x700E3000,
TEGRA_SYSCTR0_BASE = 0x700F0000,
TEGRA_USBD_BASE = 0x7D000000,
TEGRA_USB2_BASE = 0x7D004000,
diff --git a/src/soc/nvidia/tegra132/include/soc/display.h b/src/soc/nvidia/tegra132/include/soc/display.h
index a6cd1a4d5f..3378a4f2d2 100644
--- a/src/soc/nvidia/tegra132/include/soc/display.h
+++ b/src/soc/nvidia/tegra132/include/soc/display.h
@@ -17,184 +17,25 @@
#ifndef __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__
#define __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__
-/* ardisplay.h */
-#define DC_CMD_DISPLAY_WINDOW_HEADER_0 0x42
-#define DC_COM_CRC_CONTROL_0 0x300
-#define DC_COM_CRC_CHECKSUM_0 0x301
-#define DC_COM_PIN_OUTPUT_ENABLE0_0 0x302
-#define DC_COM_PIN_OUTPUT_ENABLE1_0 0x303
-#define DC_COM_PIN_OUTPUT_ENABLE2_0 0x304
-#define DC_COM_PIN_OUTPUT_ENABLE3_0 0x305
-#define DC_CMD_STATE_ACCESS_0 0x40
-#define DC_DISP_DISP_CLOCK_CONTROL_0 0x42e
-#define DC_DISP_DISP_TIMING_OPTIONS_0 0x405
-#define DC_DISP_REF_TO_SYNC_0 0x406
-#define DC_DISP_SYNC_WIDTH_0 0x407
-#define DC_DISP_BACK_PORCH_0 0x408
-#define DC_DISP_DISP_ACTIVE_0 0x409
-#define DC_DISP_FRONT_PORCH_0 0x40a
-#define DC_DISP_DISP_WIN_OPTIONS_0 0x402
-#define DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT 25
-#define DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_FIELD (0x1 << DC_DISP_DISP_WIN_OPTIONS_0_SOR_ENABLE_SHIFT)
-#define DC_DISP_DISP_SIGNAL_OPTIONS0_0 0x400
-#define DC_DISP_BLEND_BACKGROUND_COLOR_0 0x4e4
-#define DC_CMD_DISPLAY_COMMAND_0 0x32
-#define DC_CMD_STATE_CONTROL_0 0x41
-#define DC_CMD_DISPLAY_POWER_CONTROL_0 0x36
-
-/* ardisplay_a.h */
-#define DC_WIN_A_WIN_OPTIONS_0 0x700
-#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT 30
-#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_FIELD (0x1 << DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_SHIFT)
-#define DC_WIN_A_WIN_OPTIONS_0_A_WIN_ENABLE_ENABLE (1)
-#define DC_WIN_A_BYTE_SWAP_0 0x701
-#define DC_WIN_A_BUFFER_CONTROL_0 0x702
-#define DC_WIN_A_COLOR_DEPTH_0 0x703
-#define DC_WIN_A_POSITION_0 0x704
-#define DC_WIN_A_SIZE_0 0x705
-#define DC_WIN_A_PRESCALED_SIZE_0 0x706
-#define DC_WIN_A_H_INITIAL_DDA_0 0x707
-#define DC_WIN_A_V_INITIAL_DDA_0 0x708
-#define DC_WIN_A_DDA_INCREMENT_0 0x709
-#define DC_WIN_A_LINE_STRIDE_0 0x70a
-#define DC_WIN_A_DV_CONTROL_0 0x70e
-#define DC_WIN_A_BLEND_LAYER_CONTROL_0 0x716
-#define DC_WIN_A_BLEND_MATCH_SELECT_0 0x717
-#define DC_WIN_A_BLEND_NOMATCH_SELECT_0 0x718
-#define DC_WIN_A_BLEND_ALPHA_1BIT_0 0x719
-#define DC_WINBUF_A_START_ADDR_LO_0 0x800
-#define DC_WINBUF_A_START_ADDR_HI_0 0x80d
-#define DC_WINBUF_A_ADDR_H_OFFSET_0 0x806
-#define DC_WINBUF_A_ADDR_V_OFFSET_0 0x808
-
-/* ardisplay_bd.h */
-#define DC_B_WIN_BD_SIZE_0 0xd85
-#define DC_B_WIN_BD_PRESCALED_SIZE_0 0xd86
-#define DC_B_WIN_BD_LINE_STRIDE_0 0xd8a
-#define DC_B_WIN_BD_COLOR_DEPTH_0 0xd83
-#define DC_B_WINBUF_BD_START_ADDR_0 0xdc0
-#define DC_B_WIN_BD_DDA_INCREMENT_0 0xd89
-#define DC_B_WIN_BD_WIN_OPTIONS_0 0xd80
-#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_SHIFT 30
-#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_FIELD (0x1 << DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_SHIFT)
-#define DC_B_WIN_BD_WIN_OPTIONS_0_BD_WIN_ENABLE_ENABLE (1)
-
-/* arsor.h */
-#define SOR_NV_PDISP_SOR_CLK_CNTRL_0 0x13
-#define SOR_NV_PDISP_SOR_DP_PADCTL0_0 0x5c
-#define SOR_NV_PDISP_SOR_PLL0_0 0x17
-#define SOR_NV_PDISP_SOR_PLL1_0 0x18
-#define SOR_NV_PDISP_SOR_PLL2_0 0x19
-#define SOR_NV_PDISP_SOR_PLL3_0 0x1a
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT 22
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX6_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX6_SHIFT)
-#define SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT 0
-#define SOR_NV_PDISP_SOR_PLL0_0_PWR_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL0_0_PWR_SHIFT)
-#define SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT 2
-#define SOR_NV_PDISP_SOR_PLL0_0_VCOPD_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL0_0_VCOPD_SHIFT)
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT 24
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX8_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX8_SHIFT)
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT 23
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX7_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX7_SHIFT)
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT 25
-#define SOR_NV_PDISP_SOR_PLL2_0_AUX9_FIELD (0x1 << SOR_NV_PDISP_SOR_PLL2_0_AUX9_SHIFT)
-#define SOR_NV_PDISP_SOR_LANE_DRIVE_CURRENT0_0 0x4e
-#define SOR_NV_PDISP_SOR_LANE_PREEMPHASIS0_0 0x52
-#define SOR_NV_PDISP_SOR_POSTCURSOR0_0 0x56
-#define SOR_NV_PDISP_SOR_DP_PADCTL0_0 0x5c
-#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT 8
-#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_FIELD (0xff << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_VALUE_SHIFT)
-#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT 22
-#define SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_FIELD (0x1 << SOR_NV_PDISP_SOR_DP_PADCTL0_0_TX_PU_SHIFT)
-#define SOR_NV_PDISP_SOR_LVDS_0 0x1c
-#define SOR_NV_PDISP_SOR_CLK_CNTRL_0 0x13
-#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0 0x4c
-#define SOR_NV_PDISP_SOR_LANE_SEQ_CTL_0 0x21
-#define SOR_NV_PDISP_SOR_DP_TPG_0 0x6d
-#define SOR_NV_PDISP_HEAD_STATE1_0 0x7
-#define SOR_NV_PDISP_HEAD_STATE2_0 0x9
-#define SOR_NV_PDISP_HEAD_STATE3_0 0xb
-#define SOR_NV_PDISP_HEAD_STATE4_0 0xd
-#define SOR_NV_PDISP_SOR_STATE1_0 0x4
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT 12
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_FIELD (0x1 << SOR_NV_PDISP_SOR_STATE1_0_ASY_HSYNCPOL_SHIFT)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT 13
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_FIELD (0x1 << SOR_NV_PDISP_SOR_STATE1_0_ASY_VSYNCPOL_SHIFT)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT 8
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_SHIFT)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_LVDS_CUSTOM (0)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_A (8)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_DP_B (9)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PROTOCOL_CUSTOM (15)
-
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_ACTIVE_RASTER (0)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_COMPLETE_RASTER (1)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_NON_ACTIVE_RASTER (2)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT 6
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_FIELD (0x3 << SOR_NV_PDISP_SOR_STATE1_0_ASY_CRCMODE_SHIFT)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT 4
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_FIELD (0x3 << SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SHIFT)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_NONE (0)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SUBHEAD0 (1)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_SUBHEAD1 (2)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_SUBOWNER_BOTH (3)
-
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT 0
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_SHIFT)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_NONE (0)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD0 (1)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_OWNER_HEAD1 (2)
-
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0 0x58
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT 24
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_FIELD (0x1 << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_POLARITY_SHIFT)
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT 16
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_FIELD (0xf << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_FRAC_SHIFT)
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT 8
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_FIELD (0x7f << SOR_NV_PDISP_SOR_DP_CONFIG0_0_ACTIVESYM_COUNT_SHIFT)
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT 0
-#define SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_FIELD (0x3f << SOR_NV_PDISP_SOR_DP_CONFIG0_0_WATERMARK_SHIFT)
-#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT 2
-#define SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_FIELD (0x7f << SOR_NV_PDISP_SOR_DP_LINKCTL0_0_TUSIZE_SHIFT)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT 17
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_FIELD (0xf << SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_SHIFT)
+#define COLOR_WHITE 0xFFFFFF
+#define COLOR_BLACK 0x000000
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_DEFAULTVAL (0)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_16_422 (1)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_18_444 (2)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_20_422 (3)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_422 (4)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_24_444 (5)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_30_444 (6)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_32_422 (7)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_36_444 (8)
-#define SOR_NV_PDISP_SOR_STATE1_0_ASY_PIXELDEPTH_BPP_48_444 (9)
+#define hsync_start(mode) \
+ (mode->xres + mode->hfront_porch)
-#define SOR_NV_PDISP_SOR_CRC_CNTRL_0 0x11
-#define SOR_NV_PDISP_SOR_DP_AUDIO_VBLANK_SYMBOLS_0 0x64
-#define SOR_NV_PDISP_SOR_DP_SPARE0_0 0x60
-#define SOR_NV_PDISP_SOR_PWR_0 0x15
-#define SOR_NV_PDISP_SOR_STATE0_0 0x3
-#define SOR_NV_PDISP_SOR_SUPER_STATE1_0 0x2
-#define SOR_NV_PDISP_SOR_SUPER_STATE0_0 0x1
+#define hsync_end(mode) \
+ (mode->xres + mode->hfront_porch + mode->hsync_width)
-/* ardpaux.h */
-#define DPAUX_DP_AUXDATA_READ_W0 0x19
+#define htotal(mode) \
+ (mode->xres + mode->hfront_porch + \
+ mode->hsync_width + mode->hback_porch)
-#define DP_LVDS_SHIFT 25
-#define DP_LVDS (1 << DP_LVDS_SHIFT)
-
-#define SRC_BPP 16
-#define COLORDEPTH 0x6
-#define COLOR_WHITE 0xFFFFFF
+#define vtotal(mode) \
+ (mode->yres + mode->vfront_porch + \
+ mode->vsync_width + mode->vback_porch)
struct soc_nvidia_tegra132_config; /* forward declaration */
-void setup_display(struct soc_nvidia_tegra132_config *config);
-void init_dca_regs(void);
-void dp_io_powerup(void);
-u32 dp_setup_timing(u32 width, u32 height);
-void dp_misc_setting(u32 panel_bpp, u32 width, u32 height, u32 winb_addr,
- u32 lane_count, u32 enhanced_framing, u32 panel_edp,
- u32 pclkfreq, u32 linkfreq);
+
+int dsi_enable(struct soc_nvidia_tegra132_config *config);
#endif /* __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__ */
diff --git a/src/soc/nvidia/tegra132/include/soc/mipi-phy.h b/src/soc/nvidia/tegra132/include/soc/mipi-phy.h
new file mode 100644
index 0000000000..2ec3fce113
--- /dev/null
+++ b/src/soc/nvidia/tegra132/include/soc/mipi-phy.h
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef _TEGRA_MIPI_PHY_H
+#define _TEGRA_MIPI_PHY_H
+
+#include <stdlib.h>
+
+/*
+ * Macros for calculating the phy timings
+ */
+/* Period of one bit time in nano seconds */
+#define DSI_TBIT_Factorized(Freq) (((1000) * (1000))/(Freq))
+#define DSI_TBIT(Freq) (DSI_TBIT_Factorized(Freq)/(1000))
+
+//#define NV_MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+/* Period of one byte time in nano seconds */
+#define DSI_TBYTE(Freq) ((DSI_TBIT_Factorized(Freq)) * (8))
+#define DSI_PHY_TIMING_DIV(X, Freq) ((X*1000) / (DSI_TBYTE(Freq)))
+
+/*
+ * As per Mipi spec (minimum):
+ * (3 + MAX(8 * DSI_TBIT, 60 + 4 * DSI_TBIT) / DSI_TBYTE)
+ */
+#define DSI_THSTRAIL_VAL(Freq) \
+ (MAX(((8) * (DSI_TBIT(Freq))), ((60) + ((4) * (DSI_TBIT(Freq))))))
+
+int mipi_dphy_set_timing(struct tegra_dsi *dsi);
+
+#endif
diff --git a/src/soc/nvidia/tegra132/include/soc/mipi_display.h b/src/soc/nvidia/tegra132/include/soc/mipi_display.h
new file mode 100644
index 0000000000..99bfa93ea7
--- /dev/null
+++ b/src/soc/nvidia/tegra132/include/soc/mipi_display.h
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * Defines for Mobile Industry Processor Interface (MIPI(R))
+ * Display Working Group standards: DSI, DCS, DBI, DPI
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef MIPI_DISPLAY_H
+#define MIPI_DISPLAY_H
+
+/* MIPI DSI Processor-to-Peripheral transaction types */
+enum {
+ MIPI_DSI_V_SYNC_START = 0x01,
+ MIPI_DSI_V_SYNC_END = 0x11,
+ MIPI_DSI_H_SYNC_START = 0x21,
+ MIPI_DSI_H_SYNC_END = 0x31,
+
+ MIPI_DSI_COLOR_MODE_OFF = 0x02,
+ MIPI_DSI_COLOR_MODE_ON = 0x12,
+ MIPI_DSI_SHUTDOWN_PERIPHERAL = 0x22,
+ MIPI_DSI_TURN_ON_PERIPHERAL = 0x32,
+
+ MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 0x03,
+ MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 0x13,
+ MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 0x23,
+
+ MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 0x04,
+ MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 0x14,
+ MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 0x24,
+
+ MIPI_DSI_DCS_SHORT_WRITE = 0x05,
+ MIPI_DSI_DCS_SHORT_WRITE_PARAM = 0x15,
+
+ MIPI_DSI_DCS_READ = 0x06,
+
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 0x37,
+
+ MIPI_DSI_END_OF_TRANSMISSION = 0x08,
+
+ MIPI_DSI_NULL_PACKET = 0x09,
+ MIPI_DSI_BLANKING_PACKET = 0x19,
+ MIPI_DSI_GENERIC_LONG_WRITE = 0x29,
+ MIPI_DSI_DCS_LONG_WRITE = 0x39,
+
+ MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 0x0c,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 0x1c,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 0x2c,
+
+ MIPI_DSI_PACKED_PIXEL_STREAM_30 = 0x0d,
+ MIPI_DSI_PACKED_PIXEL_STREAM_36 = 0x1d,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 0x3d,
+
+ MIPI_DSI_PACKED_PIXEL_STREAM_16 = 0x0e,
+ MIPI_DSI_PACKED_PIXEL_STREAM_18 = 0x1e,
+ MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 0x2e,
+ MIPI_DSI_PACKED_PIXEL_STREAM_24 = 0x3e,
+};
+
+/* MIPI DSI Peripheral-to-Processor transaction types */
+enum {
+ MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT = 0x02,
+ MIPI_DSI_RX_END_OF_TRANSMISSION = 0x08,
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE = 0x11,
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE = 0x12,
+ MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE = 0x1a,
+ MIPI_DSI_RX_DCS_LONG_READ_RESPONSE = 0x1c,
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE = 0x21,
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE = 0x22,
+};
+
+/* MIPI DCS commands */
+enum {
+ MIPI_DCS_NOP = 0x00,
+ MIPI_DCS_SOFT_RESET = 0x01,
+ MIPI_DCS_GET_DISPLAY_ID = 0x04,
+ MIPI_DCS_GET_RED_CHANNEL = 0x06,
+ MIPI_DCS_GET_GREEN_CHANNEL = 0x07,
+ MIPI_DCS_GET_BLUE_CHANNEL = 0x08,
+ MIPI_DCS_GET_DISPLAY_STATUS = 0x09,
+ MIPI_DCS_GET_POWER_MODE = 0x0A,
+ MIPI_DCS_GET_ADDRESS_MODE = 0x0B,
+ MIPI_DCS_GET_PIXEL_FORMAT = 0x0C,
+ MIPI_DCS_GET_DISPLAY_MODE = 0x0D,
+ MIPI_DCS_GET_SIGNAL_MODE = 0x0E,
+ MIPI_DCS_GET_DIAGNOSTIC_RESULT = 0x0F,
+ MIPI_DCS_ENTER_SLEEP_MODE = 0x10,
+ MIPI_DCS_EXIT_SLEEP_MODE = 0x11,
+ MIPI_DCS_ENTER_PARTIAL_MODE = 0x12,
+ MIPI_DCS_ENTER_NORMAL_MODE = 0x13,
+ MIPI_DCS_EXIT_INVERT_MODE = 0x20,
+ MIPI_DCS_ENTER_INVERT_MODE = 0x21,
+ MIPI_DCS_SET_GAMMA_CURVE = 0x26,
+ MIPI_DCS_SET_DISPLAY_OFF = 0x28,
+ MIPI_DCS_SET_DISPLAY_ON = 0x29,
+ MIPI_DCS_SET_COLUMN_ADDRESS = 0x2A,
+ MIPI_DCS_SET_PAGE_ADDRESS = 0x2B,
+ MIPI_DCS_WRITE_MEMORY_START = 0x2C,
+ MIPI_DCS_WRITE_LUT = 0x2D,
+ MIPI_DCS_READ_MEMORY_START = 0x2E,
+ MIPI_DCS_SET_PARTIAL_AREA = 0x30,
+ MIPI_DCS_SET_SCROLL_AREA = 0x33,
+ MIPI_DCS_SET_TEAR_OFF = 0x34,
+ MIPI_DCS_SET_TEAR_ON = 0x35,
+ MIPI_DCS_SET_ADDRESS_MODE = 0x36,
+ MIPI_DCS_SET_SCROLL_START = 0x37,
+ MIPI_DCS_EXIT_IDLE_MODE = 0x38,
+ MIPI_DCS_ENTER_IDLE_MODE = 0x39,
+ MIPI_DCS_SET_PIXEL_FORMAT = 0x3A,
+ MIPI_DCS_WRITE_MEMORY_CONTINUE = 0x3C,
+ MIPI_DCS_READ_MEMORY_CONTINUE = 0x3E,
+ MIPI_DCS_SET_TEAR_SCANLINE = 0x44,
+ MIPI_DCS_GET_SCANLINE = 0x45,
+ MIPI_DCS_READ_DDB_START = 0xA1,
+ MIPI_DCS_READ_DDB_CONTINUE = 0xA8,
+};
+
+/* MIPI DCS pixel formats */
+#define MIPI_DCS_PIXEL_FMT_24BIT 7
+#define MIPI_DCS_PIXEL_FMT_18BIT 6
+#define MIPI_DCS_PIXEL_FMT_16BIT 5
+#define MIPI_DCS_PIXEL_FMT_12BIT 3
+#define MIPI_DCS_PIXEL_FMT_8BIT 2
+#define MIPI_DCS_PIXEL_FMT_3BIT 1
+
+#endif
diff --git a/src/soc/nvidia/tegra132/include/soc/mipi_dsi.h b/src/soc/nvidia/tegra132/include/soc/mipi_dsi.h
new file mode 100644
index 0000000000..1bca657ac5
--- /dev/null
+++ b/src/soc/nvidia/tegra132/include/soc/mipi_dsi.h
@@ -0,0 +1,283 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MIPI_DSI_H__
+#define __MIPI_DSI_H__
+
+struct mipi_dsi_host;
+struct mipi_dsi_device;
+
+/* request ACK from peripheral */
+#define MIPI_DSI_MSG_REQ_ACK BIT(0)
+/* use Low Power Mode to transmit message */
+#define MIPI_DSI_MSG_USE_LPM BIT(1)
+
+/**
+ * struct mipi_dsi_msg - read/write DSI buffer
+ * @channel: virtual channel id
+ * @type: payload data type
+ * @flags: flags controlling this message transmission
+ * @tx_len: length of @tx_buf
+ * @tx_buf: data to be written
+ * @rx_len: length of @rx_buf
+ * @rx_buf: data to be read, or NULL
+ */
+struct mipi_dsi_msg {
+ u8 channel;
+ u8 type;
+ u16 flags;
+
+ size_t tx_len;
+ const void *tx_buf;
+
+ size_t rx_len;
+ void *rx_buf;
+};
+
+/**
+ * struct mipi_dsi_host_ops - DSI bus operations
+ * @attach: attach DSI device to DSI host
+ * @detach: detach DSI device from DSI host
+ * @transfer: transmit a DSI packet
+ *
+ * DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg
+ * structures. This structure contains information about the type of packet
+ * being transmitted as well as the transmit and receive buffers. When an
+ * error is encountered during transmission, this function will return a
+ * negative error code. On success it shall return the number of bytes
+ * transmitted for write packets or the number of bytes received for read
+ * packets.
+ *
+ * Note that typically DSI packet transmission is atomic, so the .transfer()
+ * function will seldomly return anything other than the number of bytes
+ * contained in the transmit buffer on success.
+ */
+struct mipi_dsi_host_ops {
+ int (*attach)(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *dsi);
+ int (*detach)(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *dsi);
+ ssize_t (*transfer)(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg);
+};
+
+/**
+ * struct mipi_dsi_host - DSI host device
+ * @dev: driver model device node for this DSI host
+ * @ops: DSI host operations
+ */
+struct mipi_dsi_host {
+ //struct device *dev;
+ void *dev;
+ const struct mipi_dsi_host_ops *ops;
+};
+
+int mipi_dsi_host_register(struct mipi_dsi_host *host);
+
+/* DSI mode flags */
+
+/* video mode */
+#define MIPI_DSI_MODE_VIDEO BIT(0)
+/* video burst mode */
+#define MIPI_DSI_MODE_VIDEO_BURST BIT(1)
+/* video pulse mode */
+#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2)
+/* enable auto vertical count mode */
+#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3)
+/* enable hsync-end packets in vsync-pulse and v-porch area */
+#define MIPI_DSI_MODE_VIDEO_HSE BIT(4)
+/* disable hfront-porch area */
+#define MIPI_DSI_MODE_VIDEO_HFP BIT(5)
+/* disable hback-porch area */
+#define MIPI_DSI_MODE_VIDEO_HBP BIT(6)
+/* disable hsync-active area */
+#define MIPI_DSI_MODE_VIDEO_HSA BIT(7)
+/* flush display FIFO on vsync pulse */
+#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8)
+/* disable EoT packets in HS mode */
+#define MIPI_DSI_MODE_EOT_PACKET BIT(9)
+/* device supports non-continuous clock behavior (DSI spec 5.6.1) */
+#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10)
+
+enum mipi_dsi_pixel_format {
+ MIPI_DSI_FMT_RGB888,
+ MIPI_DSI_FMT_RGB666,
+ MIPI_DSI_FMT_RGB666_PACKED,
+ MIPI_DSI_FMT_RGB565,
+};
+
+struct mipi_dsi_master_ops {
+ int (*enslave)(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave);
+ int (*liberate)(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave);
+};
+
+/**
+ * struct mipi_dsi_device - DSI peripheral device
+ * @host: DSI host for this peripheral
+ * @dev: driver model device node for this peripheral
+ * @channel: virtual channel assigned to the peripheral
+ * @format: pixel format for video mode
+ * @lanes: number of active data lanes
+ * @mode_flags: DSI operation mode related flags
+ * @ops: callbacks for master/slave setup
+ * @master: master interface for dual-channel peripherals
+ * @slave: slave interface for dual-channel peripherals
+ *
+ * For dual-channel interfaces, the master interface can be identified by the
+ * fact that it's .slave field is set to non-NULL. The slave interface will
+ * have the .master field set to non-NULL.
+ */
+struct mipi_dsi_device {
+ struct mipi_dsi_host *host;
+
+ unsigned int channel;
+ unsigned int lanes;
+ enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
+
+ const struct mipi_dsi_master_ops *ops;
+ struct mipi_dsi_device *master;
+ struct mipi_dsi_device *slave;
+};
+
+int mipi_dsi_attach(struct mipi_dsi_device *dsi);
+int mipi_dsi_detach(struct mipi_dsi_device *dsi);
+int mipi_dsi_enslave(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave);
+int mipi_dsi_liberate(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave);
+
+/**
+ * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode
+ * @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking
+ * information only
+ * @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both
+ * V-Blanking and H-Blanking information
+ */
+enum mipi_dsi_dcs_tear_mode {
+ MIPI_DSI_DCS_TEAR_MODE_VBLANK,
+ MIPI_DSI_DCS_TEAR_MODE_VHBLANK,
+};
+
+#define MIPI_DSI_DCS_POWER_MODE_DISPLAY (1 << 2)
+#define MIPI_DSI_DCS_POWER_MODE_NORMAL (1 << 3)
+#define MIPI_DSI_DCS_POWER_MODE_SLEEP (1 << 4)
+#define MIPI_DSI_DCS_POWER_MODE_PARTIAL (1 << 5)
+#define MIPI_DSI_DCS_POWER_MODE_IDLE (1 << 6)
+
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
+ const void *data, size_t len);
+int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi);
+int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
+ u16 end);
+int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
+ u16 end);
+int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi,
+ bool reverse_page_address,
+ bool reverse_col_address,
+ bool reverse_page_col_address,
+ bool refresh_from_bottom,
+ bool reverse_rgb,
+ bool latch_right_to_left,
+ bool flip_horizontal,
+ bool flip_vertical);
+int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
+ enum mipi_dsi_dcs_tear_mode mode);
+int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format);
+
+#define MIPI_CAL_CTRL 0x00
+#define MIPI_CAL_CTRL_START (1 << 0)
+
+#define MIPI_CAL_AUTOCAL_CTRL 0x01
+
+#define MIPI_CAL_STATUS 0x02
+#define MIPI_CAL_STATUS_DONE (1 << 16)
+#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
+
+#define MIPI_CAL_CONFIG_CSIA 0x05
+#define MIPI_CAL_CONFIG_CSIB 0x06
+#define MIPI_CAL_CONFIG_CSIC 0x07
+#define MIPI_CAL_CONFIG_CSID 0x08
+#define MIPI_CAL_CONFIG_CSIE 0x09
+#define MIPI_CAL_CONFIG_DSIA 0x0e
+#define MIPI_CAL_CONFIG_DSIB 0x0f
+#define MIPI_CAL_CONFIG_DSIC 0x10
+#define MIPI_CAL_CONFIG_DSID 0x11
+
+#define MIPI_CAL_CONFIG_DSIAB_CLK 0x19
+#define MIPI_CAL_CONFIG_DSICD_CLK 0x1a
+#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
+#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
+#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
+
+#define MIPI_CAL_CONFIG_SELECT (1 << 21)
+#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
+#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
+#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
+
+#define MIPI_CAL_BIAS_PAD_CFG0 0x16
+#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
+#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
+
+#define MIPI_CAL_BIAS_PAD_CFG1 0x17
+#define MIPI_CAL_BIAS_PAD_CFG1_DEFAULT (0x20000)
+
+#define MIPI_CAL_BIAS_PAD_CFG2 0x18
+#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
+
+struct calibration_regs {
+ unsigned long data;
+ unsigned long clk;
+};
+
+struct tegra_mipi_config {
+ int calibrate_clk_lane;
+ int num_pads;
+ const struct calibration_regs *regs;
+};
+
+struct tegra_mipi {
+ void *regs;
+};
+
+struct tegra_mipi_device {
+ struct tegra_mipi *mipi;
+ const struct tegra_mipi_config *config;
+ unsigned long pads;
+};
+
+struct tegra_mipi_device *tegra_mipi_request(struct tegra_mipi_device *device,
+ int device_index);
+int tegra_mipi_calibrate(struct tegra_mipi_device *device);
+#endif /* __MIPI_DSI_H__ */
diff --git a/src/soc/nvidia/tegra132/include/soc/tegra_dsi.h b/src/soc/nvidia/tegra132/include/soc/tegra_dsi.h
new file mode 100644
index 0000000000..830257d7cc
--- /dev/null
+++ b/src/soc/nvidia/tegra132/include/soc/tegra_dsi.h
@@ -0,0 +1,219 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __TEGRA_DSI_H__
+#define __TEGRA_DSI_H__
+
+#define DSI_INCR_SYNCPT 0x00
+#define DSI_INCR_SYNCPT_CONTROL 0x01
+#define DSI_INCR_SYNCPT_ERROR 0x02
+#define DSI_CTXSW 0x08
+#define DSI_RD_DATA 0x09
+#define DSI_WR_DATA 0x0a
+#define DSI_POWER_CONTROL 0x0b
+#define DSI_POWER_CONTROL_ENABLE (1 << 0)
+#define DSI_INT_ENABLE 0x0c
+#define DSI_INT_STATUS 0x0d
+#define DSI_INT_MASK 0x0e
+#define DSI_HOST_CONTROL 0x0f
+#define DSI_HOST_CONTROL_FIFO_RESET (1 << 21)
+#define DSI_HOST_CONTROL_CRC_RESET (1 << 20)
+#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12)
+#define DSI_HOST_CONTROL_RAW (1 << 6)
+#define DSI_HOST_CONTROL_HS (1 << 5)
+#define DSI_HOST_CONTROL_FIFO_SEL (1 << 4)
+#define DSI_HOST_CONTROL_IMM_BTA (1 << 3)
+#define DSI_HOST_CONTROL_PKT_BTA (1 << 2)
+#define DSI_HOST_CONTROL_CS (1 << 1)
+#define DSI_HOST_CONTROL_ECC (1 << 0)
+#define DSI_CONTROL 0x10
+#define DSI_CONTROL_HS_CLK_CTRL (1 << 20)
+#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16)
+#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12)
+#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8)
+#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4)
+#define DSI_CONTROL_DCS_ENABLE (1 << 3)
+#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2)
+#define DSI_CONTROL_VIDEO_ENABLE (1 << 1)
+#define DSI_CONTROL_HOST_ENABLE (1 << 0)
+#define DSI_SOL_DELAY 0x11
+#define DSI_MAX_THRESHOLD 0x12
+#define DSI_TRIGGER 0x13
+#define DSI_TRIGGER_HOST (1 << 1)
+#define DSI_TRIGGER_VIDEO (1 << 0)
+#define DSI_TX_CRC 0x14
+#define DSI_STATUS 0x15
+#define DSI_STATUS_IDLE (1 << 10)
+#define DSI_STATUS_UNDERFLOW (1 << 9)
+#define DSI_STATUS_OVERFLOW (1 << 8)
+#define DSI_INIT_SEQ_CONTROL 0x1a
+#define DSI_INIT_SEQ_DATA_0 0x1b
+#define DSI_INIT_SEQ_DATA_1 0x1c
+#define DSI_INIT_SEQ_DATA_2 0x1d
+#define DSI_INIT_SEQ_DATA_3 0x1e
+#define DSI_INIT_SEQ_DATA_4 0x1f
+#define DSI_INIT_SEQ_DATA_5 0x20
+#define DSI_INIT_SEQ_DATA_6 0x21
+#define DSI_INIT_SEQ_DATA_7 0x22
+#define DSI_PKT_SEQ_0_LO 0x23
+#define DSI_PKT_SEQ_0_HI 0x24
+#define DSI_PKT_SEQ_1_LO 0x25
+#define DSI_PKT_SEQ_1_HI 0x26
+#define DSI_PKT_SEQ_2_LO 0x27
+#define DSI_PKT_SEQ_2_HI 0x28
+#define DSI_PKT_SEQ_3_LO 0x29
+#define DSI_PKT_SEQ_3_HI 0x2a
+#define DSI_PKT_SEQ_4_LO 0x2b
+#define DSI_PKT_SEQ_4_HI 0x2c
+#define DSI_PKT_SEQ_5_LO 0x2d
+#define DSI_PKT_SEQ_5_HI 0x2e
+#define DSI_DCS_CMDS 0x33
+#define DSI_PKT_LEN_0_1 0x34
+#define DSI_PKT_LEN_2_3 0x35
+#define DSI_PKT_LEN_4_5 0x36
+#define DSI_PKT_LEN_6_7 0x37
+#define DSI_PHY_TIMING_0 0x3c
+#define DSI_PHY_TIMING_1 0x3d
+#define DSI_PHY_TIMING_2 0x3e
+#define DSI_BTA_TIMING 0x3f
+
+#define DSI_TIMING_FIELD(value, period, hwinc) \
+ ((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff)
+
+#define DSI_TIMEOUT_0 0x44
+#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0)
+#define DSI_TIMEOUT_1 0x45
+#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0)
+#define DSI_TO_TALLY 0x46
+#define DSI_TALLY_TA(x) (((x) & 0xff) << 16)
+#define DSI_TALLY_LRX(x) (((x) & 0xff) << 8)
+#define DSI_TALLY_HTX(x) (((x) & 0xff) << 0)
+#define DSI_PAD_CONTROL_0 0x4b
+#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0)
+#define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8)
+#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16)
+#define DSI_PAD_CONTROL_VS1_PULLDN_CLK (1 << 24)
+#define DSI_PAD_CONTROL_CD 0x4c
+#define DSI_PAD_CD_STATUS 0x4d
+#define DSI_VIDEO_MODE_CONTROL 0x4e
+#define DSI_PAD_CONTROL_1 0x4f
+#define DSI_PAD_CONTROL_2 0x50
+#define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0)
+#define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4)
+#define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8)
+#define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12)
+#define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16)
+#define DSI_PAD_CONTROL_3 0x51
+#define DSI_PAD_CONTROL_4 0x52
+#define DSI_GANGED_MODE_CONTROL 0x53
+#define DSI_GANGED_MODE_CONTROL_ENABLE (1 << 0)
+#define DSI_GANGED_MODE_START 0x54
+#define DSI_GANGED_MODE_SIZE 0x55
+#define DSI_RAW_DATA_BYTE_COUNT 0x56
+#define DSI_ULTRA_LOW_POWER_CONTROL 0x57
+#define DSI_INIT_SEQ_DATA_8 0x58
+#define DSI_INIT_SEQ_DATA_9 0x59
+#define DSI_INIT_SEQ_DATA_10 0x5a
+#define DSI_INIT_SEQ_DATA_11 0x5b
+#define DSI_INIT_SEQ_DATA_12 0x5c
+#define DSI_INIT_SEQ_DATA_13 0x5d
+#define DSI_INIT_SEQ_DATA_14 0x5e
+#define DSI_INIT_SEQ_DATA_15 0x5f
+
+#define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9))
+#define PKT_LEN0(len) (((len) & 0x07) << 0)
+#define PKT_ID1(id) ((((id) & 0x3f) << 13) | (1 << 19))
+#define PKT_LEN1(len) (((len) & 0x07) << 10)
+#define PKT_ID2(id) ((((id) & 0x3f) << 23) | (1 << 29))
+#define PKT_LEN2(len) (((len) & 0x07) << 20)
+
+#define PKT_LP (1 << 30)
+#define NUM_PKT_SEQ 12
+
+#define APB_MISC_GP_MIPI_PAD_CTRL_0 (TEGRA_APB_MISC_GP_BASE + 0x20)
+#define DSIB_MODE_SHIFT 1
+#define DSIB_MODE_CSI (0 << DSIB_MODE_SHIFT)
+#define DSIB_MODE_DSI (1 << DSIB_MODE_SHIFT)
+
+/*
+ * pixel format as used in the DSI_CONTROL_FORMAT field
+ */
+enum tegra_dsi_format {
+ TEGRA_DSI_FORMAT_16P,
+ TEGRA_DSI_FORMAT_18NP,
+ TEGRA_DSI_FORMAT_18P,
+ TEGRA_DSI_FORMAT_24P,
+};
+
+enum dsi_dev {
+ DSI_A = 0,
+ DSI_B,
+ NUM_DSI,
+};
+
+struct panel_jdi;
+struct tegra_mipi_device;
+struct mipi_dsi_host;
+struct mipi_dsi_msg;
+
+#define MAX_DSI_VIDEO_FIFO_DEPTH 96
+#define MAX_DSI_HOST_FIFO_DEPTH 64
+
+struct tegra_dsi {
+ struct panel_jdi *panel;
+ //struct tegra_output output;
+ void *regs;
+ u8 channel;
+ unsigned long clk_rate;
+
+ unsigned long flags;
+ enum mipi_dsi_pixel_format format;
+ unsigned int lanes;
+
+ struct tegra_mipi_device *mipi;
+ struct mipi_dsi_host host;
+ bool enabled;
+
+ unsigned int video_fifo_depth;
+ unsigned int host_fifo_depth;
+
+ /* for ganged-mode support */
+ unsigned int ganged_lanes;
+ struct tegra_dsi *slave;
+ int ganged_mode;
+
+ struct tegra_dsi *master;
+};
+
+static inline unsigned long tegra_dsi_readl(struct tegra_dsi *dsi,
+ unsigned long reg)
+{
+ return readl(dsi->regs + (reg << 2));
+}
+
+static inline void tegra_dsi_writel(struct tegra_dsi *dsi, unsigned long value,
+ unsigned long reg)
+{
+ writel(value, dsi->regs + (reg << 2));
+}
+
+#endif /* __TEGRA_DSI_H__ */
diff --git a/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.c b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.c
new file mode 100644
index 0000000000..45797b1cc5
--- /dev/null
+++ b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.c
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <lib.h>
+#include <stdlib.h>
+#include <delay.h>
+#include <soc/addressmap.h>
+#include <soc/clock.h>
+#include <device/device.h>
+#include <soc/nvidia/tegra/types.h>
+#include "../chip.h"
+#include <soc/display.h>
+#include <soc/mipi_dsi.h>
+#include <soc/tegra_dsi.h>
+#include "panel-jdi-lpm102a188a.h"
+
+struct panel_jdi jdi_data[NUM_DSI];
+
+int panel_jdi_prepare(struct panel_jdi *jdi)
+{
+ int ret;
+ u8 data;
+
+ if (jdi->enabled)
+ return 0;
+
+ ret = mipi_dsi_dcs_set_column_address(jdi->dsi, 0,
+ jdi->mode->xres / 2 - 1); // 2560/2
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set column address: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_column_address(jdi->dsi->slave, 0,
+ jdi->mode->xres / 2 - 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set column address: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_page_address(jdi->dsi, 0,
+ jdi->mode->yres - 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set page address: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_page_address(jdi->dsi->slave, 0,
+ jdi->mode->yres - 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set page address: %d\n", ret);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(jdi->dsi);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to exit sleep mode: %d\n", ret);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(jdi->dsi->slave);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to exit sleep mode: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_tear_on(jdi->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set tear on: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_tear_on(jdi->dsi->slave,
+ MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set tear on: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_address_mode(jdi->dsi, false, false, false,
+ false, false, false, false, false);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set address mode: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_address_mode(jdi->dsi->slave, false, false,
+ false, false, false, false, false, false);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set address mode: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_pixel_format(jdi->dsi, 0x77);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set pixel format: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_pixel_format(jdi->dsi->slave, 0x77);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set pixel format: %d\n", ret);
+
+ data = 0xFF;
+ ret = mipi_dsi_dcs_write(jdi->dsi, 0x51, &data, 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set 0x51: %d\n", ret);
+
+ data = 0xFF;
+ ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x51, &data, 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set 0x51: %d\n", ret);
+
+ data = 0x24;
+ ret = mipi_dsi_dcs_write(jdi->dsi, 0x53, &data, 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set 0x53: %d\n", ret);
+
+ data = 0x24;
+ ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x53, &data, 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set 0x53: %d\n", ret);
+
+ data = 0x00;
+ ret = mipi_dsi_dcs_write(jdi->dsi, 0x55, &data, 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set 0x55: %d\n", ret);
+
+ data = 0x00;
+ ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x55, &data, 1);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set 0x55: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_display_on(jdi->dsi);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set display on: %d\n", ret);
+
+ ret = mipi_dsi_dcs_set_display_on(jdi->dsi->slave);
+ if (ret < 0)
+ printk(BIOS_ERR, "failed to set display on: %d\n", ret);
+
+ jdi->enabled = true;
+
+ return 0;
+}
+
+static int panel_jdi_enslave(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave)
+{
+ int ret;
+
+ ret = mipi_dsi_attach(master);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int panel_jdi_liberate(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave)
+{
+ int ret;
+
+ ret = mipi_dsi_detach(master);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct mipi_dsi_master_ops panel_jdi_master_ops = {
+ .enslave = panel_jdi_enslave,
+ .liberate = panel_jdi_liberate,
+};
+
+struct panel_jdi *panel_jdi_dsi_probe(struct mipi_dsi_device *dsi)
+{
+ static int index = 0;
+ struct panel_jdi *jdi;
+ int ret;
+
+ if (index >= NUM_DSI)
+ return (void *)-EPTR;
+
+ jdi = &jdi_data[index++];
+
+ jdi->dsi = dsi;
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = 0;
+
+ if (dsi->master) {
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ printk(BIOS_ERR, "mipi_dsi_attach() failed: %d\n", ret);
+ return (void *)-EPTR;
+ }
+
+ ret = mipi_dsi_enslave(dsi->master, dsi);
+ if (ret < 0) {
+ printk(BIOS_ERR, "mipi_dsi_enslave() failed: %d\n",
+ ret);
+ return (void *)-EPTR;
+ }
+
+ return jdi;
+ }
+
+ dsi->ops = &panel_jdi_master_ops;
+
+ jdi->enabled = 0;
+ jdi->width_mm = 211;
+ jdi->height_mm = 148;
+
+ return jdi;
+}
diff --git a/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.h b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.h
new file mode 100644
index 0000000000..a44882d386
--- /dev/null
+++ b/src/soc/nvidia/tegra132/jdi_25x18_display/panel-jdi-lpm102a188a.h
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef _PANEL_JDI_LPM102A188A_H_
+#define _PANEL_JDI_LPM102A188A_H_
+
+#define LP8557_MAX_BRIGHTNESS 0xFFF;
+
+#define LP8557_COMMAND 0x00
+#define LP8557_COMMAND_ON (1 << 0)
+
+#define LP8557_BRIGHTNESS_LOW 0x03
+#define LP8557_BRIGHTNESS_LOW_MASK(x) (((x) & 0xF) << 4)
+
+#define LP8557_BRIGHTNESS_HIGH 0x04
+#define LP8557_BRIGHTNESS_HIGH_MASK(x) (((x) & 0xFF0) >> 4)
+
+enum lp8557_config_brightness_mode {
+ LP8557_CONFIG_BRTMODE_PWM = 0x0,
+ LP8557_CONFIG_BRTMODE_REG,
+ LP8557_CONFIG_BRTMODE_PWM_REG_SHAPE_PWM,
+ LP8557_CONFIG_BRTMODE_PWM_REG_SHAPE_BRIGHTNESS,
+ LP8557_CONFIG_BRTMODE_MAX,
+};
+#define LP8557_CONFIG 0x10
+#define LP8557_CONFIG_BRTMODE(x) (((x) & 0x3) << 0)
+#define LP8557_CONFIG_AUTO_DETECT_LED (1 << 2)
+#define LP8557_CONFIG_PWM_STANDBY (1 << 7)
+
+enum lp8557_current {
+ LP8557_CURRENT_5_MA = 0x0,
+ LP8557_CURRENT_10_MA,
+ LP8557_CURRENT_13_MA,
+ LP8557_CURRENT_15_MA,
+ LP8557_CURRENT_18_MA,
+ LP8557_CURRENT_20_MA,
+ LP8557_CURRENT_23_MA,
+ LP8557_CURRENT_25_MA,
+ LP8557_CURRENT_MAX,
+};
+#define LP8557_CURRENT 0x11
+#define LP8557_CURRENT_MAXCURR(x) (((x) & 0x7) << 0)
+#define LP8557_CURRENT_ISET (1 << 7)
+
+enum lp8557_pgen_frequency {
+ LP8557_PGEN_FREQ_4_9_KHZ = 0x0,
+ LP8557_PGEN_FREQ_9_8_KHZ,
+ LP8557_PGEN_FREQ_14_6_KHZ,
+ LP8557_PGEN_FREQ_19_5_KHZ,
+ LP8557_PGEN_FREQ_24_4_KHZ,
+ LP8557_PGEN_FREQ_29_3_KHZ,
+ LP8557_PGEN_FREQ_34_2_KHZ,
+ LP8557_PGEN_FREQ_39_1_KHZ,
+ LP8557_PGEN_FREQ_MAX,
+};
+#define LP8557_PGEN 0x12
+#define LP8557_PGEN_FREQ(x) (((x) & 0x7) << 0)
+#define LP8557_PGEN_MAGIC (5 << 3)
+#define LP8557_PGEN_FSET (1 << 7)
+
+enum lp8557_boost_freq {
+ LP8557_BOOST_FREQ_500_KHZ = 0x0,
+ LP8557_BOOST_FREQ_1_MHZ,
+ LP8557_BOOST_FREQ_MAX,
+};
+enum lp8557_boost_bcomp {
+ LP8557_BOOST_BCOMP_OPTION_0 = 0x0,
+ LP8557_BOOST_BCOMP_OPTION_1,
+ LP8557_BOOST_BCOMP_MAX,
+};
+#define LP8557_BOOST 0x13
+#define LP8557_BOOST_FREQ(x) (((x) & 0x1) << 0)
+#define LP8557_BOOST_BCOMP(x) (((x) & 0x1) << 1)
+#define LP8557_BOOST_BCSET (1 << 6)
+#define LP8557_BOOST_BFSET (1 << 7)
+
+#define LP8557_LED_ENABLE 0x14
+#define LP8557_LED_ENABLE_SINKS(x) (((x) & 0x3F) << 0)
+#define LP8557_LED_ENABLE_MAGIC (2 << 6)
+
+enum lp8557_step_ramp {
+ LP8557_STEP_RAMP_0_MS = 0x0,
+ LP8557_STEP_RAMP_50_MS,
+ LP8557_STEP_RAMP_100_MS,
+ LP8557_STEP_RAMP_200_MS,
+ LP8557_STEP_RAMP_MAX,
+};
+enum lp8557_step_smoothing {
+ LP8557_STEP_SMOOTHING_NONE = 0x0,
+ LP8557_STEP_SMOOTHING_LIGHT,
+ LP8557_STEP_SMOOTHING_MEDIUM,
+ LP8557_STEP_SMOOTHING_HEAVY,
+ LP8557_STEP_SMOOTHING_MAX,
+};
+#define LP8557_STEP 0x15
+#define LP8557_STEP_RAMP(x) (((x) & 0x3) << 0)
+#define LP8557_STEP_SMOOTHING(x) (((x) & 0x3) << 6)
+
+struct mipi_dsi_device;
+struct soc_nvidia_tegra132_config;
+
+struct panel_jdi {
+ struct mipi_dsi_device *dsi;
+ const struct soc_nvidia_tegra132_config *mode;
+
+ /* Physical size */
+ unsigned int width_mm;
+ unsigned int height_mm;
+
+ int enabled;
+};
+
+struct panel_jdi *panel_jdi_dsi_probe(struct mipi_dsi_device *dsi);
+int panel_jdi_prepare(struct panel_jdi *jdi);
+
+#endif
diff --git a/src/soc/nvidia/tegra132/mipi-phy.c b/src/soc/nvidia/tegra132/mipi-phy.c
new file mode 100644
index 0000000000..35adf6e6ee
--- /dev/null
+++ b/src/soc/nvidia/tegra132/mipi-phy.c
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <lib.h>
+#include <stdlib.h>
+
+#include <soc/addressmap.h>
+#include <soc/clock.h>
+#include <device/device.h>
+#include <soc/nvidia/tegra/types.h>
+#include <soc/display.h>
+#include <soc/mipi_dsi.h>
+#include <soc/mipi_display.h>
+#include <soc/tegra_dsi.h>
+#include <soc/mipi-phy.h>
+
+int mipi_dphy_set_timing(struct tegra_dsi *dsi)
+{
+
+ u32 freq = (dsi->clk_rate * 2) / 1000000;
+
+ u32 thsdexit = (DSI_PHY_TIMING_DIV(120, (freq)));
+ u32 thstrial = (((3) + (DSI_PHY_TIMING_DIV((DSI_THSTRAIL_VAL(freq)),
+ freq))));
+ u32 tdatzero = DSI_PHY_TIMING_DIV(((145) + (5 * (DSI_TBIT(freq)))),
+ (freq));
+ u32 thsprepare = DSI_PHY_TIMING_DIV((65 + (5*(DSI_TBIT(freq)))), freq);
+ u32 tclktrial = (DSI_PHY_TIMING_DIV(80, freq));
+ u32 tclkpost = ((DSI_PHY_TIMING_DIV(((70) + ((52) * (DSI_TBIT(freq)))),
+ freq)));
+ u32 tclkzero = (DSI_PHY_TIMING_DIV(260, freq));
+ u32 ttlpx = (DSI_PHY_TIMING_DIV(60, freq)) ;
+ u32 tclkprepare = (DSI_PHY_TIMING_DIV(60, freq));
+ u32 tclkpre = 1; //min = 8*UI per mipi spec, tclk_pre=0 should be ok, but using 1 value
+ u32 twakeup = 0x7F; //min = 1ms
+
+ u32 ttaget;
+ u32 ttassure;
+ u32 ttago;
+ u32 value;
+
+ if (!ttlpx) {
+ ttaget = 5;
+ ttassure = 2;
+ ttago = 4;
+ } else {
+ ttaget = 5 * ttlpx;
+ ttassure = 2 * ttlpx;
+ ttago = 4 * ttlpx;
+ }
+
+ value = (thsdexit << 24) |
+ (thstrial << 16) |
+ (tdatzero << 8) |
+ (thsprepare << 0);
+ tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0);
+
+ value = (tclktrial << 24) |
+ (tclkpost << 16) |
+ (tclkzero << 8) |
+ (ttlpx << 0);
+ tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1);
+
+ value = (tclkprepare << 16) |
+ (tclkpre << 8) |
+ (twakeup << 0);
+ tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2);
+
+ value = (ttaget << 16) |
+ (ttassure << 8) |
+ (ttago << 0),
+ tegra_dsi_writel(dsi, value, DSI_BTA_TIMING);
+ return 0;
+}
diff --git a/src/soc/nvidia/tegra132/mipi.c b/src/soc/nvidia/tegra132/mipi.c
new file mode 100644
index 0000000000..abd2230378
--- /dev/null
+++ b/src/soc/nvidia/tegra132/mipi.c
@@ -0,0 +1,151 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <lib.h>
+#include <stdlib.h>
+#include <delay.h>
+#include <soc/addressmap.h>
+#include <soc/clock.h>
+#include <device/device.h>
+#include <soc/nvidia/tegra/types.h>
+#include <soc/display.h>
+#include <soc/mipi_dsi.h>
+#include <soc/tegra_dsi.h>
+#include "jdi_25x18_display/panel-jdi-lpm102a188a.h"
+
+static unsigned long dsi_pads[] = {
+ 0x060, /* DSIA & DSIB pads */
+ 0x180, /* DSIC & DSID pads */
+};
+
+static struct tegra_mipi mipi_data = {
+ .regs = (void *)TEGRA_MIPI_CAL_BASE,
+};
+
+static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi,
+ unsigned long reg)
+{
+ return readl(mipi->regs + (reg << 2));
+}
+
+static inline void tegra_mipi_writel(struct tegra_mipi *mipi,
+ unsigned long value, unsigned long reg)
+{
+ writel(value, mipi->regs + (reg << 2));
+}
+
+static const struct calibration_regs tegra124_mipi_calibration_regs[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
+};
+static const struct tegra_mipi_config tegra124_mipi_config = {
+ .calibrate_clk_lane = 1,
+ .regs = tegra124_mipi_calibration_regs,
+ .num_pads = ARRAY_SIZE(tegra124_mipi_calibration_regs),
+};
+
+struct tegra_mipi_device *tegra_mipi_request(struct tegra_mipi_device *device,
+ int device_index)
+{
+ device->mipi = &mipi_data;
+ device->config = &tegra124_mipi_config;
+ device->pads = dsi_pads[device_index];
+
+ return device;
+}
+
+static int tegra_mipi_wait(struct tegra_mipi *mipi)
+{
+ u32 poll_interval_us = 1000;
+ u32 timeout_us = 250 * 1000;
+ unsigned long value;
+
+ do {
+ value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
+ if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
+ (value & MIPI_CAL_STATUS_DONE) != 0)
+ return 0;
+
+ if (timeout_us > poll_interval_us)
+ timeout_us -= poll_interval_us;
+ else
+ break;
+
+ udelay(poll_interval_us);
+ } while (1);
+
+ printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__);
+ return -ETIMEDOUT;
+}
+
+int tegra_mipi_calibrate(struct tegra_mipi_device *device)
+{
+ const struct tegra_mipi_config *cfg = device->config;
+ unsigned long value, clk_value;
+ unsigned int i;
+ int err;
+
+ value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
+ value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+ tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+
+ tegra_mipi_writel(device->mipi, MIPI_CAL_BIAS_PAD_CFG1_DEFAULT,
+ MIPI_CAL_BIAS_PAD_CFG1);
+
+ value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
+ tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ for (i = 0; i < cfg->num_pads; i++) {
+ if (device->pads & BIT(i)) {
+ value = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSPDOS(0) |
+ MIPI_CAL_CONFIG_HSPUOS(4) |
+ MIPI_CAL_CONFIG_TERMOS(5);
+ clk_value = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSCLKPDOSD(0) |
+ MIPI_CAL_CONFIG_HSCLKPUOSD(4);
+ } else {
+ value = 0;
+ clk_value = 0;
+ }
+
+ tegra_mipi_writel(device->mipi, value, cfg->regs[i].data);
+
+ if (cfg->calibrate_clk_lane)
+ tegra_mipi_writel(device->mipi, clk_value,
+ cfg->regs[i].clk);
+ }
+
+ value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
+ value |= MIPI_CAL_CTRL_START;
+ tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
+
+ err = tegra_mipi_wait(device->mipi);
+
+ return err;
+}
diff --git a/src/soc/nvidia/tegra132/mipi_dsi.c b/src/soc/nvidia/tegra132/mipi_dsi.c
new file mode 100644
index 0000000000..440bb23dfc
--- /dev/null
+++ b/src/soc/nvidia/tegra132/mipi_dsi.c
@@ -0,0 +1,431 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <lib.h>
+#include <stdlib.h>
+#include <delay.h>
+#include <string.h>
+#include <soc/addressmap.h>
+#include <soc/clock.h>
+#include <device/device.h>
+#include <soc/nvidia/tegra/types.h>
+#include <soc/display.h>
+#include <soc/mipi_dsi.h>
+#include <soc/mipi_display.h>
+#include <soc/tegra_dsi.h>
+
+struct mipi_dsi_device mipi_dsi_device_data[NUM_DSI] = {
+ {
+ .master = NULL,
+ .slave = &mipi_dsi_device_data[DSI_B],
+ },
+ {
+ .master = &mipi_dsi_device_data[DSI_A],
+ .slave = NULL,
+ },
+};
+
+static struct mipi_dsi_device *
+mipi_dsi_device_alloc(struct mipi_dsi_host *host)
+{
+ static int index = 0;
+ struct mipi_dsi_device *dsi;
+
+ if (index >= NUM_DSI)
+ return (void *)-EPTR;
+
+ dsi = &mipi_dsi_device_data[index++];
+ dsi->host = host;
+ return dsi;
+}
+
+static struct mipi_dsi_device *
+of_mipi_dsi_device_add(struct mipi_dsi_host *host)
+{
+ struct mipi_dsi_device *dsi;
+ u32 reg = 0;
+
+ dsi = mipi_dsi_device_alloc(host);
+ if (IS_ERR_PTR(dsi)) {
+ printk(BIOS_ERR, "failed to allocate DSI device\n");
+ return dsi;
+ }
+
+ dsi->channel = reg;
+ host->dev = (void *)dsi;
+
+ return dsi;
+}
+
+int mipi_dsi_host_register(struct mipi_dsi_host *host)
+{
+ of_mipi_dsi_device_add(host);
+ return 0;
+}
+
+/**
+ * mipi_dsi_attach - attach a DSI device to its DSI host
+ * @dsi: DSI peripheral
+ */
+int mipi_dsi_attach(struct mipi_dsi_device *dsi)
+{
+ const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+
+ if (!ops || !ops->attach)
+ return -ENOSYS;
+
+ return ops->attach(dsi->host, dsi);
+}
+
+/**
+ * mipi_dsi_detach - detach a DSI device from its DSI host
+ * @dsi: DSI peripheral
+ */
+int mipi_dsi_detach(struct mipi_dsi_device *dsi)
+{
+ const struct mipi_dsi_host_ops *ops = dsi->host->ops;
+
+ if (!ops || !ops->detach)
+ return -ENOSYS;
+
+ return ops->detach(dsi->host, dsi);
+}
+
+/**
+ * mipi_dsi_enslave() - use a MIPI DSI peripheral as slave for dual-channel
+ * operation
+ * @master: master DSI peripheral device
+ * @slave: slave DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_enslave(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave)
+{
+ int err = 0;
+
+ slave->master = master;
+ master->slave = slave;
+
+ if (master->ops && master->ops->enslave)
+ err = master->ops->enslave(master, slave);
+
+ return err;
+}
+
+/**
+ * mipi_dsi_liberate() - stop using a MIPI DSI peripheral as slave for dual-
+ * channel operation
+ * @master: master DSI peripheral device
+ * @slave: slave DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_liberate(struct mipi_dsi_device *master,
+ struct mipi_dsi_device *slave)
+{
+ int err = 0;
+
+ if (master->ops && master->ops->liberate)
+ err = master->ops->liberate(master, slave);
+
+ master->slave = NULL;
+ slave->master = NULL;
+
+ return err;
+}
+
+/**
+ * mipi_dsi_dcs_write() - send DCS write command
+ * @dsi: DSI peripheral device
+ * @cmd: DCS command
+ * @data: buffer containing the command payload
+ * @len: command payload length
+ *
+ * This function will automatically choose the right data type depending on
+ * the command payload length.
+ *
+ * Return: The number of bytes successfully transmitted or a negative error
+ * code on failure.
+ */
+ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
+ const void *data, size_t len)
+{
+ struct mipi_dsi_msg msg;
+ ssize_t err;
+ size_t size;
+
+ u8 buffer[MAX_DSI_HOST_FIFO_DEPTH + 4];
+ u8 *tx = buffer;
+
+ if (len > MAX_DSI_HOST_FIFO_DEPTH) {
+ printk(BIOS_ERR, "%s: Error: too large payload length: %zu\n",
+ __func__, len);
+
+ return -EINVAL;
+ }
+
+ if (len > 0) {
+ unsigned int offset = 0;
+
+ /*
+ * DCS long write packets contain the word count in the header
+ * bytes 1 and 2 and have a payload containing the DCS command
+ * byte folowed by word count minus one bytes.
+ *
+ * DCS short write packets encode the DCS command and up to
+ * one parameter in header bytes 1 and 2.
+ */
+ if (len > 1)
+ size = 3 + len;
+ else
+ size = 1 + len;
+
+ /* write word count to header for DCS long write packets */
+ if (len > 1) {
+ tx[offset++] = ((1 + len) >> 0) & 0xff;
+ tx[offset++] = ((1 + len) >> 8) & 0xff;
+ }
+
+ /* write the DCS command byte followed by the payload */
+ tx[offset++] = cmd;
+ memcpy(tx + offset, data, len);
+ } else {
+ tx = &cmd;
+ size = 1;
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.flags = MIPI_DSI_MSG_USE_LPM;
+ msg.channel = dsi->channel;
+ msg.tx_len = size;
+ msg.tx_buf = tx;
+
+ switch (len) {
+ case 0:
+ msg.type = MIPI_DSI_DCS_SHORT_WRITE;
+ break;
+ case 1:
+ msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+ break;
+ default:
+ msg.type = MIPI_DSI_DCS_LONG_WRITE;
+ break;
+ }
+
+ err = dsi->host->ops->transfer(dsi->host, &msg);
+
+ return err;
+}
+
+/**
+ * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
+ * module
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
+{
+ ssize_t err;
+
+ err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * mipi_dsi_dcs_set_display_on() - start displaying the image data on the
+ * display device
+ * @dsi: DSI peripheral device
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
+{
+ ssize_t err;
+
+ err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * mipi_dsi_dcs_set_column_address() - define the column extent of the frame
+ * memory accessed by the host processor
+ * @dsi: DSI peripheral device
+ * @start: first column of frame memory
+ * @end: last column of frame memory
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
+ u16 end)
+{
+ u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
+ ssize_t err;
+
+ err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
+ sizeof(payload));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * mipi_dsi_dcs_set_page_address() - define the page extent of the frame
+ * memory accessed by the host processor
+ * @dsi: DSI peripheral device
+ * @start: first page of frame memory
+ * @end: last page of frame memory
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
+ u16 end)
+{
+ u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
+ ssize_t err;
+
+ err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
+ sizeof(payload));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
+ * output signal on the TE signal line.
+ * @dsi: DSI peripheral device
+ * @mode: the Tearing Effect Output Line mode
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
+ enum mipi_dsi_dcs_tear_mode mode)
+{
+ u8 value = mode;
+ ssize_t err;
+
+ err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
+ sizeof(value));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
+ * data used by the interface
+ * @dsi: DSI peripheral device
+ * @format: pixel format
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
+{
+ ssize_t err;
+
+ err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
+ sizeof(format));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/**
+ * mipi_dsi_dcs_set_address_mode() - sets the data order for forward transfers
+ * from the host to the peripheral
+ * @dsi: DSI peripheral device
+ * @reverse_page_address: reverses the page addressing to bottom->top
+ * @reverse_col_address: reverses the column addressing to right->left
+ * @reverse_page_col_address: reverses the page/column addressing order
+ * @refresh_from_bottom: refresh the display bottom to top
+ * @reverse_rgb: send pixel data bgr instead of rgb
+ * @latch_right_to_left: latch the incoming display data right to left
+ * @flip_horizontal: flip the image horizontally, left to right
+ * @flip_vertical: flip the image vertically, top to bottom
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi,
+ bool reverse_page_address,
+ bool reverse_col_address,
+ bool reverse_page_col_address,
+ bool refresh_from_bottom,
+ bool reverse_rgb,
+ bool latch_right_to_left,
+ bool flip_horizontal,
+ bool flip_vertical)
+{
+ ssize_t err;
+ u8 data;
+
+ data = ((flip_vertical ? 1 : 0) << 0) |
+ ((flip_horizontal ? 1 : 0) << 1) |
+ ((latch_right_to_left ? 1 : 0) << 2) |
+ ((reverse_rgb ? 1 : 0) << 3) |
+ ((refresh_from_bottom ? 1 : 0) << 4) |
+ ((reverse_page_col_address ? 1 : 0) << 5) |
+ ((reverse_col_address ? 1 : 0) << 6) |
+ ((reverse_page_address ? 1 : 0) << 7);
+
+ err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &data, 1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
diff --git a/src/soc/nvidia/tegra132/soc.c b/src/soc/nvidia/tegra132/soc.c
index 0014f84b54..d80a388f3c 100644
--- a/src/soc/nvidia/tegra132/soc.c
+++ b/src/soc/nvidia/tegra132/soc.c
@@ -76,11 +76,6 @@ static struct cpu_control_ops cntrl_ops = {
.start_cpu = cntrl_start_cpu,
};
-void display_startup(device_t dev)
-{
- printk(BIOS_INFO, "Entering %s()\n", __func__);
-}
-
static void soc_init(device_t dev)
{
struct soc_nvidia_tegra132_config *cfg;
diff --git a/src/soc/nvidia/tegra132/tegra_dsi.c b/src/soc/nvidia/tegra132/tegra_dsi.c
new file mode 100644
index 0000000000..3a7949d655
--- /dev/null
+++ b/src/soc/nvidia/tegra132/tegra_dsi.c
@@ -0,0 +1,874 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <lib.h>
+#include <stdlib.h>
+#include <delay.h>
+#include <timer.h>
+#include <soc/addressmap.h>
+#include <soc/clock.h>
+#include <device/device.h>
+#include <soc/nvidia/tegra/types.h>
+#include "chip.h"
+#include <soc/display.h>
+#include <soc/mipi_dsi.h>
+#include <soc/mipi_display.h>
+#include <soc/tegra_dsi.h>
+#include <soc/mipi-phy.h>
+#include "jdi_25x18_display/panel-jdi-lpm102a188a.h"
+
+struct tegra_mipi_device mipi_device_data[NUM_DSI];
+
+struct tegra_dsi dsi_data[NUM_DSI] = {
+ {
+ .regs = (void *)TEGRA_DSIA_BASE,
+ .channel = 0,
+ .slave = &dsi_data[DSI_B],
+ .master = NULL,
+ .video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH,
+ .host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH,
+ },
+ {
+ .regs = (void *)TEGRA_DSIB_BASE,
+ .channel = 0,
+ .slave = NULL,
+ .master = &dsi_data[DSI_A],
+ .video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH,
+ .host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH,
+ },
+};
+
+static inline struct tegra_dsi *host_to_tegra(struct mipi_dsi_host *host)
+{
+ return container_of(host, struct tegra_dsi, host);
+}
+
+/*
+ * non-burst mode with sync pulses
+ */
+static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = {
+ [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+ PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+ PKT_LP,
+ [ 1] = 0,
+ [ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+ PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+ PKT_LP,
+ [ 3] = 0,
+ [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+ PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+ PKT_LP,
+ [ 5] = 0,
+ [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+ PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
+ [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
+ PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
+ PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
+ [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+ PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+ PKT_LP,
+ [ 9] = 0,
+ [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+ PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
+ [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
+ PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
+ PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
+};
+
+/*
+ * non-burst mode with sync events
+ */
+static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
+ [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 1] = 0,
+ [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 3] = 0,
+ [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 5] = 0,
+
+ [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+ PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+
+ [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+ [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 9] = 0,
+
+ [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+ PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+
+ [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+};
+
+static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
+ [ 0] = 0,
+ [ 1] = 0,
+ [ 2] = 0,
+ [ 3] = 0,
+ [ 4] = 0,
+ [ 5] = 0,
+ [ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP,
+ [ 7] = 0,
+ [ 8] = 0,
+ [ 9] = 0,
+ [10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP,
+ [11] = 0,
+};
+
+static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
+{
+ int err;
+
+ err = mipi_dphy_set_timing(dsi);
+ if (err < 0) {
+ printk(BIOS_ERR, "failed to set D-PHY timing: %d\n", err);
+ return err;
+ }
+
+ if (dsi->slave)
+ tegra_dsi_set_phy_timing(dsi->slave);
+ return 0;
+}
+
+static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
+ unsigned int *mulp, unsigned int *divp)
+{
+ switch (format) {
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ case MIPI_DSI_FMT_RGB888:
+ *mulp = 3;
+ *divp = 1;
+ break;
+
+ case MIPI_DSI_FMT_RGB565:
+ *mulp = 2;
+ *divp = 1;
+ break;
+
+ case MIPI_DSI_FMT_RGB666:
+ *mulp = 9;
+ *divp = 4;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,
+ enum tegra_dsi_format *fmt)
+{
+ switch (format) {
+ case MIPI_DSI_FMT_RGB888:
+ *fmt = TEGRA_DSI_FORMAT_24P;
+ break;
+
+ case MIPI_DSI_FMT_RGB666:
+ *fmt = TEGRA_DSI_FORMAT_18NP;
+ break;
+
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ *fmt = TEGRA_DSI_FORMAT_18P;
+ break;
+
+ case MIPI_DSI_FMT_RGB565:
+ *fmt = TEGRA_DSI_FORMAT_16P;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void tegra_dsi_ganged_enable(struct tegra_dsi *dsi, unsigned int start,
+ unsigned int size)
+{
+ u32 value;
+
+ tegra_dsi_writel(dsi, start, DSI_GANGED_MODE_START);
+ tegra_dsi_writel(dsi, size << 16 | size, DSI_GANGED_MODE_SIZE);
+
+ value = DSI_GANGED_MODE_CONTROL_ENABLE;
+ tegra_dsi_writel(dsi, value, DSI_GANGED_MODE_CONTROL);
+}
+
+static void tegra_dsi_enable(struct tegra_dsi *dsi)
+{
+ u32 value;
+
+ value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
+ value |= DSI_POWER_CONTROL_ENABLE;
+ tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
+
+ if (dsi->slave)
+ tegra_dsi_enable(dsi->slave);
+}
+
+static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
+ const struct soc_nvidia_tegra132_config *mode)
+{
+ unsigned int hact, hsw, hbp, hfp, i, mul, div;
+ enum tegra_dsi_format format;
+ const u32 *pkt_seq;
+ u32 value;
+ int err;
+
+ if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+ printk(BIOS_SPEW, "Non-burst video mode with sync pulses\n");
+ pkt_seq = pkt_seq_video_non_burst_sync_pulses;
+ } else if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
+ printk(BIOS_SPEW, "Non-burst video mode with sync events\n");
+ pkt_seq = pkt_seq_video_non_burst_sync_events;
+ } else {
+ printk(BIOS_SPEW, "Command mode\n");
+ pkt_seq = pkt_seq_command_mode;
+ }
+
+ err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
+ if (err < 0)
+ return err;
+
+ err = tegra_dsi_get_format(dsi->format, &format);
+ if (err < 0)
+ return err;
+
+ value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
+ DSI_CONTROL_LANES(dsi->lanes - 1) |
+ DSI_CONTROL_SOURCE(pipe);
+ tegra_dsi_writel(dsi, value, DSI_CONTROL);
+
+ tegra_dsi_writel(dsi, dsi->video_fifo_depth, DSI_MAX_THRESHOLD);
+
+ value = DSI_HOST_CONTROL_HS;
+ tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+
+ value = tegra_dsi_readl(dsi, DSI_CONTROL);
+
+ if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+ value |= DSI_CONTROL_HS_CLK_CTRL;
+
+ value &= ~DSI_CONTROL_TX_TRIG(3);
+
+ /* enable DCS commands for command mode */
+ if (dsi->flags & MIPI_DSI_MODE_VIDEO)
+ value &= ~DSI_CONTROL_DCS_ENABLE;
+ else
+ value |= DSI_CONTROL_DCS_ENABLE;
+
+ value |= DSI_CONTROL_VIDEO_ENABLE;
+ value &= ~DSI_CONTROL_HOST_ENABLE;
+ tegra_dsi_writel(dsi, value, DSI_CONTROL);
+
+ for (i = 0; i < NUM_PKT_SEQ; i++)
+ tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i);
+
+ if (dsi->flags & MIPI_DSI_MODE_VIDEO) {
+ /* horizontal active pixels */
+ hact = mode->xres * mul / div;
+
+ /* horizontal sync width */
+ hsw = (hsync_end(mode) - hsync_start(mode)) * mul / div;
+ hsw -= 10;
+
+ /* horizontal back porch */
+ hbp = (htotal(mode) - hsync_end(mode)) * mul / div;
+ hbp -= 14;
+
+ /* horizontal front porch */
+ hfp = (hsync_start(mode) - mode->xres) * mul / div;
+ hfp -= 8;
+
+ tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1);
+ tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3);
+ tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5);
+ tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7);
+
+ /* set SOL delay (for non-burst mode only) */
+ tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY);
+
+ /* TODO: implement ganged mode */
+ } else {
+ u16 bytes;
+ if (dsi->ganged_mode) {
+ /*
+ * For ganged mode, assume symmetric left-right mode.
+ */
+ bytes = 1 + (mode->xres / 2) * mul / div;
+ } else {
+ /* 1 byte (DCS command) + pixel data */
+ bytes = 1 + mode->xres * mul / div;
+ }
+
+ tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1);
+ tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_2_3);
+ tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5);
+ tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7);
+
+ value = MIPI_DCS_WRITE_MEMORY_START << 8 |
+ MIPI_DCS_WRITE_MEMORY_CONTINUE;
+ tegra_dsi_writel(dsi, value, DSI_DCS_CMDS);
+
+ /* set SOL delay */
+ if (dsi->ganged_mode) {
+ unsigned long delay, bclk, bclk_ganged;
+ unsigned int lanes = dsi->ganged_lanes;
+
+ /* SOL to valid, valid to FIFO and FIFO write delay */
+ delay = 4 + 4 + 2;
+ delay = DIV_ROUND_UP(delay * mul, div * lanes);
+ /* FIFO read delay */
+ delay = delay + 6;
+
+ bclk = DIV_ROUND_UP(htotal(mode) * mul, div * lanes);
+ bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes);
+ value = bclk - bclk_ganged + delay + 20;
+ } else {
+ /* TODO: revisit for non-ganged mode */
+ value = 8 * mul / div;
+ }
+
+ tegra_dsi_writel(dsi, value, DSI_SOL_DELAY);
+ }
+
+ if (dsi->slave) {
+ err = tegra_dsi_configure(dsi->slave, pipe, mode);
+ if (err < 0)
+ return err;
+
+ /*
+ * enable ganged mode
+ */
+ if (dsi->ganged_mode) {
+ tegra_dsi_ganged_enable(dsi, mode->xres / 2,
+ mode->xres / 2);
+ tegra_dsi_ganged_enable(dsi->slave, 0, mode->xres / 2);
+ }
+ }
+ return 0;
+}
+
+static int tegra_output_dsi_enable(struct tegra_dsi *dsi,
+ const struct soc_nvidia_tegra132_config *config)
+{
+ int err;
+
+ if (dsi->enabled)
+ return 0;
+
+ err = tegra_dsi_configure(dsi, 0, config);
+ if (err < 0)
+ return err;
+
+ /* enable DSI controller */
+ tegra_dsi_enable(dsi);
+
+ dsi->enabled = true;
+ return 0;
+}
+
+
+static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk,
+ unsigned int vrefresh)
+{
+ unsigned int timeout;
+ u32 value;
+
+ /* one frame high-speed transmission timeout */
+ timeout = (bclk / vrefresh) / 512;
+ value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
+ tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
+
+ /* 2 ms peripheral timeout for panel */
+ timeout = 2 * bclk / 512 * 1000;
+ value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
+ tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
+
+ value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
+ tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+
+ if (dsi->slave)
+ tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh);
+}
+
+static int tegra_output_dsi_setup_clock(struct tegra_dsi *dsi,
+ const struct soc_nvidia_tegra132_config *config)
+{
+ unsigned int mul, div, num_lanes; // , vrefresh, num_lanes;
+ unsigned long bclk;
+ unsigned long pclk = config->pixel_clock;
+ int plld;
+ int err;
+
+ err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
+ if (err < 0)
+ return err;
+
+ /*
+ * In ganged mode, account for the total number of lanes across both
+ * DSI channels so that the bit clock is properly computed.
+ */
+ if (dsi->ganged_mode)
+ num_lanes = dsi->ganged_lanes;
+ else
+ num_lanes = dsi->lanes;
+
+ /* compute byte clock */
+ bclk = (pclk * mul) / (div * num_lanes);
+
+ /*
+ * Compute bit clock and round up to the next MHz.
+ */
+ plld = DIV_ROUND_UP(bclk * 8, USECS_PER_SEC) * USECS_PER_SEC;
+
+ /*
+ * the actual rate on PLLD_OUT0 is 1/2 plld
+ */
+ dsi->clk_rate = plld / 2;
+ if (dsi->slave)
+ dsi->slave->clk_rate = dsi->clk_rate;
+
+ /* set up plld */
+ plld = clock_display(plld);
+ if (plld == 0) {
+ printk(BIOS_ERR, "%s: clock init failed\n", __func__);
+ return -1;
+ }
+
+ tegra_dsi_set_timeout(dsi, bclk, config->refresh);
+ return plld/1000000;
+}
+
+
+
+static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
+{
+ unsigned long value;
+
+ value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
+ tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
+ return 0;
+}
+
+static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)
+{
+ u32 value;
+
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
+ tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
+
+ /* start calibration */
+ tegra_dsi_pad_enable(dsi);
+
+ value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
+ DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
+ DSI_PAD_OUT_CLK(0x0);
+ tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
+
+ return tegra_mipi_calibrate(dsi->mipi);
+}
+
+static const char * const error_report[16] = {
+ "SoT Error",
+ "SoT Sync Error",
+ "EoT Sync Error",
+ "Escape Mode Entry Command Error",
+ "Low-Power Transmit Sync Error",
+ "Peripheral Timeout Error",
+ "False Control Error",
+ "Contention Detected",
+ "ECC Error, single-bit",
+ "ECC Error, multi-bit",
+ "Checksum Error",
+ "DSI Data Type Not Recognized",
+ "DSI VC ID Invalid",
+ "Invalid Transmission Length",
+ "Reserved",
+ "DSI Protocol Violation",
+};
+
+static int tegra_dsi_read_response(struct tegra_dsi *dsi,
+ const struct mipi_dsi_msg *msg,
+ unsigned int count)
+{
+ u8 *rx = msg->rx_buf;
+ unsigned int i, j, k;
+ size_t size = 0;
+ u16 errors;
+ u32 value;
+
+ /* read and parse packet header */
+ value = tegra_dsi_readl(dsi, DSI_RD_DATA);
+
+ switch (value & 0x3f) {
+ case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+ errors = (value >> 8) & 0xffff;
+ printk(BIOS_ERR, "Acknowledge and error report: %04x\n",
+ errors);
+ for (i = 0; i < ARRAY_SIZE(error_report); i++)
+ if (errors & BIT(i))
+ printk(BIOS_INFO, " %2u: %s\n", i,
+ error_report[i]);
+ break;
+
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+ rx[0] = (value >> 8) & 0xff;
+ break;
+
+ case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+ rx[0] = (value >> 8) & 0xff;
+ rx[1] = (value >> 16) & 0xff;
+ break;
+
+ case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
+ size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
+ break;
+
+ case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
+ size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
+ break;
+
+ default:
+ printk(BIOS_ERR, "unhandled response type: %02x\n",
+ value & 0x3f);
+ break;
+ }
+
+ size = MIN(size, msg->rx_len);
+
+ if (msg->rx_buf && size > 0) {
+ for (i = 0, j = 0; i < count - 1; i++, j += 4) {
+ value = tegra_dsi_readl(dsi, DSI_RD_DATA);
+
+ for (k = 0; k < 4 && (j + k) < msg->rx_len; k++)
+ rx[j + k] = (value >> (k << 3)) & 0xff;
+ }
+ }
+ return 0;
+}
+
+static int tegra_dsi_transmit(struct tegra_dsi *dsi, unsigned long timeout_ms)
+{
+ u32 poll_interval_us = 2000;
+ u32 timeout_us = timeout_ms * 1000;
+
+ tegra_dsi_writel(dsi, DSI_TRIGGER_HOST, DSI_TRIGGER);
+ udelay(poll_interval_us);
+
+ do {
+ u32 value = tegra_dsi_readl(dsi, DSI_TRIGGER);
+ if ((value & DSI_TRIGGER_HOST) == 0)
+ return 0;
+
+ //usleep_range(1000, 2000);
+ if (timeout_us > poll_interval_us)
+ timeout_us -= poll_interval_us;
+ else
+ break;
+
+ udelay(poll_interval_us);
+ } while (1);
+
+ printk(BIOS_ERR, "%s: ERROR: timeout waiting for transmission"
+ " to complete\n", __func__);
+ return -ETIMEDOUT;
+}
+
+static int tegra_dsi_wait_for_response(struct tegra_dsi *dsi,
+ unsigned long timeout_ms)
+{
+ u32 poll_interval_us = 2000;
+ u32 timeout_us = timeout_ms * 1000;
+
+ do {
+ u32 value = tegra_dsi_readl(dsi, DSI_STATUS);
+ u8 count = value & 0x1f;
+
+ if (count > 0)
+ return count;
+
+ if (timeout_us > poll_interval_us)
+ timeout_us -= poll_interval_us;
+ else
+ break;
+
+ udelay(poll_interval_us);
+ } while (1);
+
+ printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__);
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host,
+ const struct mipi_dsi_msg *msg)
+{
+ struct tegra_dsi *dsi = host_to_tegra(host);
+ const u8 *tx = msg->tx_buf;
+ unsigned int count, i, j;
+ u32 value;
+ int err;
+
+ if (msg->tx_len > dsi->video_fifo_depth * 4)
+ return -ENOSPC;
+
+ /* reset underflow/overflow flags */
+ value = tegra_dsi_readl(dsi, DSI_STATUS);
+ if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) {
+ value = DSI_HOST_CONTROL_FIFO_RESET;
+ tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+ udelay(20); // usleep_range(10, 20);
+ }
+
+ value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
+ value |= DSI_POWER_CONTROL_ENABLE;
+ tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
+
+ udelay(7000); //usleep_range(5000, 10000);
+
+ value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST |
+ DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC;
+
+ if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0)
+ value |= DSI_HOST_CONTROL_HS;
+
+ /*
+ * The host FIFO has a maximum of 64 words, so larger transmissions
+ * need to use the video FIFO.
+ */
+ if (msg->tx_len > dsi->host_fifo_depth * 4)
+ value |= DSI_HOST_CONTROL_FIFO_SEL;
+
+ tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+
+ /*
+ * For reads and messages with explicitly requested ACK, generate a
+ * BTA sequence after the transmission of the packet.
+ */
+ if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
+ (msg->rx_buf && msg->rx_len > 0)) {
+ value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
+ value |= DSI_HOST_CONTROL_PKT_BTA;
+ tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+ }
+
+ value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE;
+ tegra_dsi_writel(dsi, value, DSI_CONTROL);
+
+ /* write packet header */
+ value = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
+
+ if (tx && msg->tx_len > 0)
+ value |= tx[0] << 8;
+
+ if (tx && msg->tx_len > 1)
+ value |= tx[1] << 16;
+
+ tegra_dsi_writel(dsi, value, DSI_WR_DATA);
+
+ /* write payload (if any) */
+ if (msg->tx_len > 2) {
+ for (j = 2; j < msg->tx_len; j += 4) {
+ value = 0;
+
+ for (i = 0; i < 4 && j + i < msg->tx_len; i++)
+ value |= tx[j + i] << (i << 3);
+
+ tegra_dsi_writel(dsi, value, DSI_WR_DATA);
+ }
+ }
+
+ err = tegra_dsi_transmit(dsi, 250);
+ if (err < 0)
+ return err;
+
+ if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
+ (msg->rx_buf && msg->rx_len > 0)) {
+ err = tegra_dsi_wait_for_response(dsi, 250);
+ if (err < 0)
+ return err;
+
+ count = err;
+
+ value = tegra_dsi_readl(dsi, DSI_RD_DATA);
+ switch (value) {
+ case 0x84:
+ /*
+ dev_dbg(dsi->dev, "ACK\n");
+ */
+ break;
+
+ case 0x87:
+ /*
+ dev_dbg(dsi->dev, "ESCAPE\n");
+ */
+ break;
+
+ default:
+ printk(BIOS_INFO, "unknown status: %08x\n", value);
+ break;
+ }
+
+ if (count > 1) {
+ err = tegra_dsi_read_response(dsi, msg, count);
+ if (err < 0)
+ printk(BIOS_INFO,
+ "failed to parse response: %d\n",
+ err);
+ }
+ }
+ return 0;
+}
+
+static int tegra_dsi_ganged_setup(struct tegra_dsi *dsi,
+ struct tegra_dsi *slave)
+{
+ /*
+ * The number of ganged lanes is the sum of lanes of all peripherals
+ * in the gang.
+ */
+ dsi->slave->ganged_lanes = dsi->lanes + dsi->slave->lanes;
+ dsi->slave->ganged_mode = 1;
+
+ dsi->ganged_lanes = dsi->lanes + dsi->slave->lanes;
+ dsi->ganged_mode = 1;
+ return 0;
+}
+
+static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
+ struct mipi_dsi_device *device)
+{
+ struct tegra_dsi *dsi = host_to_tegra(host);
+ int err;
+
+ dsi->flags = device->mode_flags;
+ dsi->format = device->format;
+ dsi->lanes = device->lanes;
+
+ if (dsi->master) {
+ err = tegra_dsi_ganged_setup(dsi->master, dsi);
+ if (err < 0) {
+ printk(BIOS_ERR, "failed to set up ganged mode: %d\n",
+ err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+static const struct mipi_dsi_host_ops tegra_dsi_host_ops = {
+ .attach = tegra_dsi_host_attach,
+ .transfer = tegra_dsi_host_transfer,
+};
+
+static int dsi_probe_if(int dsi_index,
+ struct soc_nvidia_tegra132_config *config)
+{
+ struct tegra_dsi *dsi = &dsi_data[dsi_index];
+ int err;
+
+ /*
+ * Set default value. Will be taken from attached device once detected
+ */
+ dsi->flags = 0;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->lanes = 4;
+
+ /* get tegra_mipi_device */
+ dsi->mipi = tegra_mipi_request(&mipi_device_data[dsi_index], dsi_index);
+
+ /* calibrate */
+ err = tegra_dsi_pad_calibrate(dsi);
+ if (err < 0) {
+ printk(BIOS_ERR, "MIPI calibration failed: %d\n", err);
+ return err;
+ }
+
+ dsi->host.ops = &tegra_dsi_host_ops;
+ err = mipi_dsi_host_register(&dsi->host);
+ if (err < 0) {
+ printk(BIOS_ERR, "failed to register DSI host: %d\n", err);
+ return err;
+ }
+
+ /* get panel */
+ dsi->panel = panel_jdi_dsi_probe((struct mipi_dsi_device *)dsi->host.dev);
+ if (IS_ERR_PTR(dsi->panel)) {
+ printk(BIOS_ERR, "failed to get dsi panel\n");
+ return -EPTR;
+ }
+ dsi->panel->mode = config;
+ return 0;
+}
+
+static int dsi_probe(struct soc_nvidia_tegra132_config *config)
+{
+ dsi_probe_if(DSI_A, config);
+ dsi_probe_if(DSI_B, config);
+ return 0;
+}
+
+int dsi_enable(struct soc_nvidia_tegra132_config *config)
+{
+ struct tegra_dsi *dsi_a = &dsi_data[DSI_A];
+
+ dsi_probe(config);
+
+ /* set up clock and TimeOutRegisters */
+ tegra_output_dsi_setup_clock(dsi_a, config);
+
+ /* configure APB_MISC_GP_MIPI_PAD_CTRL_0 */
+ write32(DSIB_MODE_DSI, (unsigned int *)APB_MISC_GP_MIPI_PAD_CTRL_0);
+
+ /* configure phy interface timing registers */
+ tegra_dsi_set_phy_timing(dsi_a);
+
+ /* prepare panel */
+ panel_jdi_prepare(dsi_a->panel);
+
+ /* enable dsi */
+ if (tegra_output_dsi_enable(dsi_a, config)) {
+ printk(BIOS_ERR,"%s: Error: failed to enable dsi output.\n",
+ __func__);
+ return -1;
+ }
+
+ return 0;
+}