[U-Boot] [PATCH 0/5] arm: tegra: apalis/colibri_t30: video: add display driver

On popular request this series integrates the display driver from T20 to work on T30 as well and enables it for our Apalis/Colibri T30 computer/system on modules. Enjoy.
Marcel Ziswiler (5): arm: tegra20: video: rename display header ifdef gating arm: tegra20: video: ifdef gate hard-coded ugly Tegra20 pin muxing arm: tegra30: video: integrate display driver for t30 colibri_t30: enable display driver apalis_t30: enable display driver
arch/arm/dts/tegra30-apalis.dts | 29 ++ arch/arm/dts/tegra30-colibri.dts | 29 ++ arch/arm/dts/tegra30.dtsi | 104 ++++++ arch/arm/include/asm/arch-tegra20/display.h | 6 +- arch/arm/include/asm/arch-tegra30/display.h | 13 + arch/arm/include/asm/arch-tegra30/pwm.h | 14 + arch/arm/mach-tegra/tegra30/Makefile | 6 +- arch/arm/mach-tegra/tegra30/display.c | 394 +++++++++++++++++++++ .../toradex/apalis_t30/pinmux-config-apalis_t30.h | 9 +- .../colibri_t30/pinmux-config-colibri_t30.h | 8 +- drivers/video/tegra.c | 2 + include/configs/apalis_t30.h | 10 + include/configs/colibri_t30.h | 10 + include/configs/tegra30-common.h | 3 + 14 files changed, 625 insertions(+), 12 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra30/display.h create mode 100644 arch/arm/include/asm/arch-tegra30/pwm.h create mode 100644 arch/arm/mach-tegra/tegra30/display.c

As a preparatory step make sure the Tegra20 display header file is properly ifdef gated with its name.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com ---
arch/arm/include/asm/arch-tegra20/display.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm/include/asm/arch-tegra20/display.h b/arch/arm/include/asm/arch-tegra20/display.h index 018c9f9..0089536 100644 --- a/arch/arm/include/asm/arch-tegra20/display.h +++ b/arch/arm/include/asm/arch-tegra20/display.h @@ -5,8 +5,8 @@ * SPDX-License-Identifier: GPL-2.0+ */
-#ifndef __ASM_ARCH_TEGRA_DISPLAY_H -#define __ASM_ARCH_TEGRA_DISPLAY_H +#ifndef __ASM_ARCH_TEGRA20_DISPLAY_H +#define __ASM_ARCH_TEGRA20_DISPLAY_H
#include <asm/arch-tegra/dc.h> #include <fdtdec.h> @@ -134,4 +134,4 @@ int tegra_lcd_check_next_stage(const void *blob, int wait); */ void tegra_lcd_early_init(const void *blob);
-#endif /*__ASM_ARCH_TEGRA_DISPLAY_H*/ +#endif /*__ASM_ARCH_TEGRA20_DISPLAY_H*/

On 20 August 2015 at 05:29, Marcel Ziswiler marcel.ziswiler@toradex.com wrote:
As a preparatory step make sure the Tegra20 display header file is properly ifdef gated with its name.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com
arch/arm/include/asm/arch-tegra20/display.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

As a preparatory step make sure the display driver is buildable for Tegra30 as well by ifdef gating any hard-coded ugly Tegra20 pin muxing stuff.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com ---
drivers/video/tegra.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index 33c6103..3946e6e 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -215,8 +215,10 @@ static int handle_stage(const void *blob) break; case STAGE_PWM: /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ +#ifdef CONFIG_MACH_T20 pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); pinmux_tristate_disable(PMUX_PINGRP_GPU); +#endif
pwm_enable(config.pwm_channel, 32768, 0xdf, 1); break;

On 08/20/2015 05:29 AM, Marcel Ziswiler wrote:
As a preparatory step make sure the display driver is buildable for Tegra30 as well by ifdef gating any hard-coded ugly Tegra20 pin muxing stuff.
diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c
@@ -215,8 +215,10 @@ static int handle_stage(const void *blob) break; case STAGE_PWM: /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ +#ifdef CONFIG_MACH_T20
That option doesn't seem to exist, except in arch/arm/include/asm/mach-types.h where I think it's the name of an unrelated board. Do you mean CONFIG_TEGRA20, as defined by arch/arm/mach-tegra/tegra20/Kconfig?

On popular request make the display driver from T20 work on T30 as well. Turned out to be quite straight forward. However a few notes about some things encountered during porting: Of course the T30 device tree was completely missing host1x as well as PWM support but it turns out this can simply be copied from T20 supplementing some tegra30- compatible nodes here and there while comparing it with the Linux device tree includes.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com --- The older Chromium U-Boot 2011.06 which to my knowledge was the only prior attempt at enabling a display driver for T30 for whatever reason got some clocking stuff mixed up. Turns out at least for a single display controller T20 and T30 can be clocked quite similar.
arch/arm/dts/tegra30.dtsi | 104 ++++++++ arch/arm/include/asm/arch-tegra30/display.h | 13 + arch/arm/include/asm/arch-tegra30/pwm.h | 14 + arch/arm/mach-tegra/tegra30/Makefile | 6 +- arch/arm/mach-tegra/tegra30/display.c | 394 ++++++++++++++++++++++++++++ include/configs/tegra30-common.h | 3 + 6 files changed, 533 insertions(+), 1 deletion(-) create mode 100644 arch/arm/include/asm/arch-tegra30/display.h create mode 100644 arch/arm/include/asm/arch-tegra30/pwm.h create mode 100644 arch/arm/mach-tegra/tegra30/display.c
diff --git a/arch/arm/dts/tegra30.dtsi b/arch/arm/dts/tegra30.dtsi index 5ea7e34..7669b27 100644 --- a/arch/arm/dts/tegra30.dtsi +++ b/arch/arm/dts/tegra30.dtsi @@ -8,6 +8,104 @@ compatible = "nvidia,tegra30"; interrupt-parent = <&intc>;
+ host1x { + compatible = "nvidia,tegra30-host1x", "simple-bus"; + reg = <0x50000000 0x00024000>; + interrupts = <0 65 0x04 /* mpcore syncpt */ + 0 67 0x04>; /* mpcore general */ + status = "disabled"; + + #address-cells = <1>; + #size-cells = <1>; + + ranges = <0x54000000 0x54000000 0x04000000>; + + /* video-encoding/decoding */ + mpe { + reg = <0x54040000 0x00040000>; + interrupts = <0 68 0x04>; + status = "disabled"; + }; + + /* video input */ + vi { + reg = <0x54080000 0x00040000>; + interrupts = <0 69 0x04>; + status = "disabled"; + }; + + /* EPP */ + epp { + reg = <0x540c0000 0x00040000>; + interrupts = <0 70 0x04>; + status = "disabled"; + }; + + /* ISP */ + isp { + reg = <0x54100000 0x00040000>; + interrupts = <0 71 0x04>; + status = "disabled"; + }; + + /* 2D engine */ + gr2d { + reg = <0x54140000 0x00040000>; + interrupts = <0 72 0x04>; + status = "disabled"; + }; + + /* 3D engine */ + gr3d { + reg = <0x54180000 0x00040000>; + status = "disabled"; + }; + + /* display controllers */ + dc@54200000 { + compatible = "nvidia,tegra30-dc", "nvidia,tegra20-dc"; + reg = <0x54200000 0x00040000>; + interrupts = <0 73 0x04>; + status = "disabled"; + + rgb { + status = "disabled"; + }; + }; + + dc@54240000 { + compatible = "nvidia,tegra30-dc"; + reg = <0x54240000 0x00040000>; + interrupts = <0 74 0x04>; + status = "disabled"; + + rgb { + status = "disabled"; + }; + }; + + /* outputs */ + hdmi { + compatible = "nvidia,tegra30-hdmi"; + reg = <0x54280000 0x00040000>; + interrupts = <0 75 0x04>; + status = "disabled"; + }; + + tvo { + compatible = "nvidia,tegra30-tvo"; + reg = <0x542c0000 0x00040000>; + interrupts = <0 76 0x04>; + status = "disabled"; + }; + + dsi { + compatible = "nvidia,tegra30-dsi"; + reg = <0x54300000 0x00040000>; + status = "disabled"; + }; + }; + intc: interrupt-controller@50041000 { compatible = "arm,cortex-a9-gic"; reg = <0x50041000 0x1000 @@ -271,6 +369,12 @@ status = "disabled"; };
+ pwm: pwm@7000a000 { + compatible = "nvidia,tegra30-pwm", "nvidia,tegra20-pwm"; + reg = <0x7000a000 0x100>; + #pwm-cells = <2>; + }; + spi@7000d400 { compatible = "nvidia,tegra30-slink", "nvidia,tegra20-slink"; reg = <0x7000d400 0x200>; diff --git a/arch/arm/include/asm/arch-tegra30/display.h b/arch/arm/include/asm/arch-tegra30/display.h new file mode 100644 index 0000000..6a46541 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra30/display.h @@ -0,0 +1,13 @@ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_TEGRA30_DISPLAY_H +#define __ASM_ARCH_TEGRA30_DISPLAY_H + +#include <asm/arch-tegra20/display.h> + +#endif /* __ASM_ARCH_TEGRA30_DISPLAY_H */ diff --git a/arch/arm/include/asm/arch-tegra30/pwm.h b/arch/arm/include/asm/arch-tegra30/pwm.h new file mode 100644 index 0000000..98d24d0 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra30/pwm.h @@ -0,0 +1,14 @@ +/* + * Tegra pulse width frequency modulator definitions + * + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_ARCH_TEGRA30_PWM_H +#define __ASM_ARCH_TEGRA30_PWM_H + +#include <asm/arch-tegra/pwm.h> + +#endif /* __ASM_ARCH_TEGRA30_PWM_H */ diff --git a/arch/arm/mach-tegra/tegra30/Makefile b/arch/arm/mach-tegra/tegra30/Makefile index bc250de..ef1ceb1 100644 --- a/arch/arm/mach-tegra/tegra30/Makefile +++ b/arch/arm/mach-tegra/tegra30/Makefile @@ -14,6 +14,10 @@ # along with this program. If not, see http://www.gnu.org/licenses/. #
-obj-$(CONFIG_SPL_BUILD) += cpu.o +ifdef CONFIG_SPL_BUILD +obj-y += cpu.o +else +obj-$(CONFIG_VIDEO_TEGRA) += display.o +endif
obj-y += clock.o funcmux.o pinmux.o diff --git a/arch/arm/mach-tegra/tegra30/display.c b/arch/arm/mach-tegra/tegra30/display.c new file mode 100644 index 0000000..b7605ff --- /dev/null +++ b/arch/arm/mach-tegra/tegra30/display.c @@ -0,0 +1,394 @@ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/tegra.h> +#include <asm/arch/display.h> +#include <asm/arch-tegra/dc.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra/timer.h> + +static struct fdt_disp_config config; + +static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) +{ + unsigned h_dda, v_dda; + unsigned long val; + + val = readl(&dc->cmd.disp_win_header); + val |= WINDOW_A_SELECT; + writel(val, &dc->cmd.disp_win_header); + + writel(win->fmt, &dc->win.color_depth); + + clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, + BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); + + val = win->out_x << H_POSITION_SHIFT; + val |= win->out_y << V_POSITION_SHIFT; + writel(val, &dc->win.pos); + + val = win->out_w << H_SIZE_SHIFT; + val |= win->out_h << V_SIZE_SHIFT; + writel(val, &dc->win.size); + + val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; + val |= win->h << V_PRESCALED_SIZE_SHIFT; + writel(val, &dc->win.prescaled_size); + + writel(0, &dc->win.h_initial_dda); + writel(0, &dc->win.v_initial_dda); + + h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); + v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); + + val = h_dda << H_DDA_INC_SHIFT; + val |= v_dda << V_DDA_INC_SHIFT; + writel(val, &dc->win.dda_increment); + + writel(win->stride, &dc->win.line_stride); + writel(0, &dc->win.buf_stride); + + val = WIN_ENABLE; + if (win->bpp < 24) + val |= COLOR_EXPAND; + writel(val, &dc->win.win_opt); + + writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); + writel(win->x, &dc->winbuf.addr_h_offset); + writel(win->y, &dc->winbuf.addr_v_offset); + + writel(0xff00, &dc->win.blend_nokey); + writel(0xff00, &dc->win.blend_1win); + + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + val |= GENERAL_UPDATE | WIN_A_UPDATE; + writel(val, &dc->cmd.state_ctrl); +} + +static void write_pair(struct fdt_disp_config *config, int item, u32 *reg) +{ + writel(config->horiz_timing[item] | + (config->vert_timing[item] << 16), reg); +} + +static int update_display_mode(struct dc_disp_reg *disp, + struct fdt_disp_config *config) +{ + unsigned long val; + unsigned long rate; + unsigned long div; + + writel(0x0, &disp->disp_timing_opt); + write_pair(config, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); + write_pair(config, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); + write_pair(config, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); + write_pair(config, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); + + writel(config->width | (config->height << 16), &disp->disp_active); + + val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; + val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; + writel(val, &disp->data_enable_opt); + + val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; + val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; + val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; + writel(val, &disp->disp_interface_ctrl); + + /* + * The pixel clock divider is in 7.1 format (where the bottom bit + * represents 0.5). Here we calculate the divider needed to get from + * the display clock (typically 600MHz) to the pixel clock. We round + * up or down as requried. + */ + rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL); + div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2; + debug("Display clock %lu, divider %lu\n", rate, div); + + writel(0x00010001, &disp->shift_clk_opt); + + val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; + val |= div << SHIFT_CLK_DIVIDER_SHIFT; + writel(val, &disp->disp_clk_ctrl); + + return 0; +} + +/* Start up the display and turn on power to PWMs */ +static void basic_init(struct dc_cmd_reg *cmd) +{ + u32 val; + + writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); + writel(0x0000011a, &cmd->cont_syncpt_vsync); + writel(0x00000000, &cmd->int_type); + writel(0x00000000, &cmd->int_polarity); + writel(0x00000000, &cmd->int_mask); + writel(0x00000000, &cmd->int_enb); + + val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; + val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; + val |= PM1_ENABLE; + writel(val, &cmd->disp_pow_ctrl); + + val = readl(&cmd->disp_cmd); + val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; + writel(val, &cmd->disp_cmd); +} + +static void basic_init_timer(struct dc_disp_reg *disp) +{ + writel(0x00000020, &disp->mem_high_pri); + writel(0x00000001, &disp->mem_high_pri_timer); +} + +static const u32 rgb_enb_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x01000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_data_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00210222, + 0x00002200, + 0x00020000, +}; + +static void rgb_enable(struct dc_com_reg *com) +{ + int i; + + for (i = 0; i < PIN_REG_COUNT; i++) { + writel(rgb_enb_tab[i], &com->pin_output_enb[i]); + writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); + writel(rgb_data_tab[i], &com->pin_output_data[i]); + } + + for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) + writel(rgb_sel_tab[i], &com->pin_output_sel[i]); +} + +static int setup_window(struct disp_ctl_win *win, + struct fdt_disp_config *config) +{ + win->x = 0; + win->y = 0; + win->w = config->width; + win->h = config->height; + win->out_x = 0; + win->out_y = 0; + win->out_w = config->width; + win->out_h = config->height; + win->phys_addr = config->frame_buffer; + win->stride = config->width * (1 << config->log2_bpp) / 8; + debug("%s: depth = %d\n", __func__, config->log2_bpp); + switch (config->log2_bpp) { + case 5: + case 24: + win->fmt = COLOR_DEPTH_R8G8B8A8; + win->bpp = 32; + break; + case 4: + win->fmt = COLOR_DEPTH_B5G6R5; + win->bpp = 16; + break; + + default: + debug("Unsupported LCD bit depth"); + return -1; + } + + return 0; +} + +struct fdt_disp_config *tegra_display_get_config(void) +{ + return config.valid ? &config : NULL; +} + +static void debug_timing(const char *name, unsigned int timing[]) +{ +#ifdef DEBUG + int i; + + debug("%s timing: ", name); + for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) + debug("%d ", timing[i]); + debug("\n"); +#endif +} + +/** + * Decode panel information from the fdt, according to a standard binding + * + * @param blob fdt blob + * @param node offset of fdt node to read from + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_decode_panel(const void *blob, int node, + struct fdt_disp_config *config) +{ + int front, back, ref; + + config->width = fdtdec_get_int(blob, node, "xres", -1); + config->height = fdtdec_get_int(blob, node, "yres", -1); + config->pixel_clock = fdtdec_get_int(blob, node, "clock", 0); + if (!config->pixel_clock || config->width == -1 || + config->height == -1) { + debug("%s: Pixel parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + back = fdtdec_get_int(blob, node, "left-margin", -1); + front = fdtdec_get_int(blob, node, "right-margin", -1); + ref = fdtdec_get_int(blob, node, "hsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Horizontal parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + /* Use a ref-to-sync of 1 always, and take this from the front porch */ + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("horiz", config->horiz_timing); + + back = fdtdec_get_int(blob, node, "upper-margin", -1); + front = fdtdec_get_int(blob, node, "lower-margin", -1); + ref = fdtdec_get_int(blob, node, "vsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Vertical parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("vert", config->vert_timing); + + return 0; +} + +/** + * Decode the display controller information from the fdt. + * + * @param blob fdt blob + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_display_decode_config(const void *blob, + struct fdt_disp_config *config) +{ + int node, rgb; + int bpp, bit; + + /* TODO: Support multiple controllers */ + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_DC); + if (node < 0) { + debug("%s: Cannot find display controller node in fdt\n", + __func__); + return node; + } + config->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg"); + if (!config->disp) { + debug("%s: No display controller address\n", __func__); + return -1; + } + + rgb = fdt_subnode_offset(blob, node, "rgb"); + + config->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); + if (config->panel_node < 0) { + debug("%s: Cannot find panel information\n", __func__); + return -1; + } + + if (tegra_decode_panel(blob, config->panel_node, config)) { + debug("%s: Failed to decode panel information\n", __func__); + return -1; + } + + bpp = fdtdec_get_int(blob, config->panel_node, "nvidia,bits-per-pixel", + -1); + bit = ffs(bpp) - 1; + if (bpp == (1 << bit)) + config->log2_bpp = bit; + else + config->log2_bpp = bpp; + if (bpp == -1) { + debug("%s: Pixel bpp parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + config->bpp = bpp; + + config->valid = 1; /* we have a valid configuration */ + + return 0; +} + +int tegra_display_probe(const void *blob, void *default_lcd_base) +{ + struct disp_ctl_win window; + struct dc_ctlr *dc; + + if (tegra_display_decode_config(blob, &config)) + return -1; + + config.frame_buffer = (u32)default_lcd_base; + + dc = (struct dc_ctlr *)config.disp; + + /* + * A header file for clock constants was NAKed upstream. + * TODO: Put this into the FDT and fdt_lcd struct when we have clock + * support there + */ + clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, + 144 * 1000000); + clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, + 600 * 1000000); + basic_init(&dc->cmd); + basic_init_timer(&dc->disp); + rgb_enable(&dc->com); + + if (config.pixel_clock) + update_display_mode(&dc->disp, &config); + + if (setup_window(&window, &config)) + return -1; + + update_window(dc, &window); + + return 0; +} diff --git a/include/configs/tegra30-common.h b/include/configs/tegra30-common.h index 3e8e3c1..0360578 100644 --- a/include/configs/tegra30-common.h +++ b/include/configs/tegra30-common.h @@ -70,6 +70,9 @@ #define CONFIG_SYS_SPL_MALLOC_START 0x80090000 #define CONFIG_SPL_STACK 0x800ffffc
+/* Align LCD to 1MB boundary */ +#define CONFIG_LCD_ALIGNMENT MMU_SECTION_SIZE + /* For USB EHCI controller */ #define CONFIG_EHCI_IS_TDI #define CONFIG_USB_EHCI_TXFIFO_THRESH 0x10

On 08/20/2015 05:29 AM, Marcel Ziswiler wrote:
On popular request make the display driver from T20 work on T30 as well. Turned out to be quite straight forward. However a few notes about some things encountered during porting: Of course the T30 device tree was completely missing host1x as well as PWM support but it turns out this can simply be copied from T20 supplementing some tegra30- compatible nodes here and there while comparing it with the Linux device tree includes.
Hopefully the process was to copy the Linux Tegra30 DT verbatim? That's far more likely to yield a correct DT than copying the Tegra20 DT to Tegra30 and then patching it until it works. If this DT doesn't exactly match the Linux kernel, this needs to be fixed.
diff --git a/arch/arm/mach-tegra/tegra30/Makefile b/arch/arm/mach-tegra/tegra30/Makefile
-obj-$(CONFIG_SPL_BUILD) += cpu.o +ifdef CONFIG_SPL_BUILD +obj-y += cpu.o
I don't think there's any need to edit the cpu.o line. While you can move it into the ifdef like that, I don't see a need.
diff --git a/arch/arm/mach-tegra/tegra30/display.c b/arch/arm/mach-tegra/tegra30/display.c
I didn't review this file in detail; I'll leave that to Thierry since he knows the display HW.
However, one question: Is this file a complete cut/paste of tegra20/display.c, or does it just replace some parts of it? Hopefully this patch doesn't simply duplicate the whole driver?

On popular request enable the display driver on Colibri T30. A few notes about some things encountered during porting: While analogue VGA (e.g. via the on-carrier RAMDAC) worked just fine from the beginning the EDT display flickered like crazy which turned out to be a pin muxing issue. Unfortunately the PWM pin muxing wasn't any good neither which made that display stay dark.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com ---
arch/arm/dts/tegra30-colibri.dts | 29 ++++++++++++++++++++++ .../colibri_t30/pinmux-config-colibri_t30.h | 8 +++--- include/configs/colibri_t30.h | 10 ++++++++ 3 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/arch/arm/dts/tegra30-colibri.dts b/arch/arm/dts/tegra30-colibri.dts index 487e1f6..ade1382 100644 --- a/arch/arm/dts/tegra30-colibri.dts +++ b/arch/arm/dts/tegra30-colibri.dts @@ -22,6 +22,17 @@ usb2 = "/usb@7d008000"; };
+ host1x { + status = "okay"; + dc@54200000 { + status = "okay"; + rgb { + status = "okay"; + nvidia,panel = <&lcd_panel>; + }; + }; + }; + memory { device_type = "memory"; reg = <0x80000000 0x40000000>; @@ -93,4 +104,22 @@ /* USBH_PEN */ nvidia,vbus-gpio = <&gpio TEGRA_GPIO(W, 2) GPIO_ACTIVE_LOW>; }; + + lcd_panel: panel { + clock = <25175000>; + xres = <640>; + yres = <480>; + left-margin = <48>; /* horizontal back porch */ + right-margin = <16>; /* horizontal front porch */ + hsync-len = <96>; + lower-margin = <11>; /* vertical front porch */ + upper-margin = <31>; /* vertical back porch */ + vsync-len = <2>; + hsync-active-high; + vsync-active-high; + nvidia,bits-per-pixel = <16>; + nvidia,pwm = <&pwm 0 0>; + nvidia,backlight-enable-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_HIGH>; + nvidia,panel-timings = <0 0 0 0>; + }; }; diff --git a/board/toradex/colibri_t30/pinmux-config-colibri_t30.h b/board/toradex/colibri_t30/pinmux-config-colibri_t30.h index 4e73c07..808eb15 100644 --- a/board/toradex/colibri_t30/pinmux-config-colibri_t30.h +++ b/board/toradex/colibri_t30/pinmux-config-colibri_t30.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, Stefan Agner + * Copyright (c) 2013-2015, Stefan Agner * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -80,7 +80,7 @@ static struct pmux_pingrp_config tegra3_pinmux_common[] = { DEFAULT_PINMUX(SDMMC3_DAT0_PB7, SDMMC3, UP, NORMAL, INPUT), DEFAULT_PINMUX(SDMMC3_DAT1_PB6, SDMMC3, UP, NORMAL, INPUT), DEFAULT_PINMUX(SDMMC3_DAT2_PB5, SDMMC3, UP, NORMAL, INPUT), - DEFAULT_PINMUX(SDMMC3_DAT3_PB4, SDMMC3, UP, NORMAL, INPUT), + DEFAULT_PINMUX(SDMMC3_DAT3_PB4, PWM0, NORMAL, NORMAL, OUTPUT), DEFAULT_PINMUX(SDMMC3_DAT6_PD3, RSVD1, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(SDMMC3_DAT7_PD4, RSVD1, NORMAL, NORMAL, INPUT),
@@ -176,7 +176,7 @@ static struct pmux_pingrp_config tegra3_pinmux_common[] = { DEFAULT_PINMUX(LCD_D22_PM6, DISPLAYA, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(LCD_D23_PM7, DISPLAYA, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(LCD_CS1_N_PW0, DISPLAYA, NORMAL, NORMAL, INPUT), - DEFAULT_PINMUX(LCD_M1_PW1, DISPLAYA, NORMAL, NORMAL, INPUT), + DEFAULT_PINMUX(LCD_M1_PW1, DISPLAYA, NORMAL, TRISTATE, OUTPUT), DEFAULT_PINMUX(LCD_DC1_PD2, DISPLAYA, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(CRT_HSYNC_PV6, CRT, NORMAL, NORMAL, OUTPUT), DEFAULT_PINMUX(CRT_VSYNC_PV7, CRT, NORMAL, NORMAL, OUTPUT), @@ -212,7 +212,7 @@ static struct pmux_pingrp_config tegra3_pinmux_common[] = { DEFAULT_PINMUX(CLK3_REQ_PEE1, DEV3, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(GMI_WP_N_PC7, GMI, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(GMI_CS2_N_PK3, RSVD1, UP, NORMAL, INPUT), /* EN_VDD_BL1 */ - DEFAULT_PINMUX(GMI_AD8_PH0, PWM0, NORMAL, NORMAL, OUTPUT), /* LCD1_BL_PWM */ + DEFAULT_PINMUX(GMI_AD8_PH0, RSVD2, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(GMI_AD10_PH2, NAND, NORMAL, NORMAL, OUTPUT), /* LCD1_BL_EN */ DEFAULT_PINMUX(GMI_A16_PJ7, UARTD, NORMAL, NORMAL, INPUT), DEFAULT_PINMUX(GMI_A17_PB0, UARTD, NORMAL, NORMAL, INPUT), diff --git a/include/configs/colibri_t30.h b/include/configs/colibri_t30.h index ef743b0..d6c9d58 100644 --- a/include/configs/colibri_t30.h +++ b/include/configs/colibri_t30.h @@ -57,6 +57,16 @@ #define CONFIG_TFTP_BLOCKSIZE 16384 #define CONFIG_TFTP_TSIZE
+/* LCD support */ +#define CONFIG_LCD +#define CONFIG_PWM_TEGRA +#define CONFIG_VIDEO_TEGRA +#define LCD_BPP LCD_COLOR16 +#define CONFIG_SYS_WHITE_ON_BLACK +#define CONFIG_CONSOLE_SCROLL_LINES 10 +#define CONFIG_CMD_BMP +#define CONFIG_LCD_LOGO + /* Miscellaneous commands */ #define CONFIG_FAT_WRITE

On 20 August 2015 at 05:29, Marcel Ziswiler marcel.ziswiler@toradex.com wrote:
On popular request enable the display driver on Colibri T30. A few notes about some things encountered during porting: While analogue VGA (e.g. via the on-carrier RAMDAC) worked just fine from the beginning the EDT display flickered like crazy which turned out to be a pin muxing issue. Unfortunately the PWM pin muxing wasn't any good neither which made that display stay dark.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com
arch/arm/dts/tegra30-colibri.dts | 29 ++++++++++++++++++++++ .../colibri_t30/pinmux-config-colibri_t30.h | 8 +++--- include/configs/colibri_t30.h | 10 ++++++++ 3 files changed, 43 insertions(+), 4 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

Enable the display driver on Apalis T30. Unfortunately the PWM pin muxing wasn't any good neither which made that display stay dark.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com ---
arch/arm/dts/tegra30-apalis.dts | 29 ++++++++++++++++++++++ .../toradex/apalis_t30/pinmux-config-apalis_t30.h | 9 ++++--- include/configs/apalis_t30.h | 10 ++++++++ 3 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/arch/arm/dts/tegra30-apalis.dts b/arch/arm/dts/tegra30-apalis.dts index 3e0545c..30e7880 100644 --- a/arch/arm/dts/tegra30-apalis.dts +++ b/arch/arm/dts/tegra30-apalis.dts @@ -27,6 +27,17 @@ usb2 = "/usb@7d008000"; };
+ host1x { + status = "okay"; + dc@54200000 { + status = "okay"; + rgb { + status = "okay"; + nvidia,panel = <&lcd_panel>; + }; + }; + }; + memory { device_type = "memory"; reg = <0x80000000 0x40000000>; @@ -287,6 +298,24 @@ nvidia,vbus-gpio = <&gpio TEGRA_GPIO(DD, 1) GPIO_ACTIVE_HIGH>; };
+ lcd_panel: panel { + clock = <25175000>; + xres = <640>; + yres = <480>; + left-margin = <48>; /* horizontal back porch */ + right-margin = <16>; /* horizontal front porch */ + hsync-len = <96>; + lower-margin = <11>; /* vertical front porch */ + upper-margin = <31>; /* vertical back porch */ + vsync-len = <2>; + hsync-active-high; + vsync-active-high; + nvidia,bits-per-pixel = <16>; + nvidia,pwm = <&pwm 0 0>; + nvidia,backlight-enable-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_HIGH>; + nvidia,panel-timings = <0 0 0 0>; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/board/toradex/apalis_t30/pinmux-config-apalis_t30.h b/board/toradex/apalis_t30/pinmux-config-apalis_t30.h index c988d39..041697e 100644 --- a/board/toradex/apalis_t30/pinmux-config-apalis_t30.h +++ b/board/toradex/apalis_t30/pinmux-config-apalis_t30.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Marcel Ziswiler + * Copyright (c) 2014-2015, Marcel Ziswiler * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -204,7 +204,8 @@ static struct pmux_pingrp_config tegra3_pinmux_common[] = { DEFAULT_PINMUX(UART2_CTS_N_PJ5, UARTB, DOWN, TRISTATE, OUTPUT), /* NC */ DEFAULT_PINMUX(UART3_TXD_PW6, UARTC, NORMAL, NORMAL, OUTPUT), DEFAULT_PINMUX(UART3_RXD_PW7, UARTC, NORMAL, NORMAL, INPUT), - DEFAULT_PINMUX(UART3_CTS_N_PA1, UARTC, NORMAL, NORMAL, INPUT), + /* disable BKL1_PWM_EN# (e.g. PMIC PWM backlight enable) for now */ + DEFAULT_PINMUX(UART3_CTS_N_PA1, RSVD1, UP, NORMAL, OUTPUT), DEFAULT_PINMUX(UART3_RTS_N_PC0, PWM0, NORMAL, NORMAL, OUTPUT), DEFAULT_PINMUX(PU0, RSVD1, DOWN, TRISTATE, OUTPUT), DEFAULT_PINMUX(PU1, RSVD1, DOWN, TRISTATE, OUTPUT), @@ -221,7 +222,7 @@ static struct pmux_pingrp_config tegra3_pinmux_common[] = { DEFAULT_PINMUX(CLK3_REQ_PEE1, DEV3, DOWN, TRISTATE, OUTPUT), /* NC */ DEFAULT_PINMUX(GMI_WP_N_PC7, GMI, DOWN, TRISTATE, OUTPUT), /* NC */ DEFAULT_PINMUX(GMI_CS2_N_PK3, RSVD1, DOWN, TRISTATE, OUTPUT), /* NC */ - DEFAULT_PINMUX(GMI_AD8_PH0, PWM0, DOWN, TRISTATE, OUTPUT), /* NC */ + DEFAULT_PINMUX(GMI_AD8_PH0, RSVD1, DOWN, TRISTATE, OUTPUT), /* NC */ DEFAULT_PINMUX(GMI_AD10_PH2, NAND, DOWN, TRISTATE, OUTPUT), /* NC */ DEFAULT_PINMUX(GMI_A16_PJ7, UARTD, DOWN, TRISTATE, OUTPUT), /* NC */ DEFAULT_PINMUX(GMI_A17_PB0, UARTD, DOWN, TRISTATE, OUTPUT), /* NC */ @@ -331,7 +332,7 @@ static struct pmux_pingrp_config unused_pins_lowpower[] = { DEFAULT_PINMUX(GMI_AD5_PG5, NAND, DOWN, TRISTATE, OUTPUT), DEFAULT_PINMUX(GMI_AD6_PG6, NAND, DOWN, TRISTATE, OUTPUT), DEFAULT_PINMUX(GMI_AD7_PG7, NAND, DOWN, TRISTATE, OUTPUT), - DEFAULT_PINMUX(GMI_AD9_PH1, PWM1, DOWN, TRISTATE, OUTPUT), + DEFAULT_PINMUX(GMI_AD9_PH1, RSVD1, DOWN, TRISTATE, OUTPUT), DEFAULT_PINMUX(GMI_AD11_PH3, NAND, DOWN, TRISTATE, OUTPUT), DEFAULT_PINMUX(GMI_AD13_PH5, NAND, DOWN, TRISTATE, OUTPUT), DEFAULT_PINMUX(GMI_WR_N_PI0, NAND, DOWN, TRISTATE, OUTPUT), diff --git a/include/configs/apalis_t30.h b/include/configs/apalis_t30.h index 6c0ae22..b2a695e 100644 --- a/include/configs/apalis_t30.h +++ b/include/configs/apalis_t30.h @@ -64,6 +64,16 @@ #define CONFIG_TFTP_BLOCKSIZE 16384 #define CONFIG_TFTP_TSIZE
+/* LCD support */ +#define CONFIG_LCD +#define CONFIG_PWM_TEGRA +#define CONFIG_VIDEO_TEGRA +#define LCD_BPP LCD_COLOR16 +#define CONFIG_SYS_WHITE_ON_BLACK +#define CONFIG_CONSOLE_SCROLL_LINES 10 +#define CONFIG_CMD_BMP +#define CONFIG_LCD_LOGO + /* Miscellaneous commands */ #define CONFIG_FAT_WRITE

On 20 August 2015 at 05:29, Marcel Ziswiler marcel.ziswiler@toradex.com wrote:
Enable the display driver on Apalis T30. Unfortunately the PWM pin muxing wasn't any good neither which made that display stay dark.
Signed-off-by: Marcel Ziswiler marcel.ziswiler@toradex.com
arch/arm/dts/tegra30-apalis.dts | 29 ++++++++++++++++++++++ .../toradex/apalis_t30/pinmux-config-apalis_t30.h | 9 ++++--- include/configs/apalis_t30.h | 10 ++++++++ 3 files changed, 44 insertions(+), 4 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org
participants (3)
-
Marcel Ziswiler
-
Simon Glass
-
Stephen Warren