[PATCH v2 0/9] Add T114 video support

T114 is not that different from T30 and all T30 drivers will work on T114 as well with some adjustments.
Patches propose general improvements for existing Tegra DC and DSI drivers as well Tegra 114 video support (experimentl).
Commits pass buildman for tegra.
Svyatoslav Ryhel (9): video: tegra20: dc: diverge DC per-SOC video: tegra20: consolidate DC header video: tegra20: dc: pass DC id to internal devices video: tegra20: dc: add PLLD2 parent support video: tegra20: dc: add reset support video: tegra20: dc: add powergate video: tegra20: add MIPI calibration driver video: tegra20: dsi: add T114 support video: tegra20: dsi: add reset support
arch/arm/dts/tegra114-u-boot.dtsi | 13 ++ arch/arm/dts/tegra114.dtsi | 4 +- arch/arm/dts/tegra30-u-boot.dtsi | 4 + arch/arm/dts/tegra30.dtsi | 2 +- arch/arm/include/asm/arch-tegra/dc.h | 8 - arch/arm/include/asm/arch-tegra114/pwm.h | 13 ++ arch/arm/include/asm/arch-tegra20/display.h | 28 --- arch/arm/include/asm/arch-tegra30/display.h | 28 --- drivers/video/tegra20/Makefile | 2 +- drivers/video/tegra20/tegra-dc.c | 166 ++++++++++++---- drivers/video/tegra20/tegra-dc.h | 44 ++++ drivers/video/tegra20/tegra-dsi.c | 94 ++++++++- .../video/tegra20/tegra-dsi.h | 24 ++- drivers/video/tegra20/tegra-mipi.c | 188 ++++++++++++++++++ drivers/video/tegra20/tegra-pwm-backlight.c | 3 +- 15 files changed, 509 insertions(+), 112 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra114/pwm.h delete mode 100644 arch/arm/include/asm/arch-tegra20/display.h delete mode 100644 arch/arm/include/asm/arch-tegra30/display.h create mode 100644 drivers/video/tegra20/tegra-dc.h rename arch/arm/include/asm/arch-tegra30/dsi.h => drivers/video/tegra20/tegra-dsi.h (90%) create mode 100644 drivers/video/tegra20/tegra-mipi.c

Diverge DC driver setup to better fit each of supported generations of Tegra SOC.
Tested-by: Agneli poczt@protonmail.ch # Toshiba AC100 T20 Tested-by: Robert Eckelmann longnoserob@gmail.com # ASUS TF101 Tested-by: Andreas Westman Dorcsak hedmoo@yahoo.com # ASUS Grouper E1565 Tested-by: Ion Agorria ion@agorria.com # HTC One X Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- arch/arm/dts/tegra114-u-boot.dtsi | 13 +++ arch/arm/dts/tegra114.dtsi | 4 +- arch/arm/dts/tegra30-u-boot.dtsi | 4 + arch/arm/dts/tegra30.dtsi | 2 +- arch/arm/include/asm/arch-tegra114/display.h | 28 +++++ arch/arm/include/asm/arch-tegra114/pwm.h | 13 +++ drivers/video/tegra20/tegra-dc.c | 107 +++++++++++++------ 7 files changed, 133 insertions(+), 38 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra114/display.h create mode 100644 arch/arm/include/asm/arch-tegra114/pwm.h
diff --git a/arch/arm/dts/tegra114-u-boot.dtsi b/arch/arm/dts/tegra114-u-boot.dtsi index 7c11972552..6a02714a25 100644 --- a/arch/arm/dts/tegra114-u-boot.dtsi +++ b/arch/arm/dts/tegra114-u-boot.dtsi @@ -1,3 +1,16 @@ #include <config.h>
#include "tegra-u-boot.dtsi" + +/ { + host1x@50000000 { + bootph-all; + dc@54200000 { + bootph-all; + }; + + dc@54240000 { + bootph-all; + }; + }; +}; diff --git a/arch/arm/dts/tegra114.dtsi b/arch/arm/dts/tegra114.dtsi index 68ee7f3165..250d692f6b 100644 --- a/arch/arm/dts/tegra114.dtsi +++ b/arch/arm/dts/tegra114.dtsi @@ -42,7 +42,7 @@ };
dc@54200000 { - compatible = "nvidia,tegra114-dc", "nvidia,tegra20-dc"; + compatible = "nvidia,tegra114-dc"; reg = <0x54200000 0x00040000>; interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; clocks = <&tegra_car TEGRA114_CLK_DISP1>, @@ -61,7 +61,7 @@ };
dc@54240000 { - compatible = "nvidia,tegra114-dc", "nvidia,tegra20-dc"; + compatible = "nvidia,tegra114-dc"; reg = <0x54240000 0x00040000>; interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>; clocks = <&tegra_car TEGRA114_CLK_DISP2>, diff --git a/arch/arm/dts/tegra30-u-boot.dtsi b/arch/arm/dts/tegra30-u-boot.dtsi index 3038227dbe..6a02714a25 100644 --- a/arch/arm/dts/tegra30-u-boot.dtsi +++ b/arch/arm/dts/tegra30-u-boot.dtsi @@ -8,5 +8,9 @@ dc@54200000 { bootph-all; }; + + dc@54240000 { + bootph-all; + }; }; }; diff --git a/arch/arm/dts/tegra30.dtsi b/arch/arm/dts/tegra30.dtsi index f198bc0edb..1177e2ab1f 100644 --- a/arch/arm/dts/tegra30.dtsi +++ b/arch/arm/dts/tegra30.dtsi @@ -158,7 +158,7 @@ };
dc@54200000 { - compatible = "nvidia,tegra30-dc", "nvidia,tegra20-dc"; + compatible = "nvidia,tegra30-dc"; reg = <0x54200000 0x00040000>; interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; clocks = <&tegra_car TEGRA30_CLK_DISP1>, diff --git a/arch/arm/include/asm/arch-tegra114/display.h b/arch/arm/include/asm/arch-tegra114/display.h new file mode 100644 index 0000000000..9411525799 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra114/display.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + */ + +#ifndef __ASM_ARCH_TEGRA_DISPLAY_H +#define __ASM_ARCH_TEGRA_DISPLAY_H + +#include <asm/arch-tegra/dc.h> + +/* This holds information about a window which can be displayed */ +struct disp_ctl_win { + enum win_color_depth_id fmt; /* Color depth/format */ + unsigned int bpp; /* Bits per pixel */ + phys_addr_t phys_addr; /* Physical address in memory */ + unsigned int x; /* Horizontal address offset (bytes) */ + unsigned int y; /* Veritical address offset (bytes) */ + unsigned int w; /* Width of source window */ + unsigned int h; /* Height of source window */ + unsigned int stride; /* Number of bytes per line */ + unsigned int out_x; /* Left edge of output window (col) */ + unsigned int out_y; /* Top edge of output window (row) */ + unsigned int out_w; /* Width of output window in pixels */ + unsigned int out_h; /* Height of output window in pixels */ +}; + +#endif /*__ASM_ARCH_TEGRA_DISPLAY_H*/ diff --git a/arch/arm/include/asm/arch-tegra114/pwm.h b/arch/arm/include/asm/arch-tegra114/pwm.h new file mode 100644 index 0000000000..af39151803 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra114/pwm.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Tegra pulse width frequency modulator definitions + * + * Copyright (c) 2011 The Chromium OS Authors. + */ + +#ifndef __ASM_ARCH_TEGRA114_PWM_H +#define __ASM_ARCH_TEGRA114_PWM_H + +#include <asm/arch-tegra/pwm.h> + +#endif /* __ASM_ARCH_TEGRA114_PWM_H */ diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index f53ad46397..7605e77bc1 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -3,7 +3,6 @@ * Copyright (c) 2011 The Chromium OS Authors. */
-#include <common.h> #include <backlight.h> #include <dm.h> #include <fdtdec.h> @@ -23,10 +22,15 @@ #include <asm/arch/pinmux.h> #include <asm/arch/pwm.h> #include <asm/arch/display.h> -#include <asm/arch-tegra/timer.h>
DECLARE_GLOBAL_DATA_PTR;
+/* Holder of Tegra per-SOC DC differences */ +struct tegra_dc_soc_info { + bool has_timer; + bool has_rgb; +}; + /* Information about the display controller */ struct tegra_lcd_priv { int width; /* width in pixels */ @@ -35,6 +39,7 @@ struct tegra_lcd_priv { struct display_timing timing; struct udevice *panel; struct dc_ctlr *dc; /* Display controller regmap */ + const struct tegra_dc_soc_info *soc; fdt_addr_t frame_buffer; /* Address of frame buffer */ unsigned pixel_clock; /* Pixel clock in Hz */ int dc_clk[2]; /* Contains clk and its parent */ @@ -43,8 +48,8 @@ struct tegra_lcd_priv {
enum { /* Maximum LCD size we support */ - LCD_MAX_WIDTH = 1920, - LCD_MAX_HEIGHT = 1200, + LCD_MAX_WIDTH = 2560, + LCD_MAX_HEIGHT = 1600, LCD_MAX_LOG2_BPP = VIDEO_BPP16, };
@@ -110,9 +115,9 @@ static void update_window(struct tegra_lcd_priv *priv, writel(val, &dc->cmd.state_ctrl); }
-static int update_display_mode(struct dc_disp_reg *disp, - struct tegra_lcd_priv *priv) +static int update_display_mode(struct tegra_lcd_priv *priv) { + struct dc_disp_reg *disp = &priv->dc->disp; struct display_timing *dt = &priv->timing; unsigned long val; unsigned long rate; @@ -128,14 +133,16 @@ static int update_display_mode(struct dc_disp_reg *disp, &disp->front_porch); writel(dt->hactive.typ | (dt->vactive.typ << 16), &disp->disp_active);
- val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; - val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; - writel(val, &disp->data_enable_opt); + if (priv->soc->has_rgb) { + 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); + 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 @@ -147,7 +154,8 @@ static int update_display_mode(struct dc_disp_reg *disp, div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2; debug("Display clock %lu, divider %lu\n", rate, div);
- writel(0x00010001, &disp->shift_clk_opt); + if (priv->soc->has_rgb) + writel(0x00010001, &disp->shift_clk_opt);
val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; val |= div << SHIFT_CLK_DIVIDER_SHIFT; @@ -174,6 +182,7 @@ static void basic_init(struct dc_cmd_reg *cmd) writel(val, &cmd->disp_pow_ctrl);
val = readl(&cmd->disp_cmd); + val &= ~CTRL_MODE_MASK; val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; writel(val, &cmd->disp_cmd); } @@ -229,8 +238,8 @@ static void rgb_enable(struct dc_com_reg *com) writel(rgb_sel_tab[i], &com->pin_output_sel[i]); }
-static int setup_window(struct disp_ctl_win *win, - struct tegra_lcd_priv *priv) +static int setup_window(struct tegra_lcd_priv *priv, + struct disp_ctl_win *win) { if (priv->rotation) { win->x = priv->width * 2; @@ -274,12 +283,11 @@ static int setup_window(struct disp_ctl_win *win, * You should pass in the U-Boot address here, and check the contents of * struct tegra_lcd_priv to see what was actually chosen. * - * @param blob Device tree blob * @param priv Driver's private data * @param default_lcd_base Default address of LCD frame buffer * Return: 0 if ok, -1 on error (unsupported bits per pixel) */ -static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv, +static int tegra_display_probe(struct tegra_lcd_priv *priv, void *default_lcd_base) { struct disp_ctl_win window; @@ -288,7 +296,7 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv, priv->frame_buffer = (u32)default_lcd_base;
/* - * We halve the rate if DISP1 paret is PLLD, since actual parent + * We halve the rate if DISP1 parent is PLLD, since actual parent * is plld_out0 which is PLLD divided by 2. */ if (priv->dc_clk[1] == CLOCK_ID_DISPLAY) @@ -303,13 +311,17 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv, rate);
basic_init(&priv->dc->cmd); - basic_init_timer(&priv->dc->disp); - rgb_enable(&priv->dc->com); + + if (priv->soc->has_timer) + basic_init_timer(&priv->dc->disp); + + if (priv->soc->has_rgb) + rgb_enable(&priv->dc->com);
if (priv->pixel_clock) - update_display_mode(&priv->dc->disp, priv); + update_display_mode(priv);
- if (setup_window(&window, priv)) + if (setup_window(priv, &window)) return -1;
update_window(priv, &window); @@ -322,7 +334,6 @@ static int tegra_lcd_probe(struct udevice *dev) struct video_uc_plat *plat = dev_get_uclass_plat(dev); struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct tegra_lcd_priv *priv = dev_get_priv(dev); - const void *blob = gd->fdt_blob; int ret;
/* Initialize the Tegra display controller */ @@ -330,8 +341,8 @@ static int tegra_lcd_probe(struct udevice *dev) funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); #endif
- if (tegra_display_probe(blob, priv, (void *)plat->base)) { - printf("%s: Failed to probe display driver\n", __func__); + if (tegra_display_probe(priv, (void *)plat->base)) { + debug("%s: Failed to probe display driver\n", __func__); return -1; }
@@ -383,6 +394,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) return -EINVAL; }
+ priv->soc = (struct tegra_dc_soc_info *)dev_get_driver_data(dev); + ret = clock_decode_pair(dev, priv->dc_clk); if (ret < 0) { debug("%s: Cannot decode clocks for '%s' (ret = %d)\n", @@ -464,19 +477,43 @@ static int tegra_lcd_bind(struct udevice *dev) static const struct video_ops tegra_lcd_ops = { };
+static const struct tegra_dc_soc_info tegra20_dc_soc_info = { + .has_timer = true, + .has_rgb = true, +}; + +static const struct tegra_dc_soc_info tegra30_dc_soc_info = { + .has_timer = false, + .has_rgb = true, +}; + +static const struct tegra_dc_soc_info tegra114_dc_soc_info = { + .has_timer = false, + .has_rgb = false, +}; + static const struct udevice_id tegra_lcd_ids[] = { - { .compatible = "nvidia,tegra20-dc" }, - { .compatible = "nvidia,tegra30-dc" }, - { } + { + .compatible = "nvidia,tegra20-dc", + .data = (ulong)&tegra20_dc_soc_info + }, { + .compatible = "nvidia,tegra30-dc", + .data = (ulong)&tegra30_dc_soc_info + }, { + .compatible = "nvidia,tegra114-dc", + .data = (ulong)&tegra114_dc_soc_info + }, { + /* sentinel */ + } };
U_BOOT_DRIVER(tegra_lcd) = { - .name = "tegra_lcd", - .id = UCLASS_VIDEO, - .of_match = tegra_lcd_ids, - .ops = &tegra_lcd_ops, - .bind = tegra_lcd_bind, - .probe = tegra_lcd_probe, + .name = "tegra_lcd", + .id = UCLASS_VIDEO, + .of_match = tegra_lcd_ids, + .ops = &tegra_lcd_ops, + .bind = tegra_lcd_bind, + .probe = tegra_lcd_probe, .of_to_plat = tegra_lcd_of_to_plat, .priv_auto = sizeof(struct tegra_lcd_priv), };

Consolidate HD headers and place the result into video/tegra20 since it is used only by devices from this directory.
Tested-by: Agneli poczt@protonmail.ch # Toshiba AC100 T20 Tested-by: Robert Eckelmann longnoserob@gmail.com # ASUS TF101 Tested-by: Andreas Westman Dorcsak hedmoo@yahoo.com # ASUS Grouper E1565 Tested-by: Ion Agorria ion@agorria.com # HTC One X Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- arch/arm/include/asm/arch-tegra/dc.h | 8 ---- arch/arm/include/asm/arch-tegra114/display.h | 28 ------------- arch/arm/include/asm/arch-tegra20/display.h | 28 ------------- arch/arm/include/asm/arch-tegra30/display.h | 28 ------------- drivers/video/tegra20/tegra-dc.c | 3 +- drivers/video/tegra20/tegra-dc.h | 41 ++++++++++++++++++++ drivers/video/tegra20/tegra-dsi.c | 2 +- drivers/video/tegra20/tegra-pwm-backlight.c | 3 +- 8 files changed, 46 insertions(+), 95 deletions(-) delete mode 100644 arch/arm/include/asm/arch-tegra114/display.h delete mode 100644 arch/arm/include/asm/arch-tegra20/display.h delete mode 100644 arch/arm/include/asm/arch-tegra30/display.h create mode 100644 drivers/video/tegra20/tegra-dc.h
diff --git a/arch/arm/include/asm/arch-tegra/dc.h b/arch/arm/include/asm/arch-tegra/dc.h index 7613d84f22..6444af2993 100644 --- a/arch/arm/include/asm/arch-tegra/dc.h +++ b/arch/arm/include/asm/arch-tegra/dc.h @@ -569,12 +569,4 @@ enum { #define DC_N_WINDOWS 5 #define DC_REG_SAVE_SPACE (DC_N_WINDOWS + 5)
-#define TEGRA_DSI_A "dsi@54300000" -#define TEGRA_DSI_B "dsi@54400000" - -struct tegra_dc_plat { - struct udevice *dev; /* Display controller device */ - struct dc_ctlr *dc; /* Display controller regmap */ -}; - #endif /* __ASM_ARCH_TEGRA_DC_H */ diff --git a/arch/arm/include/asm/arch-tegra114/display.h b/arch/arm/include/asm/arch-tegra114/display.h deleted file mode 100644 index 9411525799..0000000000 --- a/arch/arm/include/asm/arch-tegra114/display.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2010 - * NVIDIA Corporation <www.nvidia.com> - */ - -#ifndef __ASM_ARCH_TEGRA_DISPLAY_H -#define __ASM_ARCH_TEGRA_DISPLAY_H - -#include <asm/arch-tegra/dc.h> - -/* This holds information about a window which can be displayed */ -struct disp_ctl_win { - enum win_color_depth_id fmt; /* Color depth/format */ - unsigned int bpp; /* Bits per pixel */ - phys_addr_t phys_addr; /* Physical address in memory */ - unsigned int x; /* Horizontal address offset (bytes) */ - unsigned int y; /* Veritical address offset (bytes) */ - unsigned int w; /* Width of source window */ - unsigned int h; /* Height of source window */ - unsigned int stride; /* Number of bytes per line */ - unsigned int out_x; /* Left edge of output window (col) */ - unsigned int out_y; /* Top edge of output window (row) */ - unsigned int out_w; /* Width of output window in pixels */ - unsigned int out_h; /* Height of output window in pixels */ -}; - -#endif /*__ASM_ARCH_TEGRA_DISPLAY_H*/ diff --git a/arch/arm/include/asm/arch-tegra20/display.h b/arch/arm/include/asm/arch-tegra20/display.h deleted file mode 100644 index e7b3cffd46..0000000000 --- a/arch/arm/include/asm/arch-tegra20/display.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2010 - * NVIDIA Corporation <www.nvidia.com> - */ - -#ifndef __ASM_ARCH_TEGRA_DISPLAY_H -#define __ASM_ARCH_TEGRA_DISPLAY_H - -#include <asm/arch-tegra/dc.h> - -/* This holds information about a window which can be displayed */ -struct disp_ctl_win { - enum win_color_depth_id fmt; /* Color depth/format */ - unsigned bpp; /* Bits per pixel */ - phys_addr_t phys_addr; /* Physical address in memory */ - unsigned x; /* Horizontal address offset (bytes) */ - unsigned y; /* Veritical address offset (bytes) */ - unsigned w; /* Width of source window */ - unsigned h; /* Height of source window */ - unsigned stride; /* Number of bytes per line */ - unsigned out_x; /* Left edge of output window (col) */ - unsigned out_y; /* Top edge of output window (row) */ - unsigned out_w; /* Width of output window in pixels */ - unsigned out_h; /* Height of output window in pixels */ -}; - -#endif /*__ASM_ARCH_TEGRA_DISPLAY_H*/ diff --git a/arch/arm/include/asm/arch-tegra30/display.h b/arch/arm/include/asm/arch-tegra30/display.h deleted file mode 100644 index 9411525799..0000000000 --- a/arch/arm/include/asm/arch-tegra30/display.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2010 - * NVIDIA Corporation <www.nvidia.com> - */ - -#ifndef __ASM_ARCH_TEGRA_DISPLAY_H -#define __ASM_ARCH_TEGRA_DISPLAY_H - -#include <asm/arch-tegra/dc.h> - -/* This holds information about a window which can be displayed */ -struct disp_ctl_win { - enum win_color_depth_id fmt; /* Color depth/format */ - unsigned int bpp; /* Bits per pixel */ - phys_addr_t phys_addr; /* Physical address in memory */ - unsigned int x; /* Horizontal address offset (bytes) */ - unsigned int y; /* Veritical address offset (bytes) */ - unsigned int w; /* Width of source window */ - unsigned int h; /* Height of source window */ - unsigned int stride; /* Number of bytes per line */ - unsigned int out_x; /* Left edge of output window (col) */ - unsigned int out_y; /* Top edge of output window (row) */ - unsigned int out_w; /* Width of output window in pixels */ - unsigned int out_h; /* Height of output window in pixels */ -}; - -#endif /*__ASM_ARCH_TEGRA_DISPLAY_H*/ diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index 7605e77bc1..56733c529a 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -21,7 +21,8 @@ #include <asm/arch/funcmux.h> #include <asm/arch/pinmux.h> #include <asm/arch/pwm.h> -#include <asm/arch/display.h> + +#include "tegra-dc.h"
DECLARE_GLOBAL_DATA_PTR;
diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra20/tegra-dc.h new file mode 100644 index 0000000000..5c05221038 --- /dev/null +++ b/drivers/video/tegra20/tegra-dc.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + */ + +#ifndef _TEGRA_DC_H +#define _TEGRA_DC_H + +#ifndef __ASSEMBLY__ +#include <linux/bitops.h> +#endif + +/* arch-tegra/dc exists only because T124 uses it */ +#include <asm/arch-tegra/dc.h> + +#define TEGRA_DSI_A "dsi@54300000" +#define TEGRA_DSI_B "dsi@54400000" + +struct tegra_dc_plat { + struct udevice *dev; /* Display controller device */ + struct dc_ctlr *dc; /* Display controller regmap */ +}; + +/* This holds information about a window which can be displayed */ +struct disp_ctl_win { + enum win_color_depth_id fmt; /* Color depth/format */ + unsigned int bpp; /* Bits per pixel */ + phys_addr_t phys_addr; /* Physical address in memory */ + unsigned int x; /* Horizontal address offset (bytes) */ + unsigned int y; /* Veritical address offset (bytes) */ + unsigned int w; /* Width of source window */ + unsigned int h; /* Height of source window */ + unsigned int stride; /* Number of bytes per line */ + unsigned int out_x; /* Left edge of output window (col) */ + unsigned int out_y; /* Top edge of output window (row) */ + unsigned int out_w; /* Width of output window in pixels */ + unsigned int out_h; /* Height of output window in pixels */ +}; + +#endif /* _TEGRA_DC_H */ diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index a48f9c85d0..72b91ed26b 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -20,9 +20,9 @@ #include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/clock.h> -#include <asm/arch/display.h> #include <asm/arch-tegra30/dsi.h>
+#include "tegra-dc.h" #include "mipi-phy.h"
struct tegra_dsi_priv { diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra20/tegra-pwm-backlight.c index bb677daa8a..5f93f57fe9 100644 --- a/drivers/video/tegra20/tegra-pwm-backlight.c +++ b/drivers/video/tegra20/tegra-pwm-backlight.c @@ -15,7 +15,8 @@
#include <asm/io.h> #include <asm/gpio.h> -#include <asm/arch/display.h> + +#include "tegra-dc.h"
#define TEGRA_DISPLAY_A_BASE 0x54200000 #define TEGRA_DISPLAY_B_BASE 0x54240000

Tegra SoC has 2 independent display controllers called DC_A and DC_B, they are handled differently by internal video devices like DSI and HDMI controllers so it is important for last to know which display controller is used to properly set up registers. To achieve this, a pipe field was added to pdata to pass display controller id to internal Tegra SoC devices.
Tested-by: Agneli poczt@protonmail.ch # Toshiba AC100 T20 Tested-by: Robert Eckelmann longnoserob@gmail.com # ASUS TF101 Tested-by: Andreas Westman Dorcsak hedmoo@yahoo.com # ASUS Grouper E1565 Tested-by: Ion Agorria ion@agorria.com # HTC One X Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/video/tegra20/tegra-dc.c | 6 ++++++ drivers/video/tegra20/tegra-dc.h | 3 +++ 2 files changed, 9 insertions(+)
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index 56733c529a..09917376cc 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -45,6 +45,7 @@ struct tegra_lcd_priv { unsigned pixel_clock; /* Pixel clock in Hz */ int dc_clk[2]; /* Contains clk and its parent */ bool rotation; /* 180 degree panel turn */ + bool pipe; /* DC controller: 0 for A, 1 for B */ };
enum { @@ -406,6 +407,9 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
priv->rotation = dev_read_bool(dev, "nvidia,180-rotation");
+ if (!strcmp(dev->name, TEGRA_DC_B)) + priv->pipe = 1; + rgb = fdt_subnode_offset(blob, node, "rgb"); if (rgb < 0) { debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n", @@ -431,12 +435,14 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) return ret; }
+ /* Fill the platform data for internal devices */ if (!strcmp(priv->panel->name, TEGRA_DSI_A) || !strcmp(priv->panel->name, TEGRA_DSI_B)) { struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel);
dc_plat->dev = dev; dc_plat->dc = priv->dc; + dc_plat->pipe = priv->pipe; }
ret = panel_get_display_timing(priv->panel, &priv->timing); diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra20/tegra-dc.h index 5c05221038..75fc0fa4de 100644 --- a/drivers/video/tegra20/tegra-dc.h +++ b/drivers/video/tegra20/tegra-dc.h @@ -14,12 +14,15 @@ /* arch-tegra/dc exists only because T124 uses it */ #include <asm/arch-tegra/dc.h>
+#define TEGRA_DC_A "dc@54200000" +#define TEGRA_DC_B "dc@54240000" #define TEGRA_DSI_A "dsi@54300000" #define TEGRA_DSI_B "dsi@54400000"
struct tegra_dc_plat { struct udevice *dev; /* Display controller device */ struct dc_ctlr *dc; /* Display controller regmap */ + bool pipe; /* DC number: 0 for A, 1 for B */ };
/* This holds information about a window which can be displayed */

T30+ SOC have second PLLD - PLLD2 which can be actively used by DC and act as main DISP1/2 clock parent.
Tested-by: Agneli poczt@protonmail.ch # Toshiba AC100 T20 Tested-by: Robert Eckelmann longnoserob@gmail.com # ASUS TF101 Tested-by: Andreas Westman Dorcsak hedmoo@yahoo.com # ASUS Grouper E1565 Tested-by: Ion Agorria ion@agorria.com # HTC One X Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/video/tegra20/tegra-dc.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index 09917376cc..f2b2dcd48a 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -304,6 +304,12 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, if (priv->dc_clk[1] == CLOCK_ID_DISPLAY) rate /= 2;
+#ifndef CONFIG_TEGRA20 + /* PLLD2 obeys same rules as PLLD but it is present only on T30+ */ + if (priv->dc_clk[1] == CLOCK_ID_DISPLAY2) + rate /= 2; +#endif + /* * HOST1X is init by default at 150MHz with PLLC as parent */

Implement reset use to discard any changes which could have been applied to DC before and can interfere with current configuration.
Tested-by: Agneli poczt@protonmail.ch # Toshiba AC100 T20 Tested-by: Robert Eckelmann longnoserob@gmail.com # ASUS TF101 Tested-by: Andreas Westman Dorcsak hedmoo@yahoo.com # ASUS Grouper E1565 Tested-by: Ion Agorria ion@agorria.com # HTC One X Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/video/tegra20/tegra-dc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index f2b2dcd48a..f6e20eeee2 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -10,7 +10,9 @@ #include <panel.h> #include <part.h> #include <pwm.h> +#include <reset.h> #include <video.h> +#include <linux/delay.h> #include <asm/cache.h> #include <asm/global_data.h> #include <asm/system.h> @@ -342,6 +344,7 @@ static int tegra_lcd_probe(struct udevice *dev) struct video_uc_plat *plat = dev_get_uclass_plat(dev); struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct tegra_lcd_priv *priv = dev_get_priv(dev); + struct reset_ctl reset_ctl; int ret;
/* Initialize the Tegra display controller */ @@ -349,6 +352,20 @@ static int tegra_lcd_probe(struct udevice *dev) funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); #endif
+ ret = reset_get_by_name(dev, "dc", &reset_ctl); + if (ret) { + log_err("reset_get_by_name() failed: %d\n", ret); + return ret; + } + + clock_disable(priv->dc_clk[0]); + + /* Reset everything set before */ + reset_assert(&reset_ctl); + mdelay(4); + reset_deassert(&reset_ctl); + mdelay(4); + if (tegra_display_probe(priv, (void *)plat->base)) { debug("%s: Failed to probe display driver\n", __func__); return -1;

Add powergate use on T114 to complete resetting of DC.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/video/tegra20/tegra-dc.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+)
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index f6e20eeee2..41b22a2c6e 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -22,6 +22,7 @@ #include <asm/arch/clock.h> #include <asm/arch/funcmux.h> #include <asm/arch/pinmux.h> +#include <asm/arch/powergate.h> #include <asm/arch/pwm.h>
#include "tegra-dc.h" @@ -32,6 +33,7 @@ DECLARE_GLOBAL_DATA_PTR; struct tegra_dc_soc_info { bool has_timer; bool has_rgb; + bool has_pgate; };
/* Information about the display controller */ @@ -360,6 +362,28 @@ static int tegra_lcd_probe(struct udevice *dev)
clock_disable(priv->dc_clk[0]);
+ if (priv->soc->has_pgate) { + uint powergate; + + if (priv->pipe) + powergate = TEGRA_POWERGATE_DISB; + else + powergate = TEGRA_POWERGATE_DIS; + + ret = tegra_powergate_power_off(powergate); + if (ret < 0) { + log_err("failed to power off DISP gate: %d", ret); + return ret; + } + + ret = tegra_powergate_sequence_power_up(powergate, + priv->dc_clk[0]); + if (ret < 0) { + log_err("failed to power up DISP gate: %d", ret); + return ret; + } + } + /* Reset everything set before */ reset_assert(&reset_ctl); mdelay(4); @@ -510,16 +534,19 @@ static const struct video_ops tegra_lcd_ops = { static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .has_timer = true, .has_rgb = true, + .has_pgate = false, };
static const struct tegra_dc_soc_info tegra30_dc_soc_info = { .has_timer = false, .has_rgb = true, + .has_pgate = false, };
static const struct tegra_dc_soc_info tegra114_dc_soc_info = { .has_timer = false, .has_rgb = false, + .has_pgate = true, };
static const struct udevice_id tegra_lcd_ids[] = {

Dedicated MIPI calibration driver is used on T114 and newer. Before T114 MIPI calibration registers were part of VI and CSI.
Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/video/tegra20/Makefile | 2 +- drivers/video/tegra20/tegra-mipi.c | 188 +++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 drivers/video/tegra20/tegra-mipi.c
diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile index f0b534c579..a75aea2a87 100644 --- a/drivers/video/tegra20/Makefile +++ b/drivers/video/tegra20/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o -obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o mipi-phy.o +obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o diff --git a/drivers/video/tegra20/tegra-mipi.c b/drivers/video/tegra20/tegra-mipi.c new file mode 100644 index 0000000000..2df3c1a994 --- /dev/null +++ b/drivers/video/tegra20/tegra-mipi.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (c) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <dm.h> +#include <clk.h> +#include <misc.h> +#include <linux/delay.h> +#include <linux/iopoll.h> + +#include <asm/io.h> + +/* MIPI control registers 0x00 ~ 0x60 */ +struct mipi_ctlr { + uint mipi_cal_ctrl; + uint mipi_cal_autocal_ctrl; + uint mipi_cal_status; + + uint unused1[2]; + + uint mipi_cal_config_csia; + uint mipi_cal_config_csib; + uint mipi_cal_config_csic; + uint mipi_cal_config_csid; + uint mipi_cal_config_csie; + + uint unused2[4]; + + uint mipi_cal_config_dsia; + uint mipi_cal_config_dsib; + uint mipi_cal_config_dsic; + uint mipi_cal_config_dsid; + + uint unused3[4]; + + uint mipi_cal_bias_pad_cfg0; + uint mipi_cal_bias_pad_cfg1; + uint mipi_cal_bias_pad_cfg2; +}; + +#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) +#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) +#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4) +#define MIPI_CAL_CTRL_START BIT(0) + +#define MIPI_CAL_STATUS_DONE BIT(16) +#define MIPI_CAL_STATUS_ACTIVE BIT(0) + +#define MIPI_CAL_OVERIDE(x) (((x) & 0x1) << 30) +#define MIPI_CAL_SEL(x) (((x) & 0x1) << 21) +#define MIPI_CAL_HSPDOS(x) (((x) & 0x1f) << 16) +#define MIPI_CAL_HSPUOS(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_TERMOS(x) (((x) & 0x1f) << 0) + +#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1) +#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0) + +#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16) +#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8) + +#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16) +#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) +#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) + +struct tegra_mipi_priv { + struct mipi_ctlr *mipi; + struct clk *mipi_cal; +}; + +static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, + int size) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + u32 value; + + value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | + MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); + value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); + value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) | + MIPI_CAL_TERMOS(0x5); + writel(value, &priv->mipi->mipi_cal_config_dsia); + writel(value, &priv->mipi->mipi_cal_config_dsib); + + /* Deselect PAD C */ + value = readl(&priv->mipi->mipi_cal_config_dsic); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_config_dsic); + + /* Deselect PAD D */ + value = readl(&priv->mipi->mipi_cal_config_dsid); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_config_dsid); + + value = readl(&priv->mipi->mipi_cal_ctrl); + value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); + value &= ~MIPI_CAL_CTRL_PRESCALE(0x3); + value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa) | + MIPI_CAL_CTRL_PRESCALE(0x2) | + MIPI_CAL_CTRL_CLKEN_OVR; + writel(value, &priv->mipi->mipi_cal_ctrl); + + /* clear any pending status bits */ + value = readl(&priv->mipi->mipi_cal_status); + writel(value, &priv->mipi->mipi_cal_status); + + value = readl(&priv->mipi->mipi_cal_ctrl); + value |= MIPI_CAL_CTRL_START; + writel(value, &priv->mipi->mipi_cal_ctrl); + + /* + * Wait for min 72uS to let calibration logic finish calibration + * sequence codes before waiting for pads idle state to apply the + * results. + */ + udelay(80); + + return readl_poll_sleep_timeout(&priv->mipi->mipi_cal_status, value, + !(value & MIPI_CAL_STATUS_ACTIVE) && + (value & MIPI_CAL_STATUS_DONE), 100, + 250000); +} + +static int tegra_mipi_enable(struct udevice *dev, bool val) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + u32 value; + + clk_enable(priv->mipi_cal); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0); + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg0); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); + value &= ~MIPI_CAL_BIAS_PAD_PDVREG; + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); + + return 0; +} + +static const struct misc_ops tegra_mipi_ops = { + .write = tegra_mipi_calibrate, + .set_enabled = tegra_mipi_enable, +}; + +static int tegra_mipi_probe(struct udevice *dev) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + + priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev); + if (!priv->mipi) { + log_debug("%s: no MIPI controller address\n", __func__); + return -EINVAL; + } + + priv->mipi_cal = devm_clk_get(dev, NULL); + if (IS_ERR(priv->mipi_cal)) { + log_debug("%s: Could not get MIPI clock: %ld\n", + __func__, PTR_ERR(priv->mipi_cal)); + return PTR_ERR(priv->mipi_cal); + } + + return 0; +} + +static const struct udevice_id tegra_mipi_ids[] = { + { .compatible = "nvidia,tegra114-mipi" }, + { } +}; + +U_BOOT_DRIVER(tegra_mipi) = { + .name = "tegra_mipi", + .id = UCLASS_MISC, + .ops = &tegra_mipi_ops, + .of_match = tegra_mipi_ids, + .probe = tegra_mipi_probe, + .priv_auto = sizeof(struct tegra_mipi_priv), +};

Existing Tegra DSI driver mostly fits T114 apart MIPI calibration which on T114 has dedicated driver. To resolve this MIPI calibration logic was split for pre-T114 and T114+ devices.
Tested-by: Ion Agorria ion@agorria.com # HTC One X Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/video/tegra20/tegra-dsi.c | 78 ++++++++++++++++++- .../video/tegra20/tegra-dsi.h | 24 +++++- 2 files changed, 96 insertions(+), 6 deletions(-) rename arch/arm/include/asm/arch-tegra30/dsi.h => drivers/video/tegra20/tegra-dsi.h (90%)
diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 72b91ed26b..f4743e8458 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -20,17 +20,24 @@ #include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/clock.h> -#include <asm/arch-tegra30/dsi.h>
#include "tegra-dc.h" +#include "tegra-dsi.h" #include "mipi-phy.h"
+/* List of supported DSI bridges */ +enum { + DSI_V0, + DSI_V1, +}; + struct tegra_dsi_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; struct mipi_dphy_timing dphy_timing;
struct udevice *panel; + struct udevice *mipi; struct display_timing timing;
struct dsi_ctlr *dsi; @@ -41,6 +48,8 @@ struct tegra_dsi_priv { int dsi_clk; int video_fifo_depth; int host_fifo_depth; + + u32 version; };
static void tegra_dc_enable_controller(struct udevice *dev) @@ -501,6 +510,41 @@ static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad) writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2)); }
+static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv) +{ + struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad; + u32 value; + int ret; + + ret = misc_set_enabled(priv->mipi, true); + if (ret) + log_debug("%s: failed to enable MIPI calibration: %d\n", + __func__, ret); + + writel(0, &pad->pad_ctrl); + writel(0, &pad->pad_ctrl_1); + writel(0, &pad->pad_ctrl_2); + writel(0, &pad->pad_ctrl_3); + writel(0, &pad->pad_ctrl_4); + + /* DSI pad enable */ + value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); + writel(value, &pad->pad_ctrl); + + 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); + writel(value, &pad->pad_ctrl_2); + + value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | + DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); + writel(value, &pad->pad_ctrl_3); + + ret = misc_write(priv->mipi, 0, NULL, 0); + if (ret) + log_debug("%s: MIPI calibration failed %d\n", __func__, ret); +} + static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout, unsigned long bclk, unsigned int vrefresh) @@ -664,10 +708,25 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) u32 value; int ret;
+ /* If for some reasone DSI is enabled then it needs to + * be disabled in order for the panel initialization + * commands to be properly sent. + */ + value = readl(&misc->dsi_pwr_ctrl); + + if (value & DSI_POWER_CONTROL_ENABLE) { + value = readl(&misc->dsi_pwr_ctrl); + value &= ~DSI_POWER_CONTROL_ENABLE; + writel(value, &misc->dsi_pwr_ctrl); + } + /* Disable interrupt */ writel(0, &misc->int_enable);
- tegra_dsi_pad_calibrate(&priv->dsi->pad); + if (priv->version) + tegra_dsi_mipi_calibrate(priv); + else + tegra_dsi_pad_calibrate(&priv->dsi->pad);
tegra_dsi_get_muldiv(device->format, &mul, &div);
@@ -792,6 +851,8 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) struct mipi_dsi_panel_plat *mipi_plat; int ret;
+ priv->version = dev_get_driver_data(dev); + priv->dsi = (struct dsi_ctlr *)dev_read_addr_ptr(dev); if (!priv->dsi) { printf("%s: No display controller address\n", __func__); @@ -814,6 +875,16 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) return log_ret(ret); }
+ if (priv->version) { + ret = uclass_get_device_by_phandle(UCLASS_MISC, dev, + "nvidia,mipi-calibrate", + &priv->mipi); + if (ret) { + log_debug("%s: cannot get MIPI: error %d\n", __func__, ret); + return ret; + } + } + panel_get_display_timing(priv->panel, &priv->timing);
mipi_plat = dev_get_plat(priv->panel); @@ -845,7 +916,8 @@ static const struct panel_ops tegra_dsi_bridge_ops = { };
static const struct udevice_id tegra_dsi_bridge_ids[] = { - { .compatible = "nvidia,tegra30-dsi" }, + { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 }, + { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 }, { } };
diff --git a/arch/arm/include/asm/arch-tegra30/dsi.h b/drivers/video/tegra20/tegra-dsi.h similarity index 90% rename from arch/arm/include/asm/arch-tegra30/dsi.h rename to drivers/video/tegra20/tegra-dsi.h index 7ade132613..69dac4bd1b 100644 --- a/arch/arm/include/asm/arch-tegra30/dsi.h +++ b/drivers/video/tegra20/tegra-dsi.h @@ -4,8 +4,8 @@ * NVIDIA Corporation <www.nvidia.com> */
-#ifndef __ASM_ARCH_TEGRA_DSI_H -#define __ASM_ARCH_TEGRA_DSI_H +#ifndef _TEGRA_DSI_H +#define _TEGRA_DSI_H
#ifndef __ASSEMBLY__ #include <linux/bitops.h> @@ -105,6 +105,10 @@ struct dsi_pad_ctrl_reg { uint pad_ctrl_cd; /* _PAD_CONTROL_CD_0 */ uint pad_cd_status; /* _PAD_CD_STATUS_0 */ uint dsi_vid_mode_control; /* _DSI_VID_MODE_CONTROL_0 */ + uint pad_ctrl_1; /* _PAD_CONTROL_1 */ + uint pad_ctrl_2; /* _PAD_CONTROL_2 */ + uint pad_ctrl_3; /* _PAD_CONTROL_3 */ + uint pad_ctrl_4; /* _PAD_CONTROL_4 */ };
/* Display Serial Interface (DSI_) regs */ @@ -184,6 +188,20 @@ struct dsi_ctlr { #define DSI_PAD_CONTROL_PAD_LPUPADJ(x) (((x) & 0x3) << 14) #define DSI_PAD_CONTROL_PAD_LPDNADJ(x) (((x) & 0x3) << 12)
+#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0) +#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16) + +#define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0) +#define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4) +#define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8) +#define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12) +#define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16) + +#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12) +#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8) +#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4) +#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0) + /* * pixel format as used in the DSI_CONTROL_FORMAT field */ @@ -214,4 +232,4 @@ enum tegra_dsi_format { #define PAD_DRIV_DN_REF(x) (((x) & 0x7) << 16) #define PAD_DRIV_UP_REF(x) (((x) & 0x7) << 8)
-#endif /* __ASM_ARCH_TEGRA_DSI_H */ +#endif /* _TEGRA_DSI_H */

Implement reset use to discard any changes which could have been applied to DSI before and can interfere with current configuration.
Tested-by: Ion Agorria ion@agorria.com # HTC One X Tested-by: Svyatoslav Ryhel clamor95@gmail.com # Nvidia Tegratab T114 Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/video/tegra20/tegra-dsi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index f4743e8458..6408026808 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -12,6 +12,7 @@ #include <mipi_dsi.h> #include <backlight.h> #include <panel.h> +#include <reset.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/time.h> @@ -849,6 +850,7 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) struct tegra_dsi_priv *priv = dev_get_priv(dev); struct mipi_dsi_device *device = &priv->device; struct mipi_dsi_panel_plat *mipi_plat; + struct reset_ctl reset_ctl; int ret;
priv->version = dev_get_driver_data(dev); @@ -862,6 +864,13 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) priv->video_fifo_depth = 480; priv->host_fifo_depth = 64;
+ ret = reset_get_by_name(dev, "dsi", &reset_ctl); + if (ret) { + log_debug("%s: reset_get_by_name() failed: %d\n", + __func__, ret); + return ret; + } + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, "avdd-dsi-csi-supply", &priv->avdd); if (ret) @@ -900,12 +909,17 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
tegra_dsi_get_format(device->format, &priv->format);
+ reset_assert(&reset_ctl); + ret = regulator_set_enable_if_allowed(priv->avdd, true); if (ret && ret != -ENOSYS) return ret;
tegra_dsi_init_clocks(dev);
+ mdelay(2); + reset_deassert(&reset_ctl); + return 0; }
participants (1)
-
Svyatoslav Ryhel