From d4dff621756486d9cde20a1a27f656dd37e3494f Mon Sep 17 00:00:00 2001 From: Jimmy Zhang Date: Tue, 9 Dec 2014 15:20:20 -0800 Subject: 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 Original-Commit-Id: a7ab7225e3419a0fd93894dbb9a959390f29945b Original-Change-Id: Id14cbd89457cb91c23526927a432f4eb7cc6291b Original-Signed-off-by: Jimmy Zhang Original-Reviewed-on: https://chromium-review.googlesource.com/234270 Original-Reviewed-by: Aaron Durbin Reviewed-on: http://review.coreboot.org/9583 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi --- 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(-) create mode 100644 src/soc/nvidia/tegra132/dc.c delete mode 100644 src/soc/nvidia/tegra132/display.c create mode 100644 src/soc/nvidia/tegra132/dsi.c delete mode 100644 src/soc/nvidia/tegra132/tegra_dsi.c diff --git a/src/mainboard/google/rush_ryu/devicetree.cb b/src/mainboard/google/rush_ryu/devicetree.cb index b5c998192f..f621e006d8 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 71c3881626..a27fbc1fe1 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 #include +#include /* 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 fc22b2a69b..9d4c33ddf3 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 2468870569..fbbef500d4 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 #include +#include 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 0000000000..e5ab23ad89 --- /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 +#include +#include +#include +#include +#include +#include "chip.h" +#include + +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 d616b7356f..0000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "chip.h" -#include - -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 0000000000..cfeb7fdebc --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "chip.h" +#include +#include +#include +#include +#include +#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 3378a4f2d2..b0f2573246 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 f1e7c9f1c6..0000000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "chip.h" -#include -#include -#include -#include -#include -#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; -} -- cgit v1.2.3