[coreboot-gerrit] Patch set updated for coreboot: 6e2431d ryu: display: Split dc functions from dsi display code
Patrick Georgi (pgeorgi@google.com)
gerrit at coreboot.org
Mon Apr 13 13:56:34 CEST 2015
Patrick Georgi (pgeorgi at google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9583
-gerrit
commit 6e2431dc8a60a71b4916ac8829e981424ea572a6
Author: Jimmy Zhang <jimmzhang at nvidia.com>
Date: Tue Dec 9 15:20:20 2014 -0800
ryu: display: Split dc functions from dsi display code
dc supporting functions can be used for other than dsi display
interfaces. This change is preparing for supporting sor display
interface.
BUG=chrome-os-partner:34336
BRANCH=none
TEST=build ryu and test dev/rec mode, also build rush ok
Change-Id: I8a310e188fae70d7726c4360894b392c4546e105
Signed-off-by: Stefan Reinauer <reinauer at chromium.org>
Original-Commit-Id: a7ab7225e3419a0fd93894dbb9a959390f29945b
Original-Change-Id: Id14cbd89457cb91c23526927a432f4eb7cc6291b
Original-Signed-off-by: Jimmy Zhang <jimmzhang at nvidia.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/234270
Original-Reviewed-by: Aaron Durbin <adurbin at chromium.org>
---
src/mainboard/google/rush_ryu/devicetree.cb | 2 +
src/soc/nvidia/tegra/dc.h | 4 +
src/soc/nvidia/tegra132/Makefile.inc | 4 +-
src/soc/nvidia/tegra132/chip.h | 2 +
src/soc/nvidia/tegra132/dc.c | 223 ++++++
src/soc/nvidia/tegra132/display.c | 329 ---------
src/soc/nvidia/tegra132/dsi.c | 970 ++++++++++++++++++++++++++
src/soc/nvidia/tegra132/include/soc/display.h | 12 +-
src/soc/nvidia/tegra132/tegra_dsi.c | 874 -----------------------
9 files changed, 1211 insertions(+), 1209 deletions(-)
diff --git a/src/mainboard/google/rush_ryu/devicetree.cb b/src/mainboard/google/rush_ryu/devicetree.cb
index b5c9981..f621e00 100644
--- a/src/mainboard/google/rush_ryu/devicetree.cb
+++ b/src/mainboard/google/rush_ryu/devicetree.cb
@@ -50,4 +50,6 @@ chip soc/nvidia/tegra132
# kernel driver
register "pixel_clock" = "301620000"
+
+ register "win_opt" = "DSI_ENABLE"
end
diff --git a/src/soc/nvidia/tegra/dc.h b/src/soc/nvidia/tegra/dc.h
index 71c3881..a27fbc1 100644
--- a/src/soc/nvidia/tegra/dc.h
+++ b/src/soc/nvidia/tegra/dc.h
@@ -24,7 +24,9 @@
#ifndef __SOC_NVIDIA_TEGRA_DC_H
#define __SOC_NVIDIA_TEGRA_DC_H
+#include <device/device.h>
#include <stddef.h>
+#include <types.h>
/* Register definitions for the Tegra display controller */
@@ -502,7 +504,9 @@ struct tegra_dc {
unsigned long READL(void * p);
void WRITEL(unsigned long value, void * p);
+#ifndef __PRE_RAM__
void display_startup(device_t dev);
+#endif
void dp_init(void * _config);
void dp_enable(void * _dp);
unsigned int fb_base_mb(void);
diff --git a/src/soc/nvidia/tegra132/Makefile.inc b/src/soc/nvidia/tegra132/Makefile.inc
index fc22b2a..9d4c33d 100644
--- a/src/soc/nvidia/tegra132/Makefile.inc
+++ b/src/soc/nvidia/tegra132/Makefile.inc
@@ -66,8 +66,8 @@ ramstage-y += cbmem.c
ramstage-y += cpu.c
ramstage-y += cpu_lib.S
ramstage-y += clock.c
-ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += display.c
-ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += tegra_dsi.c
+ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += dc.c
+ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += dsi.c
ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += mipi_dsi.c
ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += mipi.c
ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += mipi-phy.c
diff --git a/src/soc/nvidia/tegra132/chip.h b/src/soc/nvidia/tegra132/chip.h
index 2468870..fbbef50 100644
--- a/src/soc/nvidia/tegra132/chip.h
+++ b/src/soc/nvidia/tegra132/chip.h
@@ -21,6 +21,7 @@
#define __SOC_NVIDIA_TEGRA132_CHIP_H__
#include <soc/addressmap.h>
#include <stdint.h>
+#include <soc/nvidia/tegra/dc.h>
struct soc_nvidia_tegra132_config {
/* Address to monitor if spintable employed. */
@@ -68,6 +69,7 @@ struct soc_nvidia_tegra132_config {
int refresh; /* display refresh rate */
int pixel_clock; /* dc pixel clock source rate */
+ int win_opt;
};
#endif /* __SOC_NVIDIA_TEGRA132_CHIP_H__ */
diff --git a/src/soc/nvidia/tegra132/dc.c b/src/soc/nvidia/tegra132/dc.c
new file mode 100644
index 0000000..e5ab23a
--- /dev/null
+++ b/src/soc/nvidia/tegra132/dc.c
@@ -0,0 +1,223 @@
+/*
+ * 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 <stdlib.h>
+#include <device/device.h>
+#include <soc/nvidia/tegra/dc.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,
+ "Panel Mode: %dx%d@%d.%03uHz pclk=%d\n",
+ config->xres, config->yres,
+ refresh / 1000, refresh % 1000,
+ config->pixel_clock);
+ }
+}
+
+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 win opt */
+ WRITEL(config->win_opt, &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_configure_plld(), 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.
+ */
+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->display_yres << 16) |
+ (config->display_xres *
+ config->framebuffer_bits_per_pixel / 8)),
+ &disp_ctrl->win.prescaled_size);
+
+ val = ALIGN_UP((config->display_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(((DDA_INC(config->display_yres, config->yres) << 16) |
+ DDA_INC(config->display_xres, config->xres)),
+ &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);
+}
+
+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;
+}
diff --git a/src/soc/nvidia/tegra132/display.c b/src/soc/nvidia/tegra132/display.c
deleted file mode 100644
index d616b73..0000000
--- a/src/soc/nvidia/tegra132/display.c
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * 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 <edid.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,
- "Panel 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_configure_plld(), 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->display_yres << 16) |
- (config->display_xres *
- config->framebuffer_bits_per_pixel / 8)),
- &disp_ctrl->win.prescaled_size);
-
- val = ALIGN_UP((config->display_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(((DDA_INC(config->display_yres, config->yres) << 16) |
- DDA_INC(config->display_xres, config->xres)),
- &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->display_xres *
- config->display_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_configure_plld(config->pixel_clock * 2);
- if (plld_rate == 0) {
- printk(BIOS_ERR, "dc: clock init failed\n");
- return;
- }
-
- /* 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__);
-
- /*
- * Pass panel information to cb tables
- */
- struct edid edid;
- /* Align bytes_per_line to 64 bytes as required by dc */
- edid.bytes_per_line = ALIGN_UP((config->display_xres *
- config->framebuffer_bits_per_pixel / 8), 64);
- edid.x_resolution = edid.bytes_per_line /
- (config->framebuffer_bits_per_pixel / 8);
- edid.y_resolution = config->display_yres;
- edid.framebuffer_bits_per_pixel = config->framebuffer_bits_per_pixel;
-
- printk(BIOS_INFO, "%s: bytes_per_line: %d, bits_per_pixel: %d\n "
- " x_res x y_res: %d x %d, size: %d\n",
- __func__, edid.bytes_per_line,
- edid.framebuffer_bits_per_pixel,
- edid.x_resolution, edid.y_resolution,
- (edid.bytes_per_line * edid.y_resolution));
-
- set_vbe_mode_info_valid(&edid, 0);
-
- /*
- * After this point, it is payload's responsibility to allocate
- * framebuffer and sets the base address to dc's
- * WINBUF_START_ADDR register and enables window by setting dc's
- * DISP_DISP_WIN_OPTIONS register.
- */
-}
-
diff --git a/src/soc/nvidia/tegra132/dsi.c b/src/soc/nvidia/tegra132/dsi.c
new file mode 100644
index 0000000..cfeb7fd
--- /dev/null
+++ b/src/soc/nvidia/tegra132/dsi.c
@@ -0,0 +1,970 @@
+/*
+ * 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 <edid.h>
+#include <soc/nvidia/tegra/types.h>
+#include <soc/nvidia/tegra/dc.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_configure_plld(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;
+}
+
+static 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;
+}
+
+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->display_xres *
+ config->display_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_configure_plld(config->pixel_clock * 2);
+ if (plld_rate == 0) {
+ printk(BIOS_ERR, "dc: clock init failed\n");
+ return;
+ }
+
+ /* 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__);
+
+ /*
+ * Pass panel information to cb tables
+ */
+ struct edid edid;
+ /* Align bytes_per_line to 64 bytes as required by dc */
+ edid.bytes_per_line = ALIGN_UP((config->display_xres *
+ config->framebuffer_bits_per_pixel / 8), 64);
+ edid.x_resolution = edid.bytes_per_line /
+ (config->framebuffer_bits_per_pixel / 8);
+ edid.y_resolution = config->display_yres;
+ edid.framebuffer_bits_per_pixel = config->framebuffer_bits_per_pixel;
+
+ printk(BIOS_INFO, "%s: bytes_per_line: %d, bits_per_pixel: %d\n "
+ " x_res x y_res: %d x %d, size: %d\n",
+ __func__, edid.bytes_per_line,
+ edid.framebuffer_bits_per_pixel,
+ edid.x_resolution, edid.y_resolution,
+ (edid.bytes_per_line * edid.y_resolution));
+
+ set_vbe_mode_info_valid(&edid, 0);
+
+ /*
+ * After this point, it is payload's responsibility to allocate
+ * framebuffer and sets the base address to dc's
+ * WINBUF_START_ADDR register and enables window by setting dc's
+ * DISP_DISP_WIN_OPTIONS register.
+ */
+}
diff --git a/src/soc/nvidia/tegra132/include/soc/display.h b/src/soc/nvidia/tegra132/include/soc/display.h
index 3378a4f..b0f2573 100644
--- a/src/soc/nvidia/tegra132/include/soc/display.h
+++ b/src/soc/nvidia/tegra132/include/soc/display.h
@@ -34,8 +34,12 @@
(mode->yres + mode->vfront_porch + \
mode->vsync_width + mode->vback_porch)
-struct soc_nvidia_tegra132_config; /* forward declaration */
-
-int dsi_enable(struct soc_nvidia_tegra132_config *config);
-
+/* forward declaration */
+struct soc_nvidia_tegra132_config;
+struct display_controller;
+
+int tegra_dc_init(struct display_controller *disp_ctrl);
+int update_display_mode(struct display_controller *disp_ctrl,
+ struct soc_nvidia_tegra132_config *config);
+void update_window(const struct soc_nvidia_tegra132_config *config);
#endif /* __SOC_NVIDIA_TEGRA132_INCLUDE_SOC_DISPLAY_H__ */
diff --git a/src/soc/nvidia/tegra132/tegra_dsi.c b/src/soc/nvidia/tegra132/tegra_dsi.c
deleted file mode 100644
index f1e7c9f..0000000
--- a/src/soc/nvidia/tegra132/tegra_dsi.c
+++ /dev/null
@@ -1,874 +0,0 @@
-/*
- * 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_configure_plld(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;
-}
More information about the coreboot-gerrit
mailing list