From 5643a85205643ea95078119d1406eaa436a4aca5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 24 Nov 2024 14:22:18 +0200 Subject: [PATCH 01/28] video: tegra20: dc: switch to newer clk API Switch to struct clk instead of working with plain clock id. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dc.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index d24aa375b39..bd0d8ca7b8f 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -44,7 +45,8 @@ struct tegra_lcd_priv { 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 */ + struct clk *clk; + struct clk *clk_parent; ulong scdiv; /* Clock divider used by disp_clk_ctrl */ bool rotation; /* 180 degree panel turn */ int pipe; /* DC controller: 0 for A, 1 for B */ @@ -301,7 +303,7 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, void *default_lcd_base) { struct disp_ctl_win window; - unsigned long rate = clock_get_rate(priv->dc_clk[1]); + unsigned long rate = clk_get_rate(priv->clk_parent); priv->frame_buffer = (u32)default_lcd_base; @@ -309,12 +311,12 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, * 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) + if (priv->clk_parent->id == 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) + if (priv->clk_parent->id == CLOCK_ID_DISPLAY2) rate /= 2; #endif @@ -334,7 +336,7 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, */ clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_CGENERAL, 150 * 1000000); - clock_start_periph_pll(priv->dc_clk[0], priv->dc_clk[1], + clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, rate); basic_init(&priv->dc->cmd); @@ -383,7 +385,7 @@ static int tegra_lcd_probe(struct udevice *dev) } ret = tegra_powergate_sequence_power_up(powergate, - priv->dc_clk[0]); + priv->clk->id); if (ret < 0) { log_err("failed to power up DISP gate: %d", ret); return ret; @@ -451,11 +453,18 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) 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", - __func__, dev->name, ret); - return -EINVAL; + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DC clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DC clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); } priv->rotation = dev_read_bool(dev, "nvidia,180-rotation"); From 026a1ab2fa6eb0101538cc5186ee20ff01b220fa Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 27 Nov 2024 13:35:10 +0200 Subject: [PATCH 02/28] video: tegra20: dc: remove hardcoded Tegra 2 specific parts Since pinmux driver now is available for Tegra 2, these parts may be removed from here and defined either in device tree or in the device board files. Signed-off-by: Svyatoslav Ryhel --- .../include/asm/arch-tegra20/clock-tables.h | 2 ++ drivers/video/tegra20/tegra-dc.c | 18 ++---------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/arch/arm/include/asm/arch-tegra20/clock-tables.h b/arch/arm/include/asm/arch-tegra20/clock-tables.h index 861b3d5d07c..82685353bd1 100644 --- a/arch/arm/include/asm/arch-tegra20/clock-tables.h +++ b/arch/arm/include/asm/arch-tegra20/clock-tables.h @@ -32,6 +32,7 @@ enum clock_id { CLOCK_ID_COUNT, /* number of clocks */ CLOCK_ID_NONE = -1, + CLOCK_ID_DISPLAY2 = CLOCK_ID_NONE, /* for compatibility */ }; /* The clocks supported by the hardware */ @@ -159,6 +160,7 @@ enum periph_id { PERIPH_ID_COUNT, PERIPH_ID_NONE = -1, + PERIPH_ID_DSIB = CLOCK_ID_NONE, /* for compatibility */ }; enum pll_out_id { diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index bd0d8ca7b8f..d54279ebda1 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -311,15 +311,10 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, * We halve the rate if DISP1 parent is PLLD, since actual parent * is plld_out0 which is PLLD divided by 2. */ - if (priv->clk_parent->id == CLOCK_ID_DISPLAY) + if (priv->clk_parent->id == CLOCK_ID_DISPLAY || + priv->clk_parent->id == CLOCK_ID_DISPLAY2) rate /= 2; -#ifndef CONFIG_TEGRA20 - /* PLLD2 obeys same rules as PLLD but it is present only on T30+ */ - if (priv->clk_parent->id == CLOCK_ID_DISPLAY2) - rate /= 2; -#endif - /* * 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 @@ -366,10 +361,6 @@ static int tegra_lcd_probe(struct udevice *dev) int ret; /* Initialize the Tegra display controller */ -#ifdef CONFIG_TEGRA20 - funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); -#endif - if (priv->soc->has_pgate) { uint powergate; @@ -409,11 +400,6 @@ static int tegra_lcd_probe(struct udevice *dev) return -1; } -#ifdef CONFIG_TEGRA20 - pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); - pinmux_tristate_disable(PMUX_PINGRP_GPU); -#endif - ret = panel_enable_backlight(priv->panel); if (ret) { debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); From 8f1e56c9dd2505a87778d18516c29d8015e5b4ba Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 27 Nov 2024 13:39:39 +0200 Subject: [PATCH 03/28] video: tegra20: dc: remove excessive headers Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dc.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index d54279ebda1..54d60836b66 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -10,20 +10,11 @@ #include #include #include -#include -#include #include -#include -#include #include -#include #include - #include -#include -#include #include -#include #include "tegra-dc.h" From 530ac531754f6897f9519eb7070989306fad1b1f Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 27 Nov 2024 13:57:05 +0200 Subject: [PATCH 04/28] video: tegra20: dc: improve code quality Mainly unification and improving of readability. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dc.c | 50 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index 54d60836b66..e366df9ab51 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -137,10 +137,9 @@ static int update_display_mode(struct tegra_lcd_priv *priv) val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; writel(val, &disp->disp_interface_ctrl); - } - if (priv->soc->has_rgb) writel(0x00010001, &disp->shift_clk_opt); + } val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT; @@ -260,7 +259,9 @@ static int setup_window(struct tegra_lcd_priv *priv, win->out_h = priv->height; win->phys_addr = priv->frame_buffer; win->stride = priv->width * (1 << priv->log2_bpp) / 8; - debug("%s: depth = %d\n", __func__, priv->log2_bpp); + + log_debug("%s: depth = %d\n", __func__, priv->log2_bpp); + switch (priv->log2_bpp) { case VIDEO_BPP32: win->fmt = COLOR_DEPTH_R8G8B8A8; @@ -272,7 +273,7 @@ static int setup_window(struct tegra_lcd_priv *priv, break; default: - debug("Unsupported LCD bit depth"); + log_debug("Unsupported LCD bit depth\n"); return -1; } @@ -295,6 +296,7 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, { struct disp_ctl_win window; unsigned long rate = clk_get_rate(priv->clk_parent); + int ret; priv->frame_buffer = (u32)default_lcd_base; @@ -315,7 +317,7 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, if (!priv->scdiv) priv->scdiv = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2; - debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); + log_debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); /* * HOST1X is init by default at 150MHz with PLLC as parent @@ -336,8 +338,9 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, if (priv->pixel_clock) update_display_mode(priv); - if (setup_window(priv, &window)) - return -1; + ret = setup_window(priv, &window); + if (ret) + return ret; update_window(priv, &window); @@ -362,14 +365,14 @@ static int tegra_lcd_probe(struct udevice *dev) ret = tegra_powergate_power_off(powergate); if (ret < 0) { - log_err("failed to power off DISP gate: %d", ret); + log_debug("failed to power off DISP gate: %d", ret); return ret; } ret = tegra_powergate_sequence_power_up(powergate, priv->clk->id); if (ret < 0) { - log_err("failed to power up DISP gate: %d", ret); + log_debug("failed to power up DISP gate: %d", ret); return ret; } } @@ -386,14 +389,15 @@ static int tegra_lcd_probe(struct udevice *dev) memset((u8 *)plat->base, 0, plat->size); flush_dcache_all(); - if (tegra_display_probe(priv, (void *)plat->base)) { - debug("%s: Failed to probe display driver\n", __func__); - return -1; + ret = tegra_display_probe(priv, (void *)plat->base); + if (ret) { + log_debug("%s: Failed to probe display driver\n", __func__); + return ret; } ret = panel_enable_backlight(priv->panel); if (ret) { - debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); + log_debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); return ret; } @@ -406,8 +410,8 @@ static int tegra_lcd_probe(struct udevice *dev) uc_priv->xsize = priv->width; uc_priv->ysize = priv->height; uc_priv->bpix = priv->log2_bpp; - debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, - plat->size); + log_debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, + plat->size); return panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); } @@ -424,7 +428,7 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev); if (!priv->dc) { - debug("%s: No display controller address\n", __func__); + log_debug("%s: No display controller address\n", __func__); return -EINVAL; } @@ -449,8 +453,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) rgb = fdt_subnode_offset(blob, node, "rgb"); if (rgb < 0) { - debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n", - __func__, dev->name, rgb); + log_debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n", + __func__, dev->name, rgb); return -EINVAL; } @@ -460,15 +464,15 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) */ panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); if (panel_node < 0) { - debug("%s: Cannot find panel information\n", __func__); + log_debug("%s: Cannot find panel information\n", __func__); return -EINVAL; } ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node, &priv->panel); if (ret) { - debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, - dev->name, ret); + log_debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, + dev->name, ret); return ret; } @@ -486,8 +490,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) if (ret) { ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing); if (ret) { - debug("%s: Cannot read display timing for '%s' (ret=%d)\n", - __func__, dev->name, ret); + log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n", + __func__, dev->name, ret); return -EINVAL; } } From eef35b875561d83908c683e78ddd80e5187fa2b9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 24 Nov 2024 09:38:03 +0200 Subject: [PATCH 05/28] video: tegra20: dsi: check for panels among child nodes Switch to Linux-like approach of DSI panel binding as a DSI controllers child node. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 6327266dd22..53db5a25d40 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -973,10 +973,22 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) debug("%s: Cannot get avdd-dsi-csi-supply: error %d\n", __func__, ret); - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + /* Check all DSI children */ + device_foreach_child(priv->panel, dev) { + if (device_get_uclass_id(priv->panel) == UCLASS_PANEL) + break; + } + + /* if loop exits without panel device return error */ + if (device_get_uclass_id(priv->panel) != UCLASS_PANEL) { + log_debug("%s: panel not found, ret %d\n", __func__, ret); + return -EINVAL; + } + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, dev_ofnode(priv->panel), + &priv->panel); if (ret) { - printf("%s: Cannot get panel: error %d\n", __func__, ret); + log_debug("%s: Cannot get panel: error %d\n", __func__, ret); return log_ret(ret); } @@ -1036,6 +1048,7 @@ U_BOOT_DRIVER(tegra_dsi) = { .id = UCLASS_PANEL, .of_match = tegra_dsi_bridge_ids, .ops = &tegra_dsi_bridge_ops, + .bind = dm_scan_fdt_dev, .probe = tegra_dsi_bridge_probe, .plat_auto = sizeof(struct tegra_dc_plat), .priv_auto = sizeof(struct tegra_dsi_priv), From 239b7f12997ce52808d79f4a162c3ab133a874a4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 24 Nov 2024 14:26:08 +0200 Subject: [PATCH 06/28] video: tegra20: dsi: switch to newer clk API Switch to struct clk instead of working with plain clock id. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 49 ++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 53db5a25d40..97a30402902 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -46,7 +47,9 @@ struct tegra_dsi_priv { enum tegra_dsi_format format; - int dsi_clk; + struct clk *clk; + struct clk *clk_parent; + int video_fifo_depth; int host_fifo_depth; @@ -862,12 +865,26 @@ static void tegra_dsi_init_clocks(struct udevice *dev) unsigned int mul, div; unsigned long bclk, plld; - if (!priv->slave) { + /* Switch parents of DSI clocks in case of not standard parent */ + if (priv->clk->id == PERIPH_ID_DSI && + priv->clk_parent->id == CLOCK_ID_DISPLAY2) { + /* Change DSIA clock parent to PLLD2 */ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + /* DSIA_CLK_SRC */ + setbits_le32(&clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_base, + BIT(25)); + } + + if (priv->clk->id == PERIPH_ID_DSIB && + priv->clk_parent->id == CLOCK_ID_DISPLAY) { /* Change DSIB clock parent to match DSIA */ struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); /* DSIB_CLK_SRC */ + /* DSIB_CLK_SRC */ + clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); } tegra_dsi_get_muldiv(device->format, &mul, &div); @@ -893,16 +910,16 @@ static void tegra_dsi_init_clocks(struct udevice *dev) switch (clock_get_osc_freq()) { case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */ case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 12, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 12, 0, 8); break; case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 26, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 26, 0, 8); break; case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 13, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 13, 0, 8); break; case CLOCK_OSC_FREQ_19_2: @@ -914,11 +931,7 @@ static void tegra_dsi_init_clocks(struct udevice *dev) break; } - priv->dsi_clk = clock_decode_periph_id(dev); - - clock_enable(priv->dsi_clk); - udelay(2); - reset_set_enable(priv->dsi_clk, 0); + clk_enable(priv->clk); } static int tegra_dsi_ganged_probe(struct udevice *dev) @@ -955,6 +968,20 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) return -EINVAL; } + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DSI clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DSI clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + priv->video_fifo_depth = 1920; priv->host_fifo_depth = 64; From 785787b7f087f5749a08329eb4e3a0ea823822c4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 31 Dec 2024 09:50:03 +0200 Subject: [PATCH 07/28] video: tegra20: dsi: align ganged mode implementation Align U-Boot DSI ganged mode implementation with the Linux kernel's implementation. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 51 ++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 97a30402902..dd091c15ca1 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -518,8 +518,9 @@ 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) +static void tegra_dsi_mipi_calibrate(struct udevice *dev) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad; u32 value; int ret; @@ -551,12 +552,17 @@ static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv) ret = misc_write(priv->mipi, 0, NULL, 0); if (ret) log_debug("%s: MIPI calibration failed %d\n", __func__, ret); + + if (priv->slave) + tegra_dsi_mipi_calibrate(priv->slave); } -static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout, +static void tegra_dsi_set_timeout(struct udevice *dev, unsigned long bclk, unsigned int vrefresh) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timeout_reg *rtimeout = &priv->dsi->timeout; unsigned int timeout; u32 value; @@ -572,12 +578,17 @@ static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout, value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); writel(value, &rtimeout->dsi_to_tally); + + if (priv->slave) + tegra_dsi_set_timeout(priv->slave, bclk, vrefresh); } -static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming, +static void tegra_dsi_set_phy_timing(struct udevice *dev, unsigned long period, const struct mipi_dphy_timing *dphy_timing) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timing_reg *ptiming = &priv->dsi->ptiming; u32 value; value = DSI_TIMING_FIELD(dphy_timing->hsexit, period, 1) << 24 | @@ -601,6 +612,9 @@ static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming, DSI_TIMING_FIELD(dphy_timing->tasure, period, 1) << 8 | DSI_TIMING_FIELD(dphy_timing->tago, period, 1); writel(value, &ptiming->dsi_bta_timing); + + if (priv->slave) + tegra_dsi_set_phy_timing(priv->slave, period, dphy_timing); } static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, @@ -746,6 +760,7 @@ static void tegra_dsi_configure(struct udevice *dev, } if (priv->slave) { + tegra_dsi_configure(priv->slave, mode_flags); /* * TODO: Support modes other than symmetrical left-right * split. @@ -756,6 +771,21 @@ static void tegra_dsi_configure(struct udevice *dev, } } +static void tegra_dsi_enable(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_misc_reg *misc = &priv->dsi->misc; + u32 value; + + /* enable DSI controller */ + value = readl(&misc->dsi_pwr_ctrl); + value |= DSI_POWER_CONTROL_ENABLE; + writel(value, &misc->dsi_pwr_ctrl); + + if (priv->slave) + tegra_dsi_enable(priv->slave); +} + static int tegra_dsi_encoder_enable(struct udevice *dev) { struct tegra_dsi_priv *priv = dev_get_priv(dev); @@ -783,7 +813,7 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) writel(0, &misc->int_enable); if (priv->version) - tegra_dsi_mipi_calibrate(priv); + tegra_dsi_mipi_calibrate(dev); else tegra_dsi_pad_calibrate(&priv->dsi->pad); @@ -792,7 +822,7 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) /* compute byte clock */ bclk = (timing->pixelclock.typ * mul) / (div * device->lanes); - tegra_dsi_set_timeout(&priv->dsi->timeout, bclk, 60); + tegra_dsi_set_timeout(dev, bclk, 60); /* * Compute bit clock and round up to the next MHz. @@ -816,8 +846,7 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) * The D-PHY timing fields are expressed in byte-clock cycles, so * multiply the period by 8. */ - tegra_dsi_set_phy_timing(&priv->dsi->ptiming, - period * 8, &priv->dphy_timing); + tegra_dsi_set_phy_timing(dev, period * 8, &priv->dphy_timing); /* Perform panel HW setup */ ret = panel_enable_backlight(priv->panel); @@ -828,13 +857,7 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) tegra_dc_enable_controller(dev); - /* enable DSI controller */ - value = readl(&misc->dsi_pwr_ctrl); - value |= DSI_POWER_CONTROL_ENABLE; - writel(value, &misc->dsi_pwr_ctrl); - - if (priv->slave) - tegra_dsi_encoder_enable(priv->slave); + tegra_dsi_enable(dev); return 0; } From aa9fa8c1addf76e94f02de8d5ca7774777680285 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 2 Dec 2024 08:08:03 +0200 Subject: [PATCH 08/28] video: tegra20: dsi: make SOL delay calculation mode independent Move SOL delay calculation outside of video mode conditions. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 49 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index dd091c15ca1..e1502d8d60e 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -712,9 +712,6 @@ static void tegra_dsi_configure(struct udevice *dev, writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3); writel(hfp, &len->dsi_pkt_len_4_5); writel(0x0f0f << 16, &len->dsi_pkt_len_6_7); - - /* set SOL delay (for non-burst mode only) */ - writel(8 * mul / div, &misc->dsi_sol_delay); } else { if (priv->master || priv->slave) { /* @@ -734,31 +731,31 @@ static void tegra_dsi_configure(struct udevice *dev, value = MIPI_DCS_WRITE_MEMORY_START << 8 | MIPI_DCS_WRITE_MEMORY_CONTINUE; writel(value, &len->dsi_dcs_cmds); - - /* set SOL delay */ - if (priv->master || priv->slave) { - unsigned long delay, bclk, bclk_ganged; - unsigned int lanes = device->lanes; - unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + - timing->hback_porch.typ + timing->hsync_len.typ; - - /* 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 * 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; - } - - writel(value, &misc->dsi_sol_delay); } + /* set SOL delay */ + if (priv->master || priv->slave) { + unsigned long delay, bclk, bclk_ganged; + unsigned int lanes = device->lanes; + unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + + timing->hback_porch.typ + timing->hsync_len.typ; + + /* 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 * mul, div * lanes); + bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); + value = bclk - bclk_ganged + delay + 20; + } else { + /* set SOL delay (for non-burst mode only) */ + value = 8 * mul / div; + } + + writel(value, &misc->dsi_sol_delay); + if (priv->slave) { tegra_dsi_configure(priv->slave, mode_flags); /* From 7d2004983e969e8a5603b8973c8450484b55da71 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 2 Dec 2024 08:12:36 +0200 Subject: [PATCH 09/28] video: tegra20: dsi: calculate packet parameters for video mode Calculate packet parameters for video mode same way it is done or command mode, by halving timings plugged into equations. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index e1502d8d60e..2beb9ec9f24 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -697,12 +697,19 @@ static void tegra_dsi_configure(struct udevice *dev, /* horizontal back porch */ hbp = timing->hback_porch.typ * mul / div; - if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) - hbp += hsw; - /* horizontal front porch */ hfp = timing->hfront_porch.typ * mul / div; + if (priv->master || priv->slave) { + hact /= 2; + hsw /= 2; + hbp = hbp / 2 - 1; + hfp /= 2; + } + + if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) + hbp += hsw; + /* subtract packet overhead */ hsw -= 10; hbp -= 14; From 69d3ad18808dac8d2df78553f316566906db3d02 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 26 Feb 2025 09:51:09 +0200 Subject: [PATCH 10/28] video: tegra20: dsi: calculate lanes for ganged mode Use Linux DSI driver approach to calculate lanes for ganged mode. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 35 +++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 2beb9ec9f24..b132a23432c 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -617,6 +617,28 @@ static void tegra_dsi_set_phy_timing(struct udevice *dev, tegra_dsi_set_phy_timing(priv->slave, period, dphy_timing); } +static u32 tegra_dsi_get_lanes(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + + if (priv->master) { + struct tegra_dsi_priv *mpriv = dev_get_priv(priv->master); + struct mipi_dsi_device *mdevice = &mpriv->device; + + return mdevice->lanes + device->lanes; + } + + if (priv->slave) { + struct tegra_dsi_priv *spriv = dev_get_priv(priv->slave); + struct mipi_dsi_device *sdevice = &spriv->device; + + return device->lanes + sdevice->lanes; + } + + return device->lanes; +} + static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, unsigned int size) { @@ -743,7 +765,7 @@ static void tegra_dsi_configure(struct udevice *dev, /* set SOL delay */ if (priv->master || priv->slave) { unsigned long delay, bclk, bclk_ganged; - unsigned int lanes = device->lanes; + unsigned int lanes = tegra_dsi_get_lanes(dev); unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + timing->hback_porch.typ + timing->hsync_len.typ; @@ -798,7 +820,7 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) struct dsi_misc_reg *misc = &priv->dsi->misc; unsigned int mul, div; unsigned long bclk, plld, period; - u32 value; + u32 value, lanes; int ret; /* If for some reasone DSI is enabled then it needs to @@ -824,7 +846,8 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) tegra_dsi_get_muldiv(device->format, &mul, &div); /* compute byte clock */ - bclk = (timing->pixelclock.typ * mul) / (div * device->lanes); + lanes = tegra_dsi_get_lanes(dev); + bclk = (timing->pixelclock.typ * mul) / (div * lanes); tegra_dsi_set_timeout(dev, bclk, 60); @@ -889,7 +912,7 @@ static void tegra_dsi_init_clocks(struct udevice *dev) struct tegra_dsi_priv *priv = dev_get_priv(dev); struct tegra_dc_plat *dc_plat = dev_get_plat(dev); struct mipi_dsi_device *device = &priv->device; - unsigned int mul, div; + unsigned int mul, div, lanes; unsigned long bclk, plld; /* Switch parents of DSI clocks in case of not standard parent */ @@ -916,8 +939,8 @@ static void tegra_dsi_init_clocks(struct udevice *dev) tegra_dsi_get_muldiv(device->format, &mul, &div); - bclk = (priv->timing.pixelclock.typ * mul) / - (div * device->lanes); + lanes = tegra_dsi_get_lanes(dev); + bclk = (priv->timing.pixelclock.typ * mul) / (div * lanes); plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC); From 5325221186244173fa2274352cf4fb693988accd Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 31 Dec 2024 09:58:01 +0200 Subject: [PATCH 11/28] video: tegra20: dsi: pass source on DSI configuration Parametrize DSI configuration by passing DC source pipe. This should resolve possible failure if second DC is used with DSI for some reason. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index b132a23432c..079b00e0f61 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -650,7 +650,7 @@ static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, writel(DSI_GANGED_MODE_CONTROL_ENABLE, &ganged->ganged_mode_ctrl); } -static void tegra_dsi_configure(struct udevice *dev, +static void tegra_dsi_configure(struct udevice *dev, unsigned int pipe, unsigned long mode_flags) { struct tegra_dsi_priv *priv = dev_get_priv(dev); @@ -681,7 +681,7 @@ static void tegra_dsi_configure(struct udevice *dev, value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(priv->format) | DSI_CONTROL_LANES(device->lanes - 1) | - DSI_CONTROL_SOURCE(0); + DSI_CONTROL_SOURCE(pipe); writel(value, &misc->dsi_ctrl); writel(priv->video_fifo_depth, &misc->dsi_max_threshold); @@ -786,7 +786,7 @@ static void tegra_dsi_configure(struct udevice *dev, writel(value, &misc->dsi_sol_delay); if (priv->slave) { - tegra_dsi_configure(priv->slave, mode_flags); + tegra_dsi_configure(priv->slave, pipe, mode_flags); /* * TODO: Support modes other than symmetrical left-right * split. @@ -815,6 +815,7 @@ static void tegra_dsi_enable(struct udevice *dev) static int tegra_dsi_encoder_enable(struct udevice *dev) { struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); struct mipi_dsi_device *device = &priv->device; struct display_timing *timing = &priv->timing; struct dsi_misc_reg *misc = &priv->dsi->misc; @@ -880,7 +881,7 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) if (ret) return ret; - tegra_dsi_configure(dev, device->mode_flags); + tegra_dsi_configure(dev, dc_plat->pipe, device->mode_flags); tegra_dc_enable_controller(dev); From a6855d57eaba6b0873bd49f6c4541a306a4e3e3e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 24 Nov 2024 14:06:32 +0200 Subject: [PATCH 12/28] ARM: tegra: endeavoru: adjust panel node Bind panel in Linux-style, as DSI child. Signed-off-by: Svyatoslav Ryhel --- arch/arm/dts/tegra30-htc-endeavoru.dts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/arch/arm/dts/tegra30-htc-endeavoru.dts b/arch/arm/dts/tegra30-htc-endeavoru.dts index dbff795bd89..8a0ba3c07cc 100644 --- a/arch/arm/dts/tegra30-htc-endeavoru.dts +++ b/arch/arm/dts/tegra30-htc-endeavoru.dts @@ -48,7 +48,17 @@ avdd-dsi-csi-supply = <&avdd_dsi_csi>; - panel = <&panel>; + panel@0 { + compatible = "htc,edge-panel"; + reg = <0>; + + reset-gpios = <&gpio TEGRA_GPIO(N, 6) GPIO_ACTIVE_LOW>; + + vdd-supply = <&vdd_3v3_panel>; + vddio-supply = <&vdd_1v8_panel>; + + backlight = <&backlight>; + }; }; }; @@ -1292,17 +1302,6 @@ }; }; - panel: panel { - compatible = "htc,edge-panel"; - - reset-gpios = <&gpio TEGRA_GPIO(N, 6) GPIO_ACTIVE_LOW>; - - vdd-supply = <&vdd_3v3_panel>; - vddio-supply = <&vdd_1v8_panel>; - - backlight = <&backlight>; - }; - vcore_emmc: regulator-emmc { compatible = "regulator-fixed"; regulator-name = "vdd_2v85_sdmmc"; From a237a209933de579dc16e768502850fee486fda6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 21 Nov 2024 14:43:00 +0200 Subject: [PATCH 13/28] pinctrl: tegra: add Tegra K1 support Tegra 124 is fully compatible with existing Tegra pincontrol driver, but it needs a specific MIPI PAD control pinconfig. Signed-off-by: Svyatoslav Ryhel --- arch/arm/include/asm/arch-tegra124/pinmux.h | 4 ++ drivers/pinctrl/tegra/pinctrl-tegra.c | 56 +++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/arch/arm/include/asm/arch-tegra124/pinmux.h b/arch/arm/include/asm/arch-tegra124/pinmux.h index 3aba17d21e4..fbe15fc612d 100644 --- a/arch/arm/include/asm/arch-tegra124/pinmux.h +++ b/arch/arm/include/asm/arch-tegra124/pinmux.h @@ -578,6 +578,10 @@ static const char * const tegra_pinctrl_to_drvgrp[] = { [PMUX_DRVGRP_AO4] = "ao4", }; +static const char * const tegra_pinctrl_to_mipipadgrp[] = { + [PMUX_MIPIPADCTRLGRP_DSI_B] = "mipi_pad_ctrl_dsi_b", +}; + static const char * const tegra_pinctrl_to_func[] = { [PMUX_FUNC_DEFAULT] = "default", [PMUX_FUNC_BLINK] = "blink", diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index ad7112a05e6..e6b957f5537 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -67,6 +67,58 @@ exit: kfree(drive_group); } +#ifdef TEGRA_PMX_SOC_HAS_MIPI_PAD_CTRL_GRPS +static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) +{ + struct pmux_mipipadctrlgrp_config *mipipad_group; + int i, ret, pad_id; + const char *function; + const char **pads; + + mipipad_group = kmalloc_array(padcnt, sizeof(*mipipad_group), GFP_KERNEL); + if (!mipipad_group) { + log_debug("%s: cannot allocate mipi pad group array\n", __func__); + return; + } + + /* decode function id and fill the first copy of pmux_mipipadctrlgrp_config */ + function = dev_read_string(config, "nvidia,function"); + if (function) + for (i = 0; i < PMUX_FUNC_COUNT; i++) + if (tegra_pinctrl_to_func[i]) + if (!strcmp(function, tegra_pinctrl_to_func[i])) + break; + + mipipad_group[0].func = i; + + for (i = 1; i < padcnt; i++) + memcpy(&mipipad_group[i], &mipipad_group[0], sizeof(mipipad_group[0])); + + ret = dev_read_string_list(config, "nvidia,pins", &pads); + if (ret < 0) { + log_debug("%s: could not parse property nvidia,pins\n", __func__); + goto exit; + } + + for (i = 0; i < padcnt; i++) { + for (pad_id = 0; pad_id < PMUX_MIPIPADCTRLGRP_COUNT; pad_id++) + if (tegra_pinctrl_to_mipipadgrp[pad_id]) + if (!strcmp(pads[i], tegra_pinctrl_to_mipipadgrp[pad_id])) { + mipipad_group[i].grp = pad_id; + break; + } + } + + pinmux_config_mipipadctrlgrp_table(mipipad_group, padcnt); + + free(pads); +exit: + kfree(mipipad_group); +} +#else +static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) { } +#endif + static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) { struct pmux_pingrp_config *pinmux_group; @@ -170,6 +222,9 @@ static int tegra_pinctrl_set_state(struct udevice *dev, struct udevice *config) if (!strncmp(name, "drive_", 6)) /* Drive node is detected */ tegra_pinctrl_set_drive(child, ret); + else if (!strncmp(name, "mipi_pad_ctrl_", 14)) + /* Handle T124 specific pinconfig */ + tegra_pinctrl_set_mipipad(child, ret); else /* Pin node is detected */ tegra_pinctrl_set_pin(child, ret); @@ -236,6 +291,7 @@ static int tegra_pinctrl_bind(struct udevice *dev) static const struct udevice_id tegra_pinctrl_ids[] = { { .compatible = "nvidia,tegra30-pinmux" }, { .compatible = "nvidia,tegra114-pinmux" }, + { .compatible = "nvidia,tegra124-pinmux" }, { }, }; From cd37937fc313e0235487bbd641fd9bad112b82cd Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 18 Nov 2024 08:58:18 +0200 Subject: [PATCH 14/28] video: tegra20: dc: dsi: add Tegra K1 compatible Tegra K1 is fully compatible with existing DC and DSI implementation using Tegra 4 data. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dc.c | 3 +++ drivers/video/tegra20/tegra-dsi.c | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index e366df9ab51..16a2b5281bf 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -553,6 +553,9 @@ static const struct udevice_id tegra_lcd_ids[] = { }, { .compatible = "nvidia,tegra114-dc", .data = (ulong)&tegra114_dc_soc_info + }, { + .compatible = "nvidia,tegra124-dc", + .data = (ulong)&tegra114_dc_soc_info }, { /* sentinel */ } diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 079b00e0f61..25cd4db0061 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -1118,6 +1118,7 @@ static const struct panel_ops tegra_dsi_bridge_ops = { static const struct udevice_id tegra_dsi_bridge_ids[] = { { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 }, { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 }, + { .compatible = "nvidia,tegra124-dsi", .data = DSI_V1 }, { } }; From f84ac08147d89b196ad6ec38d6a6773bf965dac8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 18 Nov 2024 08:58:18 +0200 Subject: [PATCH 15/28] video: tegra20: mipi: add Tegra K1 support Re-design MIPI calibration driver to fit T124. Signed-off-by: Svyatoslav Ryhel --- drivers/video/tegra20/tegra-dsi.c | 11 ++- drivers/video/tegra20/tegra-mipi.c | 134 ++++++++++++++++++++++++++--- 2 files changed, 130 insertions(+), 15 deletions(-) diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 25cd4db0061..9f39ac7589b 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -53,6 +53,7 @@ struct tegra_dsi_priv { int video_fifo_depth; int host_fifo_depth; + u32 calibration_pads; u32 version; /* for ganged-mode support */ @@ -549,7 +550,7 @@ static void tegra_dsi_mipi_calibrate(struct udevice *dev) DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); writel(value, &pad->pad_ctrl_3); - ret = misc_write(priv->mipi, 0, NULL, 0); + ret = misc_write(priv->mipi, priv->calibration_pads, NULL, 0); if (ret) log_debug("%s: MIPI calibration failed %d\n", __func__, ret); @@ -1078,6 +1079,14 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) log_debug("%s: cannot get MIPI: error %d\n", __func__, ret); return ret; } + + ret = dev_read_u32_index(dev, "nvidia,mipi-calibrate", 1, + &priv->calibration_pads); + if (ret) { + log_debug("%s: cannot get calibration pads: error %d\n", + __func__, ret); + return ret; + } } panel_get_display_timing(priv->panel, &priv->timing); diff --git a/drivers/video/tegra20/tegra-mipi.c b/drivers/video/tegra20/tegra-mipi.c index 2df3c1a9942..a4f4343d008 100644 --- a/drivers/video/tegra20/tegra-mipi.c +++ b/drivers/video/tegra20/tegra-mipi.c @@ -10,9 +10,10 @@ #include #include +#include #include -/* MIPI control registers 0x00 ~ 0x60 */ +/* MIPI control registers 0x00 ~ 0x74 */ struct mipi_ctlr { uint mipi_cal_ctrl; uint mipi_cal_autocal_ctrl; @@ -38,8 +39,17 @@ struct mipi_ctlr { uint mipi_cal_bias_pad_cfg0; uint mipi_cal_bias_pad_cfg1; uint mipi_cal_bias_pad_cfg2; + + uint mipi_cal_dsia_config_2; + uint mipi_cal_dsib_config_2; + uint mipi_cal_cilc_config_2; + uint mipi_cal_cild_config_2; + uint mipi_cal_csie_config_2; }; +#define MIPI_DSIA_PADS 0x60 +#define MIPI_DSIB_PADS 0x180 + #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) @@ -64,26 +74,25 @@ struct mipi_ctlr { #define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) #define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) +#define MIPI_CAL_HSCLKPDOSDSI(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_HSCLKPUOSDSI(x) (((x) & 0x1f) << 0) + struct tegra_mipi_priv { struct mipi_ctlr *mipi; struct clk *mipi_cal; + u32 version; }; -static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, - int size) +enum { + T114, + T124, +}; + +static void tegra114_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) { - 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); @@ -99,6 +108,95 @@ static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf value = readl(&priv->mipi->mipi_cal_config_dsid); value &= ~(MIPI_CAL_SEL(0x1)); writel(value, &priv->mipi->mipi_cal_config_dsid); +} + +static void tegra124_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) +{ + u32 value; + + /* Calibrate DSI-A */ + if (calibration_pads == MIPI_DSIA_PADS) { + printf("Calibrating DSI-A pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_dsia); + writel(value, &priv->mipi->mipi_cal_config_dsib); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + + /* Deselect PAD C */ + value = readl(&priv->mipi->mipi_cal_cilc_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + + /* Deselect PAD D */ + value = readl(&priv->mipi->mipi_cal_cild_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + } + + /* Calibrate DSI-B */ + if (calibration_pads == MIPI_DSIB_PADS) { + printf("Calibrating DSI-B pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_csic); + writel(value, &priv->mipi->mipi_cal_config_csid); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + + /* Deselect PAD A */ + value = readl(&priv->mipi->mipi_cal_dsia_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + + /* Deselect PAD B */ + value = readl(&priv->mipi->mipi_cal_dsib_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + } +} + +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); + + switch (priv->version) { + case T114: + tegra114_mipi_pads_cal(priv, offset); + break; + + case T124: + tegra124_mipi_pads_cal(priv, offset); + break; + + default: + return -EINVAL; + } value = readl(&priv->mipi->mipi_cal_ctrl); value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); @@ -134,6 +232,11 @@ static int tegra_mipi_enable(struct udevice *dev, bool val) struct tegra_mipi_priv *priv = dev_get_priv(dev); u32 value; + reset_set_enable(priv->mipi_cal->id, 1); + mdelay(100); + reset_set_enable(priv->mipi_cal->id, 0); + mdelay(1); + clk_enable(priv->mipi_cal); value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0); @@ -157,6 +260,8 @@ static int tegra_mipi_probe(struct udevice *dev) { struct tegra_mipi_priv *priv = dev_get_priv(dev); + priv->version = dev_get_driver_data(dev); + priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev); if (!priv->mipi) { log_debug("%s: no MIPI controller address\n", __func__); @@ -174,7 +279,8 @@ static int tegra_mipi_probe(struct udevice *dev) } static const struct udevice_id tegra_mipi_ids[] = { - { .compatible = "nvidia,tegra114-mipi" }, + { .compatible = "nvidia,tegra114-mipi", .data = T114 }, + { .compatible = "nvidia,tegra124-mipi", .data = T124 }, { } }; From e503be022f1eeabc51551071f37e2c006a6a9a14 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 18 Nov 2024 08:42:43 +0200 Subject: [PATCH 16/28] video: add TI LP855x backlight driver Add support for National Semiconductor/TI LP8550/1/2/3/5/6/7 LED Backlight. Driver is based on Linux version but is reworked and optimised for U-Boot DM framework. Currently only register driven backlight control is supported, PWM driver backlight control may be added later if needed. Signed-off-by: Svyatoslav Ryhel --- drivers/video/Kconfig | 10 + drivers/video/Makefile | 1 + drivers/video/lp855x_backlight.c | 302 +++++++++++++++++++++++++++++++ 3 files changed, 313 insertions(+) create mode 100644 drivers/video/lp855x_backlight.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c3cebaacd0..926dafe7a5a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -743,6 +743,16 @@ config BACKLIGHT_LM3533 LM3533 Lighting Power chip. Only Bank A is supported as for now. Supported backlight level range is from 2 to 255 with step of 1. +config BACKLIGHT_LP855x + bool "Backlight Driver for LP855x" + depends on BACKLIGHT + select DM_I2C + help + Say Y to enable the backlight driver for National Semiconductor / TI + LP8550/1/2/3/5/6/7 LED Backlight Driver. Only register driven mode is + supported for now, PWM mode can be added if there will be any need in + it. Supported backlight level range is from 0 to 255 with step of 1. + source "drivers/video/ti/Kconfig" source "drivers/video/exynos/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 5a00438ce06..391ef8397bc 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_$(PHASE_)BMP) += bmp.o endif obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o +obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o obj-${CONFIG_EXYNOS_FB} += exynos/ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ diff --git a/drivers/video/lp855x_backlight.c b/drivers/video/lp855x_backlight.c new file mode 100644 index 00000000000..5debc0aa453 --- /dev/null +++ b/drivers/video/lp855x_backlight.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 Texas Instruments + * Copyright (c) 2024 Svyatoslav Ryhel + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LP855x_MIN_BRIGHTNESS 0x00 +#define LP855x_MAX_BRIGHTNESS 0xff + +/* LP8550/1/2/3/6 Registers */ +#define LP855X_BRIGHTNESS_CTRL 0x00 +#define LP855X_DEVICE_CTRL 0x01 +#define LP855X_EEPROM_START 0xa0 +#define LP855X_EEPROM_END 0xa7 +#define LP8556_EPROM_START 0x98 +#define LP8556_EPROM_END 0xaf + +/* LP8555/7 Registers */ +#define LP8557_BL_CMD 0x00 +#define LP8557_BL_MASK 0x01 +#define LP8557_BL_ON 0x01 +#define LP8557_BL_OFF 0x00 +#define LP8557_BRIGHTNESS_CTRL 0x04 +#define LP8557_CONFIG 0x10 +#define LP8555_EPROM_START 0x10 +#define LP8555_EPROM_END 0x7a +#define LP8557_EPROM_START 0x10 +#define LP8557_EPROM_END 0x1e + +struct lp855x_rom_data { + u8 addr; + u8 val; +}; + +struct lp855x_backlight_priv; + +/* + * struct lp855x_device_config + * @pre_init_device: init device function call before updating the brightness + * @reg_brightness: register address for brigthenss control + * @reg_devicectrl: register address for device control + * @post_init_device: late init device function call + */ +struct lp855x_device_config { + int (*pre_init_device)(struct udevice *dev); + u8 reg_brightness; + u8 reg_devicectrl; + u8 reg_eepromstart; + u8 reg_eepromend; + int (*post_init_device)(struct udevice *dev); +}; + +struct lp855x_backlight_priv { + struct udevice *supply; /* regulator for VDD input */ + struct udevice *enable; /* regulator for EN/VDDIO input */ + + u8 device_control; + u8 initial_brightness; + + int size_program; + struct lp855x_rom_data *rom_data; + struct lp855x_device_config *cfg; +}; + +static int lp855x_backlight_enable(struct udevice *dev) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + int ret; + + ret = regulator_set_enable_if_allowed(priv->supply, 1); + if (ret) { + log_debug("%s: enabling power-supply failed (%d)\n", + __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->enable, 1); + if (ret) { + log_debug("%s: enabling enable-supply failed (%d)\n", + __func__, ret); + return ret; + } + mdelay(2); + + if (priv->cfg->pre_init_device) { + ret = priv->cfg->pre_init_device(dev); + if (ret) { + log_debug("%s: pre init device err: %d\n", + __func__, ret); + return ret; + } + } + + ret = dm_i2c_reg_write(dev, priv->cfg->reg_brightness, + priv->initial_brightness); + if (ret) + return ret; + + ret = dm_i2c_reg_write(dev, priv->cfg->reg_devicectrl, + priv->device_control); + if (ret) + return ret; + + if (priv->size_program > 0) { + int i; + u8 val, addr; + + for (i = 0; i < priv->size_program; i++) { + addr = priv->rom_data[i].addr; + val = priv->rom_data[i].val; + + if (addr < priv->cfg->reg_eepromstart && + addr > priv->cfg->reg_eepromend) + continue; + + ret = dm_i2c_reg_write(dev, addr, val); + if (ret) + return ret; + } + } + + if (priv->cfg->post_init_device) { + ret = priv->cfg->post_init_device(dev); + if (ret) { + log_debug("%s: post init device err: %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static int lp855x_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + + if (percent == BACKLIGHT_DEFAULT) + percent = priv->initial_brightness; + + if (percent < LP855x_MIN_BRIGHTNESS) + percent = LP855x_MIN_BRIGHTNESS; + + if (percent > LP855x_MAX_BRIGHTNESS) + percent = LP855x_MAX_BRIGHTNESS; + + /* Set brightness level */ + return dm_i2c_reg_write(dev, priv->cfg->reg_brightness, + percent); +} + +static int lp855x_backlight_probe(struct udevice *dev) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + int rom_length, ret; + + if (device_get_uclass_id(dev->parent) != UCLASS_I2C) + return -EPROTONOSUPPORT; + + priv->cfg = (struct lp855x_device_config *)dev_get_driver_data(dev); + + dev_read_u8(dev, "dev-ctrl", &priv->device_control); + dev_read_u8(dev, "init-brt", &priv->initial_brightness); + + /* Fill ROM platform data if defined */ + rom_length = dev_get_child_count(dev); + if (rom_length > 0) { + struct lp855x_rom_data *rom; + ofnode child; + int i = 0; + + rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL); + if (!rom) + return -ENOMEM; + + dev_for_each_subnode(child, dev) { + ofnode_read_u8(child, "rom-addr", &rom[i].addr); + ofnode_read_u8(child, "rom-val", &rom[i].val); + i++; + } + + priv->size_program = rom_length; + priv->rom_data = &rom[0]; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "power-supply", &priv->supply); + if (ret) { + log_err("%s: cannot get power-supply: ret = %d\n", __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "enable-supply", &priv->enable); + if (ret) { + log_err("%s: cannot get enable-supply: ret = %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static const struct backlight_ops lp855x_backlight_ops = { + .enable = lp855x_backlight_enable, + .set_brightness = lp855x_backlight_set_brightness, +}; + +static int lp8556_bl_rst(struct udevice *dev) +{ + int ret; + + /* Reset backlight after updating EPROM settings */ + ret = dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK, + LP8557_BL_OFF); + if (ret) + return ret; + + mdelay(10); + + return dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK, + LP8557_BL_ON); +} + +static int lp8557_bl_off(struct udevice *dev) +{ + /* BL_ON = 0 before updating EPROM settings */ + return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_OFF); +} + +static int lp8557_bl_on(struct udevice *dev) +{ + /* BL_ON = 1 after updating EPROM settings */ + return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_ON); +} + +static struct lp855x_device_config lp855x_dev_cfg = { + .reg_brightness = LP855X_BRIGHTNESS_CTRL, + .reg_devicectrl = LP855X_DEVICE_CTRL, + .reg_eepromstart = LP855X_EEPROM_START, + .reg_eepromend = LP855X_EEPROM_END, +}; + +static struct lp855x_device_config lp8555_dev_cfg = { + .reg_brightness = LP8557_BRIGHTNESS_CTRL, + .reg_devicectrl = LP8557_CONFIG, + .reg_eepromstart = LP8555_EPROM_START, + .reg_eepromend = LP8555_EPROM_END, + .pre_init_device = lp8557_bl_off, + .post_init_device = lp8557_bl_on, +}; + +static struct lp855x_device_config lp8556_dev_cfg = { + .reg_brightness = LP855X_BRIGHTNESS_CTRL, + .reg_devicectrl = LP855X_DEVICE_CTRL, + .reg_eepromstart = LP8556_EPROM_START, + .reg_eepromend = LP8556_EPROM_END, + .post_init_device = lp8556_bl_rst, +}; + +static struct lp855x_device_config lp8557_dev_cfg = { + .reg_brightness = LP8557_BRIGHTNESS_CTRL, + .reg_devicectrl = LP8557_CONFIG, + .reg_eepromstart = LP8557_EPROM_START, + .reg_eepromend = LP8557_EPROM_END, + .pre_init_device = lp8557_bl_off, + .post_init_device = lp8557_bl_on, +}; + +static const struct udevice_id lp855x_backlight_ids[] = { + { .compatible = "ti,lp8550", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8551", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8552", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8553", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8555", .data = (ulong)&lp8555_dev_cfg }, + { .compatible = "ti,lp8556", .data = (ulong)&lp8556_dev_cfg }, + { .compatible = "ti,lp8557", .data = (ulong)&lp8557_dev_cfg }, + { } +}; + +U_BOOT_DRIVER(lp855x_backlight) = { + .name = "lp855x_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = lp855x_backlight_ids, + .probe = lp855x_backlight_probe, + .ops = &lp855x_backlight_ops, + .priv_auto = sizeof(struct lp855x_backlight_priv), +}; From 0298591857ffb1d5bef5b089fe17c39b589091e5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 18 Nov 2024 08:45:51 +0200 Subject: [PATCH 17/28] video: panel: add Sharp LQ079L1SX01 MIPI DSI panel driver This module is a color active matrix LCD module incorporating Oxide TFT (Thin Film Transistor). It is composed of a color TFT-LCD panel, driver ICs, a control circuit and power supply circuit, and a backlight unit. Graphics and texts can be displayed on a 1536x2048 dots panel with (16,777,216) colors by using MIPI DUAL DSI interface, supplying +3.3V DC supply voltage for TFT-LCD panel driving and supplying DC supply voltage for LED Backlight. Signed-off-by: Svyatoslav Ryhel --- drivers/video/Kconfig | 9 + drivers/video/Makefile | 1 + drivers/video/sharp-lq079l1sx01.c | 288 ++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 drivers/video/sharp-lq079l1sx01.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 926dafe7a5a..b1ef73f3e5c 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -599,6 +599,15 @@ config VIDEO_LCD_SAMSUNG_LTL106HL02 LCD module found in Microsoft Surface 2. The panel has a FullHD resolution (1920x1080). +config VIDEO_LCD_SHARP_LQ079L1SX01 + tristate "Sharp LQ079L1SX01 1536x2048 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for Sharp LQ079L1SX01 + LCD module found in Xiaomi Mi Pad tablet. The panel has a QXGA + resolution (1536x2048). + config VIDEO_LCD_SHARP_LQ101R1SX01 tristate "Sharp LQ101R1SX01 2560x1600 DSI video mode panel" depends on PANEL && BACKLIGHT diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 391ef8397bc..6073bc5234a 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R69328) += renesas-r69328.o obj-$(CONFIG_VIDEO_LCD_SAMSUNG_LTL106HL02) += samsung-ltl106hl02.o +obj-$(CONFIG_VIDEO_LCD_SHARP_LQ079L1SX01) += sharp-lq079l1sx01.o obj-$(CONFIG_VIDEO_LCD_SHARP_LQ101R1SX01) += sharp-lq101r1sx01.o obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o diff --git a/drivers/video/sharp-lq079l1sx01.c b/drivers/video/sharp-lq079l1sx01.c new file mode 100644 index 00000000000..a8197f40fc7 --- /dev/null +++ b/drivers/video/sharp-lq079l1sx01.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sharp LQ079L1SX01 DSI panel driver + * + * Copyright (c) 2024 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct sharp_lq079l1sx01_priv { + struct udevice *backlight; + struct udevice *panel_sec; + + struct udevice *avdd; + struct udevice *vddio; + struct udevice *vsp; + struct udevice *vsn; + + struct gpio_desc reset_gpio; +}; + +static struct display_timing default_timing = { + .pixelclock.typ = 215000000, + .hactive.typ = 1536, + .hfront_porch.typ = 136, + .hback_porch.typ = 28, + .hsync_len.typ = 28, + .vactive.typ = 2048, + .vfront_porch.typ = 14, + .vback_porch.typ = 8, + .vsync_len.typ = 2, +}; + +static int dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data) +{ + return mipi_dsi_dcs_write(dsi, cmd, &data, 1); +} + +static int sharp_lq079l1sx01_configure_link(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_debug("%s: failed to exit sleep mode %s: %d\n", + __func__, dev->parent->name, ret); + } + mdelay(120); + + ret = dcs_write_one(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff); + if (ret < 0) { + log_debug("%s: failed to SET_DISPLAY_BRIGHTNESS %s: %d\n", + __func__, dev->parent->name, ret); + } + ret = dcs_write_one(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x01); + if (ret < 0) { + log_debug("%s: failed to WRITE_POWER_SAVE %s: %d\n", + __func__, dev->parent->name, ret); + } + ret = dcs_write_one(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c); + if (ret < 0) { + log_debug("%s: failed to WRITE_CONTROL_DISPLAY %s: %d\n", + __func__, dev->parent->name, ret); + } + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + log_debug("%s: failed to set panel on %s: %d\n", + __func__, dev->parent->name, ret); + } + + return 0; +} + +static int sharp_lq079l1sx01_enable_backlight(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + + if (!priv->panel_sec) + return 0; + + sharp_lq079l1sx01_configure_link(dev); + sharp_lq079l1sx01_configure_link(priv->panel_sec); + + return 0; +} + +static int sharp_lq079l1sx01_set_backlight(struct udevice *dev, int percent) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + if (!priv->panel_sec) + return 0; + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + return backlight_set_brightness(priv->backlight, percent); +} + +static int sharp_lq079l1sx01_timings(struct udevice *dev, + struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int sharp_lq079l1sx01_of_to_plat(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + /* If node has no link2 it is secondary panel */ + if (!dev_read_bool(dev, "link2")) + return 0; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, + "link2", &priv->panel_sec); + if (ret) { + log_debug("%s: cannot get secondary panel: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_debug("%s: cannot get backlight: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "avdd-supply", &priv->avdd); + if (ret) { + log_debug("%s: cannot get avdd-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vddio-supply", &priv->vddio); + if (ret) { + log_debug("%s: cannot get vddio-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vsp-supply", &priv->vsp); + if (ret) { + log_debug("%s: cannot get vsp-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vsn-supply", &priv->vsn); + if (ret) { + log_debug("%s: cannot get vsn-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: cannot get reser-gpios (%d)\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int sharp_lq079l1sx01_hw_init(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + if (!priv->panel_sec) + return 0; + + ret = regulator_set_enable_if_allowed(priv->vddio, 1); + if (ret) { + log_debug("%s: enabling vddio-supply failed (%d)\n", + __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->avdd, 1); + if (ret) { + log_debug("%s: enabling avdd-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(12); + + ret = regulator_set_enable_if_allowed(priv->vsp, 1); + if (ret) { + log_debug("%s: enabling vsp-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(12); + + ret = regulator_set_enable_if_allowed(priv->vsn, 1); + if (ret) { + log_debug("%s: enabling vsn-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(24); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error disabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + udelay(3); + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_debug("%s: error enabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + udelay(3); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error disabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + mdelay(32); + + return 0; +} + +static int sharp_lq079l1sx01_probe(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + + /* fill characteristics of DSI data link */ + plat->lanes = 4; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; + + return sharp_lq079l1sx01_hw_init(dev); +} + +static const struct panel_ops sharp_lq079l1sx01_ops = { + .enable_backlight = sharp_lq079l1sx01_enable_backlight, + .set_backlight = sharp_lq079l1sx01_set_backlight, + .get_display_timing = sharp_lq079l1sx01_timings, +}; + +static const struct udevice_id sharp_lq079l1sx01_ids[] = { + { .compatible = "sharp,lq079l1sx01" }, + { } +}; + +U_BOOT_DRIVER(sharp_lq079l1sx01) = { + .name = "sharp_lq079l1sx01", + .id = UCLASS_PANEL, + .of_match = sharp_lq079l1sx01_ids, + .ops = &sharp_lq079l1sx01_ops, + .of_to_plat = sharp_lq079l1sx01_of_to_plat, + .probe = sharp_lq079l1sx01_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct sharp_lq079l1sx01_priv), +}; From 892e97c0e1bd4887957e025f90c5a3779176925f Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 21 Nov 2024 14:42:39 +0200 Subject: [PATCH 18/28] ARM: tegra124: implement BCT patching This function allows updating bootloader from u-boot on production devices without need in host PC. Be aware! It works only with re-crypt BCT and AES encrypted devices. Signed-off-by: Svyatoslav Ryhel --- arch/arm/mach-tegra/Kconfig | 2 +- arch/arm/mach-tegra/tegra124/Makefile | 1 + arch/arm/mach-tegra/tegra124/bct.c | 91 +++++++++++++++++++++++++++ arch/arm/mach-tegra/tegra124/bct.h | 55 ++++++++++++++++ 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-tegra/tegra124/bct.c create mode 100644 arch/arm/mach-tegra/tegra124/bct.h diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 78b89729f19..4690dcb3ea6 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -247,7 +247,7 @@ config CMD_ENTERRCM config CMD_EBTUPDATE bool "Enable 'ebtupdate' command" - depends on TEGRA20 || TEGRA30 + depends on TEGRA20 || TEGRA30 || TEGRA124 select TEGRA_CRYPTO help Updating u-boot from within u-boot in rather complex or even diff --git a/arch/arm/mach-tegra/tegra124/Makefile b/arch/arm/mach-tegra/tegra124/Makefile index dee790015a3..7b93db89c0f 100644 --- a/arch/arm/mach-tegra/tegra124/Makefile +++ b/arch/arm/mach-tegra/tegra124/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_XPL_BUILD) += cpu.o +obj-$(CONFIG_$(XPL_)CMD_EBTUPDATE) += bct.o obj-y += clock.o obj-y += pmc.o diff --git a/arch/arm/mach-tegra/tegra124/bct.c b/arch/arm/mach-tegra/tegra124/bct.c new file mode 100644 index 00000000000..a71aa87fce1 --- /dev/null +++ b/arch/arm/mach-tegra/tegra124/bct.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022, Ramin + * Copyright (c) 2022, Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include "bct.h" +#include "uboot_aes.h" + +/* Device with "sbk burned: false" will expose zero key */ +const u8 nosbk[AES128_KEY_LENGTH] = { 0 }; + +/* + * @param bct boot config table start in RAM + * @param ect bootloader start in RAM + * @param ebt_size bootloader file size in bytes + * Return: 0, or 1 if failed + */ +static int bct_patch(u8 *bct, u8 *ebt, u32 ebt_size) +{ + struct nvboot_config_table *bct_tbl = NULL; + u8 ebt_hash[AES128_KEY_LENGTH] = { 0 }; + u8 bct_hash[AES128_KEY_LENGTH] = { 0 }; + u8 sbk[AES128_KEY_LENGTH] = { 0 }; + u8 *sbct = bct + UBCT_LENGTH; + bool encrypted; + int ret; + + ebt_size = roundup(ebt_size, EBT_ALIGNMENT); + + memcpy(sbk, (u8 *)(bct + UBCT_LENGTH + SBCT_LENGTH), + NVBOOT_CMAC_AES_HASH_LENGTH * 4); + + encrypted = memcmp(&sbk, &nosbk, AES128_KEY_LENGTH); + + if (encrypted) { + ret = decrypt_data_block(sbct, SBCT_LENGTH, sbk); + if (ret) + return 1; + + ret = encrypt_data_block(ebt, ebt_size, sbk); + if (ret) + return 1; + } + + ret = sign_enc_data_block(ebt, ebt_size, ebt_hash, sbk); + if (ret) + return 1; + + bct_tbl = (struct nvboot_config_table *)bct; + + memcpy((u8 *)&bct_tbl->bootloader[0].crypto_hash, + ebt_hash, NVBOOT_CMAC_AES_HASH_LENGTH * 4); + bct_tbl->bootloader[0].entry_point = CONFIG_SPL_TEXT_BASE; + bct_tbl->bootloader[0].load_addr = CONFIG_SPL_TEXT_BASE; + bct_tbl->bootloader[0].length = ebt_size; + + if (encrypted) { + ret = encrypt_data_block(sbct, SBCT_LENGTH, sbk); + if (ret) + return 1; + } + + ret = sign_enc_data_block(sbct, SBCT_LENGTH, bct_hash, sbk); + if (ret) + return 1; + + memcpy((u8 *)&bct_tbl->crypto_hash, bct_hash, + NVBOOT_CMAC_AES_HASH_LENGTH * 4); + + return 0; +} + +static int do_ebtupdate(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 bct_addr = hextoul(argv[1], NULL); + u32 ebt_addr = hextoul(argv[2], NULL); + u32 ebt_size = hextoul(argv[3], NULL); + + return bct_patch((u8 *)bct_addr, (u8 *)ebt_addr, ebt_size); +} + +U_BOOT_CMD(ebtupdate, 4, 0, do_ebtupdate, + "update bootloader on re-crypted Tegra124 devices", + "" +); diff --git a/arch/arm/mach-tegra/tegra124/bct.h b/arch/arm/mach-tegra/tegra124/bct.h new file mode 100644 index 00000000000..eb0f712d595 --- /dev/null +++ b/arch/arm/mach-tegra/tegra124/bct.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _BCT_H_ +#define _BCT_H_ + +/* + * Defines the BCT parametres for T124 + */ +#define UBCT_LENGTH 0x6b0 /* bytes */ +#define SBCT_LENGTH 0x1950 /* bytes */ + +#define BCT_HASH 0x10 +#define EBT_ALIGNMENT 0x10 + +/* + * Defines the CMAC-AES-128 hash length in 32 bit words. (128 bits = 4 words) + */ +#define NVBOOT_CMAC_AES_HASH_LENGTH 4 + +/* + * Defines the RSA modulus length in 32 bit words used for PKC secure boot. + */ +#define NVBOOT_SE_RSA_MODULUS_LENGTH 64 + +/* + * Defines the maximum number of bootloader descriptions in the BCT. + */ +#define NVBOOT_MAX_BOOTLOADERS 4 + +struct nv_bootloader_info { + u32 version; + u32 start_blk; + u32 start_page; + u32 length; + u32 load_addr; + u32 entry_point; + u32 attribute; + + /* Specifies the AES-CMAC MAC or RSASSA-PSS signature of the BL. */ + u32 crypto_hash[NVBOOT_CMAC_AES_HASH_LENGTH]; + u32 bl_rsa_sig[NVBOOT_SE_RSA_MODULUS_LENGTH]; +}; + +struct nvboot_config_table { + u32 ubct_unused1[196]; + u32 crypto_hash[NVBOOT_CMAC_AES_HASH_LENGTH]; + u32 ubct_unused2[228]; + + u32 sbct_unused1[1318]; + u32 bootloader_used; + struct nv_bootloader_info bootloader[NVBOOT_MAX_BOOTLOADERS]; + u32 sbct_unused2; +}; + +#endif /* _BCT_H_ */ From d41adba55337b3f01b773ddda37be1be5b5b0054 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sat, 16 Nov 2024 14:33:47 +0200 Subject: [PATCH 19/28] board: xiaomi: mocha: add Xiaomi Mi Pad A0101 support The Mi Pad is a tablet computer based on Nvidia Tegra K1 SoC which originally ran the Android operating system. The Mi Pad has a 7.9" IPS display with 1536 x 2048 (324 ppi) resolution. 2 GB of RAM and 16/64 GB of internal memory that can be supplemented with a microSDXC card giving up to 128 GB of additional storage. Signed-off-by: Svyatoslav Ryhel --- arch/arm/dts/Makefile | 1 + arch/arm/dts/tegra124-xiaomi-mocha.dts | 592 +++++++++++++++++++++++++ arch/arm/mach-tegra/tegra124/Kconfig | 5 + board/xiaomi/mocha/Kconfig | 12 + board/xiaomi/mocha/MAINTAINERS | 8 + board/xiaomi/mocha/Makefile | 9 + board/xiaomi/mocha/mocha-spl.c | 49 ++ board/xiaomi/mocha/mocha.c | 41 ++ board/xiaomi/mocha/mocha.env | 23 + configs/mocha_defconfig | 91 ++++ doc/board/index.rst | 1 + doc/board/xiaomi/index.rst | 9 + doc/board/xiaomi/mocha.rst | 112 +++++ include/configs/mocha.h | 25 ++ 14 files changed, 978 insertions(+) create mode 100644 arch/arm/dts/tegra124-xiaomi-mocha.dts create mode 100644 board/xiaomi/mocha/Kconfig create mode 100644 board/xiaomi/mocha/MAINTAINERS create mode 100644 board/xiaomi/mocha/Makefile create mode 100644 board/xiaomi/mocha/mocha-spl.c create mode 100644 board/xiaomi/mocha/mocha.c create mode 100644 board/xiaomi/mocha/mocha.env create mode 100644 configs/mocha_defconfig create mode 100644 doc/board/xiaomi/index.rst create mode 100644 doc/board/xiaomi/mocha.rst create mode 100644 include/configs/mocha.h diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 267b0179a5f..6096781cc44 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -121,6 +121,7 @@ dtb-$(CONFIG_ARCH_TEGRA) += \ tegra124-nyan-big.dtb \ tegra124-cei-tk1-som.dtb \ tegra124-venice2.dtb \ + tegra124-xiaomi-mocha.dtb \ tegra186-p2771-0000-000.dtb \ tegra186-p2771-0000-500.dtb \ tegra210-p2371-0000.dtb \ diff --git a/arch/arm/dts/tegra124-xiaomi-mocha.dts b/arch/arm/dts/tegra124-xiaomi-mocha.dts new file mode 100644 index 00000000000..6cb1781566f --- /dev/null +++ b/arch/arm/dts/tegra124-xiaomi-mocha.dts @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; + +#include +#include "tegra124.dtsi" + +/ { + model = "Xiaomi Mi Pad A0101"; + compatible = "xiaomi,mocha", "nvidia,tegra124"; + + chosen { + stdout-path = &uartd; + }; + + aliases { + i2c0 = &pwr_i2c; + i2c1 = &gen1_i2c; + + mmc0 = &sdmmc4; /* eMMC */ + mmc1 = &sdmmc3; /* uSD slot */ + + usb0 = &usb1; + }; + + memory { + device_type = "memory"; + reg = <0x80000000 0x80000000>; + }; + + host1x@50000000 { + dsia: dsi@54300000 { + status = "okay"; + + avdd-dsi-csi-supply = <&avdd_dsi_csi>; + nvidia,ganged-mode = <&dsib>; + + panel@0 { + compatible = "sharp,lq079l1sx01"; + reg = <0>; + + link2 = <&panel_secondary>; + + avdd-supply = <&avdd_lcd>; + vddio-supply = <&vdd_lcd_io>; + + vsp-supply = <&vsp_5v5_lcd>; + vsn-supply = <&vsn_5v5_lcd>; + + reset-gpios = <&gpio TEGRA_GPIO(H, 3) GPIO_ACTIVE_LOW>; + + backlight = <&lp8556>; + }; + }; + + dsib: dsi@54400000 { + status = "okay"; + + avdd-dsi-csi-supply = <&avdd_dsi_csi>; + + panel_secondary: panel@0 { + compatible = "sharp,lq079l1sx01"; + reg = <0>; + }; + }; + }; + + pinmux@70000868 { + pinctrl-names = "default"; + pinctrl-0 = <&state_default>; + + state_default: pinmux { + /* Keys pinmux */ + keys { + nvidia,pins = "kb_col0_pq0", + "kb_col6_pq6", + "kb_col7_pq7"; + nvidia,function = "rsvd2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + hall-front { + nvidia,pins = "pi5"; + nvidia,function = "gmi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + hall-back { + nvidia,pins = "gpio_w3_aud_pw3"; + nvidia,function = "spi1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Leds pinmux */ + bl-en { + nvidia,pins = "pbb4"; + nvidia,function = "vgp4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + keys-led { + nvidia,pins = "ph1"; + nvidia,function = "pwm1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* Panel pinmux */ + lcd-rst { + nvidia,pins = "ph3"; + nvidia,function = "gmi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + lcd-vsp { + nvidia,pins = "pi4"; + nvidia,function = "gmi"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + lcd-vsn { + nvidia,pins = "kb_row10_ps2"; + nvidia,function = "kbc"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + lcd-id { + nvidia,pins = "kb_row6_pr6"; + nvidia,function = "displaya_alt"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + lcd-pwm { + nvidia,pins = "ph2"; + nvidia,function = "pwm2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* SDMMC3 pinmux */ + sdmmc3-clk { + nvidia,pins = "sdmmc3_clk_pa6"; + nvidia,function = "sdmmc3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc3-cmd { + nvidia,pins = "sdmmc3_cmd_pa7", + "sdmmc3_dat0_pb7", + "sdmmc3_dat1_pb6", + "sdmmc3_dat2_pb5", + "sdmmc3_dat3_pb4", + "sdmmc3_clk_lb_out_pee4", + "sdmmc3_clk_lb_in_pee5"; + nvidia,function = "sdmmc3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc3-cd { + nvidia,pins = "sdmmc3_cd_n_pv2"; + nvidia,function = "sdmmc3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + usd-pwr { + nvidia,pins = "kb_row0_pr0"; + nvidia,function = "rsvd4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* SDMMC4 pinmux */ + sdmmc4-clk { + nvidia,pins = "sdmmc4_clk_pcc4"; + nvidia,function = "sdmmc4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + sdmmc4-cmd { + nvidia,pins = "sdmmc4_cmd_pt7", + "sdmmc4_dat0_paa0", + "sdmmc4_dat1_paa1", + "sdmmc4_dat2_paa2", + "sdmmc4_dat3_paa3", + "sdmmc4_dat4_paa4", + "sdmmc4_dat5_paa5", + "sdmmc4_dat6_paa6", + "sdmmc4_dat7_paa7"; + nvidia,function = "sdmmc4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + + /* I2C pinmux */ + gen1-i2c { + nvidia,pins = "gen1_i2c_sda_pc5", + "gen1_i2c_scl_pc4"; + nvidia,function = "i2c1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <1>; + nvidia,open-drain = <1>; + }; + gen2-i2c { + nvidia,pins = "gen2_i2c_scl_pt5", + "gen2_i2c_sda_pt6"; + nvidia,function = "i2c2"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <1>; + nvidia,open-drain = <1>; + }; + cam-i2c { + nvidia,pins = "cam_i2c_scl_pbb1", + "cam_i2c_sda_pbb2"; + nvidia,function = "i2c3"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,lock = <1>; + nvidia,open-drain = <1>; + }; + ddc-i2c { + nvidia,pins = "ddc_scl_pv4", + "ddc_sda_pv5"; + nvidia,function = "i2c4"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + }; + pwr-i2c { + nvidia,pins = "pwr_i2c_scl_pz6", + "pwr_i2c_sda_pz7"; + nvidia,function = "i2cpwr"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = <1>; + }; + + dsi-b { + nvidia,pins = "mipi_pad_ctrl_dsi_b"; + nvidia,function = "dsi_b"; + }; + + /* GPIO power/drive control */ + drive-sdio1 { + nvidia,pins = "drive_sdio1"; + nvidia,high-speed-mode = ; + nvidia,schmitt = ; + nvidia,low-power-mode = ; + nvidia,pull-down-strength = <32>; + nvidia,pull-up-strength = <42>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + + drive-sdio3 { + nvidia,pins = "drive_sdio3"; + nvidia,high-speed-mode = ; + nvidia,schmitt = ; + nvidia,low-power-mode = ; + nvidia,pull-down-strength = <20>; + nvidia,pull-up-strength = <36>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + + drive-gma { + nvidia,pins = "drive_gma"; + nvidia,high-speed-mode = ; + nvidia,schmitt = ; + nvidia,low-power-mode = ; + nvidia,pull-down-strength = <1>; + nvidia,pull-up-strength = <2>; + nvidia,slew-rate-rising = ; + nvidia,slew-rate-falling = ; + }; + }; + }; + + uartd: serial@70006300 { + status = "okay"; + }; + + gen1_i2c: i2c@7000c000 { + status = "okay"; + clock-frequency = <400000>; + + lp8556: backlight@2c { + compatible = "ti,lp8556"; + reg = <0x2c>; + + dev-ctrl = /bits/ 8 <0x83>; + init-brt = /bits/ 8 <0x1f>; + + power-supply = <&vdd_3v3_sys>; + enable-supply = <&vddio_1v8_bl>; + + rom-98h { + rom-addr = /bits/ 8 <0x98>; + rom-val = /bits/ 8 <0x80>; + }; + + rom-9eh { + rom-addr = /bits/ 8 <0x9e>; + rom-val = /bits/ 8 <0x21>; + }; + + rom-a0h { + rom-addr = /bits/ 8 <0xa0>; + rom-val = /bits/ 8 <0xff>; + }; + + rom-a1h { + rom-addr = /bits/ 8 <0xa1>; + rom-val = /bits/ 8 <0x3f>; + }; + + rom-a2h { + rom-addr = /bits/ 8 <0xa2>; + rom-val = /bits/ 8 <0x20>; + }; + + rom-a3h { + rom-addr = /bits/ 8 <0xa3>; + rom-val = /bits/ 8 <0x00>; + }; + + rom-a4h { + rom-addr = /bits/ 8 <0xa4>; + rom-val = /bits/ 8 <0x72>; + }; + + rom-a5h { + rom-addr = /bits/ 8 <0xa5>; + rom-val = /bits/ 8 <0x24>; + }; + + rom-a6h { + rom-addr = /bits/ 8 <0xa6>; + rom-val = /bits/ 8 <0x80>; + }; + + rom-a7h { + rom-addr = /bits/ 8 <0xa7>; + rom-val = /bits/ 8 <0xf5>; + }; + + rom-a8h { + rom-addr = /bits/ 8 <0xa8>; + rom-val = /bits/ 8 <0x24>; + }; + + rom-a9h { + rom-addr = /bits/ 8 <0xa9>; + rom-val = /bits/ 8 <0xb2>; + }; + + rom-aah { + rom-addr = /bits/ 8 <0xaa>; + rom-val = /bits/ 8 <0x8f>; + }; + + rom-aeh { + rom-addr = /bits/ 8 <0xae>; + rom-val = /bits/ 8 <0x0f>; + }; + }; + }; + + pwr_i2c: i2c@7000d000 { + status = "okay"; + clock-frequency = <400000>; + + /* Texas Instruments TPS65913 PMIC */ + pmic: tps65913@58 { + compatible = "ti,tps65913"; + reg = <0x58>; + + interrupts = ; + #interrupt-cells = <2>; + interrupt-controller; + + ti,system-power-controller; + + palmas_gpio: gpio { + compatible = "ti,palmas-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; + + pinmux { + compatible = "ti,tps65913-pinctrl"; + + pinctrl-names = "default"; + pinctrl-0 = <&palmas_default>; + + palmas_default: pinmux { + pin_gpio4 { + pins = "gpio4"; + function = "gpio"; + }; + }; + }; + + pmic { + compatible = "ti,tps65913-pmic"; + + regulators { + vdd_1v8_vio: smps8 { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + vdd_hv_sdmmc: smps9 { + regulator-name = "vdd_hv_sdmmc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + avdd_lcd: ldo2 { + regulator-name = "avdd_lcd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + + avdd_dsi_csi: ldo5 { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + + vddio_usd: ldo9 { + regulator-name = "vddio_sdmmc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + avdd_usb: ldousb { + regulator-name = "vdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; + }; + }; + + sdmmc3: sdhci@700b0400 { + status = "okay"; + bus-width = <4>; + + cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_HIGH>; + power-gpios = <&gpio TEGRA_GPIO(R, 0) GPIO_ACTIVE_HIGH>; + + vmmc-supply = <&vdd_hv_sdmmc>; + vqmmc-supply = <&vddio_usd>; + }; + + sdmmc4: sdhci@700b0600 { + status = "okay"; + bus-width = <8>; + non-removable; + + vmmc-supply = <&vdd_hv_sdmmc>; + vqmmc-supply = <&vdd_1v8_vio>; + }; + + usb1: usb@7d000000 { + status = "okay"; + dr_mode = "otg"; + }; + + clk32k_in: clock-32k { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "ref-oscillator"; + }; + + extcon-keys { + compatible = "gpio-keys"; + + switch-back-hall-sensor { + label = "Hall sensor (back)"; + gpios = <&gpio TEGRA_GPIO(W, 3) GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + switch-front-hall-sensor { + label = "Hall sensor (front)"; + gpios = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + key-power { + label = "Power"; + gpios = <&gpio TEGRA_GPIO(Q, 0) GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + key-volume-down { + label = "Volume Down"; + gpios = <&gpio TEGRA_GPIO(Q, 7) GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + key-volume-up { + label = "Volume Up"; + gpios = <&gpio TEGRA_GPIO(Q, 6) GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + vdd_3v3_sys: regulator-bl-en { + compatible = "regulator-fixed"; + regulator-name = "vdd_5v0_bl"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-high; + }; + + vddio_1v8_bl: regulator-bl-io { + compatible = "regulator-fixed"; + regulator-name = "vddio_1v8_bl"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(BB, 4) GPIO_ACTIVE_HIGH>; + }; + + vdd_lcd_io: regulator-lcdvio { + compatible = "regulator-fixed"; + regulator-name = "dvdd_lcd"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + enable-active-high; + gpio = <&palmas_gpio 4 GPIO_ACTIVE_HIGH>; + }; + + vsp_5v5_lcd: regulator-vsp { + compatible = "regulator-fixed"; + regulator-name = "avdd_lcd_vsp"; + regulator-min-microvolt = <5500000>; + regulator-max-microvolt = <5500000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(I, 4) GPIO_ACTIVE_HIGH>; + }; + + vsn_5v5_lcd: regulator-vsn { + compatible = "regulator-fixed"; + regulator-name = "avdd_lcd_vsn"; + regulator-min-microvolt = <5500000>; + regulator-max-microvolt = <5500000>; + enable-active-high; + gpio = <&gpio TEGRA_GPIO(S, 2) GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/arch/arm/mach-tegra/tegra124/Kconfig b/arch/arm/mach-tegra/tegra124/Kconfig index 84c8f86bad0..a62b055f7e6 100644 --- a/arch/arm/mach-tegra/tegra124/Kconfig +++ b/arch/arm/mach-tegra/tegra124/Kconfig @@ -30,6 +30,10 @@ config TARGET_CEI_TK1_SOM the SoC are assigned to which functions, and the PCIEe configuration. +config TARGET_MOCHA + bool "Xiaomi Tegra124 Mi Pad board" + select BOARD_LATE_INIT + config TARGET_NYAN_BIG bool "Google/NVIDIA Nyan-big Chromebook" select BOARD_LATE_INIT @@ -54,5 +58,6 @@ source "board/nvidia/jetson-tk1/Kconfig" source "board/nvidia/nyan-big/Kconfig" source "board/nvidia/venice2/Kconfig" source "board/toradex/apalis-tk1/Kconfig" +source "board/xiaomi/mocha/Kconfig" endif diff --git a/board/xiaomi/mocha/Kconfig b/board/xiaomi/mocha/Kconfig new file mode 100644 index 00000000000..25c61d4169e --- /dev/null +++ b/board/xiaomi/mocha/Kconfig @@ -0,0 +1,12 @@ +if TARGET_MOCHA + +config SYS_BOARD + default "mocha" + +config SYS_VENDOR + default "xiaomi" + +config SYS_CONFIG_NAME + default "mocha" + +endif diff --git a/board/xiaomi/mocha/MAINTAINERS b/board/xiaomi/mocha/MAINTAINERS new file mode 100644 index 00000000000..c3871a15a35 --- /dev/null +++ b/board/xiaomi/mocha/MAINTAINERS @@ -0,0 +1,8 @@ +MOCHA BOARD +M: Svyatoslav Ryhel +S: Maintained +F: arch/arm/dts/tegra124-xiaomi-mocha.dts +F: board/xiaomi/mocha/ +F: configs/mocha_defconfig +F: doc/board/xiaomi/mocha.rst +F: include/configs/mocha.h diff --git a/board/xiaomi/mocha/Makefile b/board/xiaomi/mocha/Makefile new file mode 100644 index 00000000000..c42e42639b3 --- /dev/null +++ b/board/xiaomi/mocha/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2024, Svyatoslav Ryhel +# + +obj-$(CONFIG_XPL_BUILD) += mocha-spl.o + +obj-y += mocha.o + diff --git a/board/xiaomi/mocha/mocha-spl.c b/board/xiaomi/mocha/mocha-spl.c new file mode 100644 index 00000000000..5fb11df0a93 --- /dev/null +++ b/board/xiaomi/mocha/mocha-spl.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Mocha SPL stage configuration + * + * (C) Copyright 2024 + * Svyatoslav Ryhel + */ + +#include +#include +#include + +#define TPS65913_I2C_ADDR (0x58 << 1) + +#define TPS65913_SMPS12_CTRL 0x20 +#define TPS65913_SMPS12_VOLTAGE 0x23 +#define TPS65913_SMPS45_CTRL 0x28 +#define TPS65913_SMPS45_VOLTAGE 0x2B +#define TPS65913_SMPS7_CTRL 0x30 +#define TPS65913_SMPS7_VOLTAGE 0x33 + +#define TPS65913_SMPS12_CTRL_DATA (0x5100 | TPS65913_SMPS12_CTRL) +#define TPS65913_SMPS12_VOLTAGE_DATA (0x3800 | TPS65913_SMPS12_VOLTAGE) +#define TPS65913_SMPS45_CTRL_DATA (0x5100 | TPS65913_SMPS45_CTRL) +#define TPS65913_SMPS45_VOLTAGE_DATA (0x3800 | TPS65913_SMPS45_VOLTAGE) +#define TPS65913_SMPS7_CTRL_DATA (0x5100 | TPS65913_SMPS7_CTRL) +#define TPS65913_SMPS7_VOLTAGE_DATA (0x4700 | TPS65913_SMPS7_VOLTAGE) + +void pmic_enable_cpu_vdd(void) +{ + /* Set CORE VDD to 1.150V. */ + tegra_i2c_ll_write(TPS65913_I2C_ADDR, TPS65913_SMPS7_VOLTAGE_DATA); + udelay(1000); + tegra_i2c_ll_write(TPS65913_I2C_ADDR, TPS65913_SMPS7_CTRL_DATA); + + udelay(1000); + + /* Set CPU VDD to 1.0V. */ + tegra_i2c_ll_write(TPS65913_I2C_ADDR, TPS65913_SMPS12_VOLTAGE_DATA); + udelay(1000); + tegra_i2c_ll_write(TPS65913_I2C_ADDR, TPS65913_SMPS12_CTRL_DATA); + udelay(10 * 1000); + + /* Set GPU VDD to 1.0V. */ + tegra_i2c_ll_write(TPS65913_I2C_ADDR, TPS65913_SMPS45_VOLTAGE_DATA); + udelay(1000); + tegra_i2c_ll_write(TPS65913_I2C_ADDR, TPS65913_SMPS45_CTRL_DATA); + udelay(10 * 1000); +} diff --git a/board/xiaomi/mocha/mocha.c b/board/xiaomi/mocha/mocha.c new file mode 100644 index 00000000000..5026d541a5f --- /dev/null +++ b/board/xiaomi/mocha/mocha.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2024 + * Svyatoslav Ryhel + */ + +#include +#include +#include +#include + +#ifdef CONFIG_MMC_SDHCI_TEGRA + +#define TPS65913_I2C_ADDRESS 0x58 +#define TPS65913_PRIMARY_SECONDARY_PAD2 0xfb +#define GPIO_4 BIT(0) +#define TPS65913_PRIMARY_SECONDARY_PAD3 0xfe +#define DVFS2 BIT(1) +#define DVFS1 BIT(0) + +/* We are using this function only till palmas pinctrl driver is available */ +void pin_mux_mmc(void) +{ + struct udevice *dev; + int ret; + + ret = i2c_get_chip_for_busnum(0, TPS65913_I2C_ADDRESS, 1, &dev); + if (ret) { + log_debug("%s: cannot find PMIC I2C chip\n", __func__); + return; + } + + /* GPIO4 function has to be GPIO */ + dm_i2c_reg_clrset(dev, TPS65913_PRIMARY_SECONDARY_PAD2, + GPIO_4, 0); + + /* DVFS1 and DVFS2 are disabled */ + dm_i2c_reg_clrset(dev, TPS65913_PRIMARY_SECONDARY_PAD3, + DVFS2 | DVFS1, 0); +} +#endif diff --git a/board/xiaomi/mocha/mocha.env b/board/xiaomi/mocha/mocha.env new file mode 100644 index 00000000000..d93e24316f6 --- /dev/null +++ b/board/xiaomi/mocha/mocha.env @@ -0,0 +1,23 @@ +#include + +button_cmd_0_name=Volume Down +button_cmd_0=bootmenu +button_cmd_1_name=Hall sensor (back) +button_cmd_1=poweroff +button_cmd_1_name=Hall sensor (front) +button_cmd_1=poweroff + +partitions=name=emmc,start=0,size=-,uuid=${uuid_gpt_rootfs} + +boot_block_size_r=0x400000 +boot_block_size=0x2000 +boot_dev=1 + +bootmenu_0=mount internal storage=usb start && ums 0 mmc 0; bootmenu +bootmenu_1=mount external storage=usb start && ums 0 mmc 1; bootmenu +bootmenu_2=fastboot=echo Starting Fastboot protocol ...; fastboot usb 0; bootmenu +bootmenu_3=update bootloader=run flash_uboot +bootmenu_4=reboot RCM=enterrcm +bootmenu_5=reboot=reset +bootmenu_6=power off=poweroff +bootmenu_delay=-1 diff --git a/configs/mocha_defconfig b/configs/mocha_defconfig new file mode 100644 index 00000000000..0cbedb21797 --- /dev/null +++ b/configs/mocha_defconfig @@ -0,0 +1,91 @@ +CONFIG_ARM=y +CONFIG_ARCH_TEGRA=y +CONFIG_SUPPORT_PASSING_ATAGS=y +CONFIG_CMDLINE_TAG=y +CONFIG_INITRD_TAG=y +CONFIG_TEXT_BASE=0x80110000 +CONFIG_SYS_MALLOC_LEN=0x2500000 +CONFIG_NR_DRAM_BANKS=2 +CONFIG_ENV_SOURCE_FILE="mocha" +CONFIG_ENV_SIZE=0x3000 +CONFIG_ENV_OFFSET=0xFFFFD000 +CONFIG_DEFAULT_DEVICE_TREE="tegra124-xiaomi-mocha" +CONFIG_SPL_STACK=0x800ffffc +CONFIG_SPL_TEXT_BASE=0x80108000 +CONFIG_SYS_LOAD_ADDR=0x82000000 +CONFIG_TEGRA124=y +CONFIG_TARGET_MOCHA=y +CONFIG_TEGRA_ENABLE_UARTD=y +CONFIG_CMD_EBTUPDATE=y +CONFIG_TEGRA_GPU=y +CONFIG_BUTTON_CMD=y +CONFIG_BOOTDELAY=0 +CONFIG_AUTOBOOT_KEYED=y +CONFIG_AUTOBOOT_KEYED_CTRLC=y +CONFIG_OF_SYSTEM_SETUP=y +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" +CONFIG_SYS_PBSIZE=2086 +CONFIG_SPL_FOOTPRINT_LIMIT=y +CONFIG_SPL_MAX_FOOTPRINT=0x8000 +# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set +CONFIG_SPL_SYS_MALLOC=y +CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y +CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x80090000 +CONFIG_SPL_SYS_MALLOC_SIZE=0x10000 +CONFIG_SYS_PROMPT="Tegra124 (Mocha) # " +# CONFIG_CMD_BOOTEFI_BOOTMGR is not set +CONFIG_CMD_BOOTMENU=y +# CONFIG_CMD_IMI is not set +CONFIG_CMD_GPIO=y +CONFIG_CMD_GPT=y +CONFIG_CMD_GPT_RENAME=y +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_POWEROFF=y +CONFIG_CMD_USB=y +CONFIG_CMD_USB_MASS_STORAGE=y +CONFIG_CMD_UMS_ABORT_KEYED=y +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_PAUSE=y +CONFIG_CMD_REGULATOR=y +CONFIG_CMD_EXT4_WRITE=y +# CONFIG_SPL_DOS_PARTITION is not set +# CONFIG_SPL_EFI_PARTITION is not set +CONFIG_ENV_OVERWRITE=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_SYS_MMC_ENV_PART=2 +CONFIG_BUTTON=y +CONFIG_USB_FUNCTION_FASTBOOT=y +CONFIG_FASTBOOT_BUF_ADDR=0x91000000 +CONFIG_FASTBOOT_BUF_SIZE=0x10000000 +CONFIG_FASTBOOT_FLASH=y +CONFIG_FASTBOOT_FLASH_MMC_DEV=0 +CONFIG_FASTBOOT_CMD_OEM_FORMAT=y +CONFIG_PALMAS_GPIO=y +CONFIG_SYS_I2C_TEGRA=y +CONFIG_BUTTON_KEYBOARD=y +CONFIG_DM_PMIC=y +CONFIG_PMIC_PALMAS=y +CONFIG_DM_REGULATOR=y +CONFIG_DM_REGULATOR_FIXED=y +CONFIG_DM_REGULATOR_PALMAS=y +CONFIG_PWM_TEGRA=y +CONFIG_SYS_NS16550=y +CONFIG_SYSRESET_PALMAS=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_TEGRA=y +CONFIG_USB_KEYBOARD=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_MANUFACTURER="Xiaomi" +CONFIG_USB_GADGET_VENDOR_NUM=0x18d1 +CONFIG_USB_GADGET_PRODUCT_NUM=0xd00d +CONFIG_CI_UDC=y +CONFIG_VIDEO=y +CONFIG_VIDEO_BRIDGE=y +# CONFIG_VIDEO_FONT_8X16 is not set +CONFIG_VIDEO_FONT_16X32=y +# CONFIG_VIDEO_LOGO is not set +CONFIG_VIDEO_LCD_SHARP_LQ079L1SX01=y +CONFIG_BACKLIGHT_LP855x=y +CONFIG_VIDEO_DSI_TEGRA30=y diff --git a/doc/board/index.rst b/doc/board/index.rst index b055046e649..84c135e02c1 100644 --- a/doc/board/index.rst +++ b/doc/board/index.rst @@ -69,4 +69,5 @@ Board-specific doc variscite/index wexler/index xen/index + xiaomi/index xilinx/index diff --git a/doc/board/xiaomi/index.rst b/doc/board/xiaomi/index.rst new file mode 100644 index 00000000000..109ab4a251f --- /dev/null +++ b/doc/board/xiaomi/index.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Xiaomi +====== + +.. toctree:: + :maxdepth: 2 + + mocha diff --git a/doc/board/xiaomi/mocha.rst b/doc/board/xiaomi/mocha.rst new file mode 100644 index 00000000000..be3e333127b --- /dev/null +++ b/doc/board/xiaomi/mocha.rst @@ -0,0 +1,112 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +U-Boot for the Xiaomi Mi Pad tablet +=================================== + +``DISCLAMER!`` Moving your Xiaomi Mi Pad to use U-Boot assumes replacement +of the vendor bootloader. Vendor Android firmwares will no longer be able +to run on the device. This replacement IS reversible. + +Quick Start +----------- + +- Build U-Boot +- Boot U-Boot +- Process and flash U-Boot +- Boot Linux +- Self Upgrading +- Chainload configuration + +Build U-Boot +------------ + +.. code-block:: bash + + $ export CROSS_COMPILE=arm-none-eabi- + $ make mocha_defconfig + $ make + +After the build succeeds, you will obtain the final ``u-boot-dtb-tegra.bin`` +image, ready for booting or further processing. + +Boot U-Boot +----------- +Existing tegrarcm loader can be used to pre-load U-Boot you have build +into RAM and basically perform a tethered cold-boot. + +.. code-block:: bash + + $ tegrarcm --bct mocha.bct --bootloader u-boot-dtb-tegra.bin --loadaddr 0x80108000 + +U-Boot will try to load Linux kernel and if fails, it will turn the +tablet off. While pre-loading U-Boot, hold the ``volume down`` button +which will trigger the bootmenu. + +Process and flash U-Boot +------------------------ + +``DISCLAMER!`` All questions related to the re-crypt work should be asked +in re-crypt repo issues. NOT HERE! + +re-crypt is a tool that processes the ``u-boot-dtb-tegra.bin`` binary into +form usable by device. This process is required only on the first +installation or to recover the device in case of a failed update. + +.. code-block:: bash + + $ git clone https://gitlab.com/grate-driver/re-crypt.git + $ cd re-crypt # place your u-boot-dtb-tegra.bin here + $ ./re-crypt.py --dev mocha + +The script will produce ``bct.img`` and ``ebt.img`` ready to flash. + +Permanent installation can be performed by pre-loading just built U-Boot +into RAM via tegrarcm. While pre-loading U-Boot, hold the ``volume down`` +button which will trigger the bootmenu. There, select ``fastboot`` using +the volume and power buttons. + +After, on host PC, do: + +.. code-block:: bash + + $ fastboot flash 0.1 bct.img + $ fastboot flash 0.2 ebt.img + $ fastboot reboot + +Device will reboot. + +Boot Linux +---------- + +To boot Linux, U-Boot will look for an ``extlinux.conf`` on MicroSD and then on +eMMC. Additionally, if the ``volume down`` button is pressed while booting, the +device will enter bootmenu. Bootmenu contains entries to mount MicroSD and eMMC +as mass storage, fastboot, reboot, reboot RCM, poweroff, enter U-Boot console +and update bootloader (check the next chapter). + +Flashing ``bct.img`` and ``ebt.img`` eliminates vendor restrictions on eMMC and +allows the user to use/partition it in any way the user desires. + +Self Upgrading +-------------- + +Place your ``u-boot-dtb-tegra.bin`` on the first partition of the MicroSD card +and insert it into the tablet. Enter bootmenu, choose update the bootloader +option with the Power button and U-Boot should update itself. Once the process +is completed, U-Boot will ask to press any button to reboot. + +Chainload configuration +----------------------- + +To build U-Boot without SPL suitable for chainloading adjust mocha_defconfig: + +.. code-block:: + + CONFIG_TEXT_BASE=0x80A00000 + CONFIG_SKIP_LOWLEVEL_INIT=y + # CONFIG_OF_BOARD_SETUP is not set + CONFIG_TEGRA_SUPPORT_NON_SECURE=y + +After the build succeeds, you will obtain the final ``u-boot-dtb.bin`` +file, ready for booting using vendor bootloader's fastboot or which can be +further processed into a flashable image. diff --git a/include/configs/mocha.h b/include/configs/mocha.h new file mode 100644 index 00000000000..1c2eb906085 --- /dev/null +++ b/include/configs/mocha.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2010-2013, NVIDIA CORPORATION. All rights reserved. + * + * Copyright (c) 2024, Svyatoslav Ryhel + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#include "tegra124-common.h" + +/* High-level configuration options */ +#define CFG_TEGRA_BOARD_STRING "Xiaomi Mocha" + +/* Board-specific serial config */ +#define CFG_SYS_NS16550_COM1 NV_PA_APB_UARTD_BASE + +#ifdef CONFIG_TEGRA_SUPPORT_NON_SECURE + #define CFG_PRAM 0x38400 /* 225 MB */ +#endif + +#include "tegra-common-post.h" + +#endif /* __CONFIG_H */ From 8fb7ed59a8fa6d73a5d4ee9ec65c521de3ed1f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Schw=C3=B6bel?= Date: Mon, 3 Mar 2025 13:31:07 +0200 Subject: [PATCH 20/28] common: edid: update timing selection logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older EDID timing algorithms relied solely on detailed timings, typically optimized for a display's native resolution. This caused issues with newer 4K panels on older hardware, which couldn't handle those high resolutions. To address this, the algorithm now also considers standard timings, offering lower, compatible resolutions. Future improvements may include checking established timings for even broader compatibility. Signed-off-by: Jonas Schwöbel Signed-off-by: Svyatoslav Ryhel --- common/edid.c | 306 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 272 insertions(+), 34 deletions(-) diff --git a/common/edid.c b/common/edid.c index 48a737690db..e2ac7100a88 100644 --- a/common/edid.c +++ b/common/edid.c @@ -16,6 +16,197 @@ #include #include +#define TIMING(c, ha, hfp, hbp, hsl, va, vfp, vbp, vsl, f) \ + .pixelclock = { (c), (c), (c) }, \ + .hactive = { (ha), (ha), (ha) }, \ + .hfront_porch = { (hfp), (hfp), (hfp) }, \ + .hback_porch = { (hbp), (hbp), (hbp) }, \ + .hsync_len = { (hsl), (hsl), (hsl) }, \ + .vactive = { (va), (va), (va) }, \ + .vfront_porch = { (vfp), (vfp), (vfp) }, \ + .vback_porch = { (vbp), (vbp), (vbp) }, \ + .vsync_len = { (vsl), (vsl), (vsl) }, \ + .flags = (f) + +static const struct display_timing dmt_timings[] = { + { TIMING(31500000, 640, 32, 64, 96, 350, 32, 3, 60, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(31500000, 640, 32, 64, 96, 400, 1, 3, 41, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(35500000, 720, 36, 72, 108, 400, 1, 3, 42, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(25175000, 640, 16, 96, 48, 480, 10, 2, 33, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(31500000, 640, 24, 40, 128, 480, 9, 3, 28, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(31500000, 640, 16, 64, 120, 480, 1, 3, 16, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(36000000, 640, 56, 56, 80, 480, 1, 3, 25, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(36000000, 800, 24, 72, 128, 600, 1, 2, 22, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(40000000, 800, 40, 128, 88, 600, 1, 4, 23, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(50000000, 800, 56, 120, 64, 600, 37, 6, 23, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(49500000, 800, 16, 80, 160, 600, 1, 3, 21, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(56250000, 800, 32, 64, 152, 600, 1, 3, 27, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(73250000, 800, 48, 32, 80, 600, 3, 4, 29, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(33750000, 848, 16, 112, 112, 480, 6, 8, 23, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(44900000, 1024, 8, 176, 56, 768, 0, 8, 41, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(65000000, 1024, 24, 136, 160, 768, 3, 6, 29, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(75000000, 1024, 24, 136, 144, 768, 3, 6, 29, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(78750000, 1024, 16, 96, 176, 768, 1, 3, 28, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(94500000, 1024, 48, 96, 208, 768, 1, 3, 36, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(115500000, 1024, 48, 32, 80, 768, 3, 4, 38, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(108000000, 1152, 64, 128, 256, 864, 1, 3, 32, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(74250000, 1280, 110, 40, 220, 720, 5, 5, 20, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(68250000, 1280, 48, 32, 80, 768, 3, 7, 12, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(79500000, 1280, 64, 128, 192, 768, 3, 7, 20, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(102250000, 1280, 80, 128, 208, 768, 3, 7, 27, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(117500000, 1280, 80, 136, 216, 768, 3, 7, 31, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(140250000, 1280, 48, 32, 80, 768, 3, 7, 35, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(71000000, 1280, 48, 32, 80, 800, 3, 6, 14, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(83500000, 1280, 72, 128, 200, 800, 3, 6, 22, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(106500000, 1280, 80, 128, 208, 800, 3, 6, 29, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(122500000, 1280, 80, 136, 216, 800, 3, 6, 34, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(146250000, 1280, 48, 32, 80, 800, 3, 6, 38, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(108000000, 1280, 96, 112, 312, 960, 1, 3, 36, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(148500000, 1280, 64, 160, 224, 960, 1, 3, 47, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(175500000, 1280, 48, 32, 80, 960, 3, 4, 50, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(108000000, 1280, 48, 112, 248, 1024, 1, 3, 38, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(135000000, 1280, 16, 144, 248, 1024, 1, 3, 38, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(157500000, 1280, 64, 160, 224, 1024, 1, 3, 44, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(187250000, 1280, 48, 32, 80, 1024, 3, 7, 50, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(85500000, 1360, 64, 112, 256, 768, 3, 6, 18, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(148250000, 1360, 48, 32, 80, 768, 3, 5, 37, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(85500000, 1366, 70, 143, 213, 768, 3, 3, 24, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(72000000, 1366, 14, 56, 64, 768, 1, 3, 28, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(101000000, 1400, 48, 32, 80, 1050, 3, 4, 23, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(121750000, 1400, 88, 144, 232, 1050, 3, 4, 32, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(156000000, 1400, 104, 144, 248, 1050, 3, 4, 42, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(179500000, 1400, 104, 152, 256, 1050, 3, 4, 48, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(208000000, 1400, 48, 32, 80, 1050, 3, 4, 55, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(88750000, 1440, 48, 32, 80, 900, 3, 6, 17, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(106500000, 1440, 80, 152, 232, 900, 3, 6, 25, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(136750000, 1440, 96, 152, 248, 900, 3, 6, 33, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(157000000, 1440, 104, 152, 256, 900, 3, 6, 39, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(182750000, 1440, 48, 32, 80, 900, 3, 6, 44, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(108000000, 1600, 24, 80, 96, 900, 1, 3, 96, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(162000000, 1600, 64, 192, 304, 1200, 1, 3, 46, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(175500000, 1600, 64, 192, 304, 1200, 1, 3, 46, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(189000000, 1600, 64, 192, 304, 1200, 1, 3, 46, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(202500000, 1600, 64, 192, 304, 1200, 1, 3, 46, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(229500000, 1600, 64, 192, 304, 1200, 1, 3, 46, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(268250000, 1600, 48, 32, 80, 1200, 3, 4, 64, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(119000000, 1680, 48, 32, 80, 1050, 3, 6, 21, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(146250000, 1680, 104, 176, 280, 1050, 3, 6, 30, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(187000000, 1680, 120, 176, 296, 1050, 3, 6, 40, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(214750000, 1680, 128, 176, 304, 1050, 3, 6, 46, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(245500000, 1680, 48, 32, 80, 1050, 3, 6, 53, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(204750000, 1792, 128, 200, 328, 1344, 1, 3, 46, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(261000000, 1792, 96, 216, 352, 1344, 1, 3, 69, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(333250000, 1792, 48, 32, 80, 1344, 3, 4, 72, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(218250000, 1856, 96, 224, 352, 1392, 1, 3, 43, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(288000000, 1856, 128, 224, 352, 1392, 1, 3, 104, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(356500000, 1856, 48, 32, 80, 1392, 3, 4, 75, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(148500000, 1920, 88, 44, 148, 1080, 4, 5, 36, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(154000000, 1920, 48, 32, 80, 1200, 3, 6, 26, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(193250000, 1920, 136, 200, 336, 1200, 3, 6, 36, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(245250000, 1920, 136, 208, 344, 1200, 3, 6, 46, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(281250000, 1920, 144, 208, 352, 1200, 3, 6, 53, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(317000000, 1920, 48, 32, 80, 1200, 3, 6, 62, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(234000000, 1920, 128, 208, 344, 1440, 1, 3, 56, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(297000000, 1920, 144, 224, 352, 1440, 1, 3, 56, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(380500000, 1920, 48, 32, 80, 1440, 3, 4, 78, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(162000000, 2048, 26, 80, 96, 1152, 1, 3, 44, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(268500000, 2560, 48, 32, 80, 1600, 3, 6, 37, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(348500000, 2560, 192, 280, 472, 1600, 3, 6, 49, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(443250000, 2560, 208, 280, 488, 1600, 3, 6, 63, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(505250000, 2560, 208, 280, 488, 1600, 3, 6, 73, + DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) }, + { TIMING(552750000, 2560, 48, 32, 80, 1600, 3, 6, 85, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(556744000, 4096, 8, 32, 40, 2160, 48, 8, 6, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, + { TIMING(556188000, 4096, 8, 32, 40, 2160, 48, 8, 6, + DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) }, +}; + int edid_check_info(struct edid1_info *edid_info) { if ((edid_info == NULL) || (edid_info->version == 0)) @@ -168,11 +359,11 @@ static bool cea_is_hdmi_vsdb_present(struct edid_cea861_info *info) return false; } -static bool edid_find_valid_timing(void *buf, int count, - struct display_timing *timing, - bool (*mode_valid)(void *priv, - const struct display_timing *timing), - void *mode_valid_priv) +static bool edid_find_valid_detailed_timing(void *buf, int count, + struct display_timing *timing, + bool (*mode_valid)(void *priv, + const struct display_timing *timing), + void *mode_valid_priv) { struct edid_detailed_timing *t = buf; bool found = false; @@ -191,6 +382,71 @@ static bool edid_find_valid_timing(void *buf, int count, return found; } +static bool edid_get_standard_timing(struct edid1_info *edid, int i, unsigned int *x, + unsigned int *y, unsigned int *freq) +{ + unsigned int aspect = 10000; + unsigned char xres, vfreq; + + xres = EDID1_INFO_STANDARD_TIMING_XRESOLUTION(*edid, i); + vfreq = EDID1_INFO_STANDARD_TIMING_VFREQ(*edid, i); + if (xres != vfreq || (xres != 0 && xres != 1) || + (vfreq != 0 && vfreq != 1)) { + switch (EDID1_INFO_STANDARD_TIMING_ASPECT(*edid, i)) { + case ASPECT_625: // 16:10 + aspect = 6250; + break; + case ASPECT_75: // 4:3 + aspect = 7500; + break; + case ASPECT_8: // 5:4 + aspect = 8000; + break; + case ASPECT_5625: // 16:9 + aspect = 5625; + break; + } + + *x = (xres + 31) * 8; + *y = *x * aspect / 10000; + *freq = (vfreq & 0x3f) + 60; + + return true; + } + + return false; +} + +static bool edid_find_valid_standard_timing(struct edid1_info *buf, + struct display_timing *timing, + bool (*mode_valid)(void *priv, + const struct display_timing *timing), + void *mode_valid_priv) +{ + unsigned int x, y, freq; + bool found = false; + int i, k; + + for (i = 0; i < ARRAY_SIZE(buf->standard_timings); i++) { + if (!edid_get_standard_timing(buf, i, &x, &y, &freq)) + continue; + + for (k = 0; k < ARRAY_SIZE(dmt_timings); k++) { + const struct display_timing *dt = &dmt_timings[k]; + + if (dt->hactive.typ == x && dt->vactive.typ == y) { + found = mode_valid(mode_valid_priv, dt); + if (found) { + memcpy(timing, dt, sizeof(*timing)); + return true; + } + } + } + } + + return found; +} + int edid_get_timing_validate(u8 *buf, int buf_size, struct display_timing *timing, int *panel_bits_per_colourp, @@ -217,8 +473,8 @@ int edid_get_timing_validate(u8 *buf, int buf_size, } /* Look for detailed timing in base EDID */ - found = edid_find_valid_timing(edid->monitor_details.descriptor, 4, - timing, mode_valid, mode_valid_priv); + found = edid_find_valid_detailed_timing(edid->monitor_details.descriptor, 4, + timing, mode_valid, mode_valid_priv); /* Look for detailed timing in CTA-861 Extension Block */ if (!found && edid->extension_flag && buf_size >= EDID_EXT_SIZE) { @@ -231,12 +487,17 @@ int edid_get_timing_validate(u8 *buf, int buf_size, int size = count * sizeof(struct edid_detailed_timing); if (offset >= 4 && offset + size < EDID_SIZE) - found = edid_find_valid_timing( + found = edid_find_valid_detailed_timing( (u8 *)info + offset, count, timing, mode_valid, mode_valid_priv); } } + /* Look for timing in Standard Timings */ + if (!found) + found = edid_find_valid_standard_timing(edid, timing, mode_valid, + mode_valid_priv); + if (!found) return -EINVAL; @@ -461,34 +722,11 @@ void edid_print_info(struct edid1_info *edid_info) /* Standard timings. */ printf("Standard timings:\n"); for (i = 0; i < ARRAY_SIZE(edid_info->standard_timings); i++) { - unsigned int aspect = 10000; - unsigned int x, y; - unsigned char xres, vfreq; + unsigned int x, y, freq; - xres = EDID1_INFO_STANDARD_TIMING_XRESOLUTION(*edid_info, i); - vfreq = EDID1_INFO_STANDARD_TIMING_VFREQ(*edid_info, i); - if ((xres != vfreq) || - ((xres != 0) && (xres != 1)) || - ((vfreq != 0) && (vfreq != 1))) { - switch (EDID1_INFO_STANDARD_TIMING_ASPECT(*edid_info, - i)) { - case ASPECT_625: - aspect = 6250; - break; - case ASPECT_75: - aspect = 7500; - break; - case ASPECT_8: - aspect = 8000; - break; - case ASPECT_5625: - aspect = 5625; - break; - } - x = (xres + 31) * 8; - y = x * aspect / 10000; + if (edid_get_standard_timing(edid_info, i, &x, &y, &freq)) { printf("\t%dx%d%c\t%d Hz\n", x, y, - x > 1000 ? ' ' : '\t', (vfreq & 0x3f) + 60); + x > 1000 ? ' ' : '\t', freq); have_timing = 1; } } From dbc27c2462871722fe9ee591a0e7cdba6d5f48b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Schw=C3=B6bel?= Date: Tue, 4 Mar 2025 09:02:11 +0200 Subject: [PATCH 21/28] ARM: tegra: clock: fix PLLD/PLLD2 related clock calculations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While PLLD/D2 is the nominal parent clock, all derived clocks are generated from its single output, plld_out0, which is PLLD/D2 divided by two. Direct use of PLLD/D2 is absent in peripheral clock configurations. Therefore, clock derivation formulas must take in account this division. Signed-off-by: Jonas Schwöbel Signed-off-by: Svyatoslav Ryhel --- arch/arm/mach-tegra/clock.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 157e6c4911a..a375693481e 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -358,6 +358,13 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, break; } + /* + * PLLD/PLLD2 raw clock rate is never used, instead plld_out0 is used + * that is PLLD/PLLD2 halved. + */ + if (parent == CLOCK_ID_DISPLAY || parent == CLOCK_ID_DISPLAY2) + parent_rate /= 2; + return get_rate_from_divider(parent_rate, div); } @@ -449,6 +456,7 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, enum clock_id parent, unsigned rate, int *extra_div) { unsigned effective_rate; + unsigned int parent_rate; int mux_bits, divider_bits, source; int divider; int xdiv = 0; @@ -457,7 +465,17 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, source = get_periph_clock_source(periph_id, parent, &mux_bits, ÷r_bits); - divider = find_best_divider(divider_bits, pll_rate[parent], + /* + * Clocks derived from PLLD/D2 are actually sourced from its halved + * output, plld_out0/plld2_out0. No peripheral clocks use the raw + * PLLD/D2 frequency. This halving must be accounted for in derived + * clock calculations. + */ + parent_rate = pll_rate[parent]; + if (parent == CLOCK_ID_DISPLAY || parent == CLOCK_ID_DISPLAY2) + parent_rate /= 2; + + divider = find_best_divider(divider_bits, parent_rate, rate, &xdiv); if (extra_div) *extra_div = xdiv; @@ -685,6 +703,16 @@ int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon) else writel(base_reg, &simple_pll->pll_base); + /* + * Changing clocks was never intended in the U-Boot for Tegra. + * If a clock is changed after clock_init() the parent rate is wrong. + * Usually there is no reason to change peripheral clocks, but Display + * PLLs which needs to generate a precise pixelclock might be adjusted. + * Especially in the case of HDMI display with changing and prior + * unknown resolution. + */ + pll_rate[clkid] = clock_get_rate(clkid); + return 0; } From c825b1f89213de4647ee84a7197289b63fbf2160 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Wed, 5 Mar 2025 12:19:20 +0200 Subject: [PATCH 22/28] ARM: tegra20: mark second DC with bootph-all For the Tegra 2, similar to other Tegra SoC generations, 'bootph-all' must be applied to both display controllers. Signed-off-by: Svyatoslav Ryhel --- arch/arm/dts/tegra20-u-boot.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/dts/tegra20-u-boot.dtsi b/arch/arm/dts/tegra20-u-boot.dtsi index fa582bcb9fd..b74aa5bb0d4 100644 --- a/arch/arm/dts/tegra20-u-boot.dtsi +++ b/arch/arm/dts/tegra20-u-boot.dtsi @@ -9,5 +9,9 @@ dc@54200000 { bootph-all; }; + + dc@54240000 { + bootph-all; + }; }; }; From c614dc5317f2c1ccd4ade0b55e2b76d75a8817d8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 11 Mar 2025 19:37:08 +0200 Subject: [PATCH 23/28] configs: transformer: add 3 second delay before power off Introduce a 3-second delay and an informational message during boot to enhance user experience. Signed-off-by: Svyatoslav Ryhel --- configs/transformer_t20_defconfig | 2 +- configs/transformer_t30_defconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/transformer_t20_defconfig b/configs/transformer_t20_defconfig index b69366581a4..6f823ec720e 100644 --- a/configs/transformer_t20_defconfig +++ b/configs/transformer_t20_defconfig @@ -21,7 +21,7 @@ CONFIG_BOOTDELAY=0 CONFIG_AUTOBOOT_KEYED=y CONFIG_AUTOBOOT_KEYED_CTRLC=y CONFIG_OF_SYSTEM_SETUP=y -CONFIG_BOOTCOMMAND="bootflow scan; poweroff" +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" CONFIG_SYS_PBSIZE=2085 CONFIG_SPL_FOOTPRINT_LIMIT=y CONFIG_SPL_MAX_FOOTPRINT=0x8000 diff --git a/configs/transformer_t30_defconfig b/configs/transformer_t30_defconfig index c5f0bc2a613..9d63755533a 100644 --- a/configs/transformer_t30_defconfig +++ b/configs/transformer_t30_defconfig @@ -21,7 +21,7 @@ CONFIG_AUTOBOOT_KEYED=y CONFIG_AUTOBOOT_KEYED_CTRLC=y CONFIG_OF_BOARD_SETUP=y CONFIG_OF_SYSTEM_SETUP=y -CONFIG_BOOTCOMMAND="bootflow scan; poweroff" +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" CONFIG_SYS_PBSIZE=2084 CONFIG_SPL_FOOTPRINT_LIMIT=y CONFIG_SPL_MAX_FOOTPRINT=0x8000 From 8f0c488c232c98fe13c961d279852db0c681c93f Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 11 Mar 2025 19:37:39 +0200 Subject: [PATCH 24/28] configs: endeavoru: add 3 second delay before power off Introduce a 3-second delay and an informational message during boot to enhance user experience. Signed-off-by: Svyatoslav Ryhel --- configs/endeavoru_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/endeavoru_defconfig b/configs/endeavoru_defconfig index 1d1f1042291..cc8777e4d83 100644 --- a/configs/endeavoru_defconfig +++ b/configs/endeavoru_defconfig @@ -21,7 +21,7 @@ CONFIG_AUTOBOOT_KEYED=y CONFIG_AUTOBOOT_KEYED_CTRLC=y CONFIG_OF_BOARD_SETUP=y CONFIG_OF_SYSTEM_SETUP=y -CONFIG_BOOTCOMMAND="bootflow scan; poweroff" +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" CONFIG_SYS_PBSIZE=2084 CONFIG_SPL_FOOTPRINT_LIMIT=y CONFIG_SPL_MAX_FOOTPRINT=0x8000 From c9768e1899ddbf17c6803e5042b4df7ac5e6ef65 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 11 Mar 2025 19:38:17 +0200 Subject: [PATCH 25/28] configs: grouper: add 3 second delay before power off Introduce a 3-second delay and an informational message during boot to enhance user experience. Signed-off-by: Svyatoslav Ryhel --- configs/grouper_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/grouper_defconfig b/configs/grouper_defconfig index 9221ffb46a3..f265db8232f 100644 --- a/configs/grouper_defconfig +++ b/configs/grouper_defconfig @@ -21,7 +21,7 @@ CONFIG_AUTOBOOT_KEYED=y CONFIG_AUTOBOOT_KEYED_CTRLC=y CONFIG_OF_BOARD_SETUP=y CONFIG_OF_SYSTEM_SETUP=y -CONFIG_BOOTCOMMAND="bootflow scan; poweroff" +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" CONFIG_SYS_PBSIZE=2084 CONFIG_SPL_FOOTPRINT_LIMIT=y CONFIG_SPL_MAX_FOOTPRINT=0x8000 From a0448e4f9de951577c0a980de1595e2d63b696e7 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 11 Mar 2025 19:39:27 +0200 Subject: [PATCH 26/28] configs: picasso: add 3 second delay before power off Introduce a 3-second delay and an informational message during boot to enhance user experience. Signed-off-by: Svyatoslav Ryhel --- configs/picasso_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/picasso_defconfig b/configs/picasso_defconfig index 994951bb81e..a9b7f91b735 100644 --- a/configs/picasso_defconfig +++ b/configs/picasso_defconfig @@ -21,7 +21,7 @@ CONFIG_BOOTDELAY=0 CONFIG_AUTOBOOT_KEYED=y CONFIG_AUTOBOOT_KEYED_CTRLC=y CONFIG_OF_SYSTEM_SETUP=y -CONFIG_BOOTCOMMAND="bootflow scan; poweroff" +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" CONFIG_SYS_PBSIZE=2085 CONFIG_SPL_FOOTPRINT_LIMIT=y CONFIG_SPL_MAX_FOOTPRINT=0x8000 From f7b9a1311c0034e8f9db1221aff3a36b1d7591e1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 11 Mar 2025 19:39:55 +0200 Subject: [PATCH 27/28] configs: x3_t30: add 3 second delay before power off Introduce a 3-second delay and an informational message during boot to enhance user experience. Signed-off-by: Svyatoslav Ryhel --- configs/x3_t30_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/x3_t30_defconfig b/configs/x3_t30_defconfig index c8da5b4ce35..2d72a3bd56f 100644 --- a/configs/x3_t30_defconfig +++ b/configs/x3_t30_defconfig @@ -21,7 +21,7 @@ CONFIG_AUTOBOOT_KEYED=y CONFIG_AUTOBOOT_KEYED_CTRLC=y CONFIG_OF_BOARD_SETUP=y CONFIG_OF_SYSTEM_SETUP=y -CONFIG_BOOTCOMMAND="bootflow scan; poweroff" +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" CONFIG_SYS_PBSIZE=2084 CONFIG_SPL_FOOTPRINT_LIMIT=y CONFIG_SPL_MAX_FOOTPRINT=0x8000 From b156a1171e356f16aac88322b3bdcec071543c6c Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 11 Mar 2025 19:47:02 +0200 Subject: [PATCH 28/28] configs: qc750: add 3 second delay before power off Introduce a 3-second delay and an informational message during boot to enhance user experience. Signed-off-by: Svyatoslav Ryhel --- configs/qc750_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/qc750_defconfig b/configs/qc750_defconfig index 2485e64a2f0..e958e0d53ca 100644 --- a/configs/qc750_defconfig +++ b/configs/qc750_defconfig @@ -22,7 +22,7 @@ CONFIG_AUTOBOOT_KEYED=y CONFIG_AUTOBOOT_KEYED_CTRLC=y CONFIG_OF_BOARD_SETUP=y CONFIG_OF_SYSTEM_SETUP=y -CONFIG_BOOTCOMMAND="bootflow scan; poweroff" +CONFIG_BOOTCOMMAND="bootflow scan; echo 'Boot configuration not found... Power off in 3 sec'; sleep 3; poweroff" CONFIG_SYS_PBSIZE=2084 CONFIG_SPL_FOOTPRINT_LIMIT=y CONFIG_SPL_MAX_FOOTPRINT=0x8000