diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pinctrl/tegra/pinctrl-tegra.c | 34 | ||||
-rw-r--r-- | drivers/video/Kconfig | 6 | ||||
-rw-r--r-- | drivers/video/bridge/Kconfig | 8 | ||||
-rw-r--r-- | drivers/video/bridge/dp501.c | 27 | ||||
-rw-r--r-- | drivers/video/bridge/ssd2825.c | 159 | ||||
-rw-r--r-- | drivers/video/bridge/tc358768.c | 101 | ||||
-rw-r--r-- | drivers/video/endeavoru-panel.c | 21 | ||||
-rw-r--r-- | drivers/video/lg-ld070wx3.c | 2 | ||||
-rw-r--r-- | drivers/video/lm3533_backlight.c | 142 | ||||
-rw-r--r-- | drivers/video/renesas-r61307.c | 13 | ||||
-rw-r--r-- | drivers/video/renesas-r69328.c | 37 | ||||
-rw-r--r-- | drivers/video/samsung-ltl106hl02.c | 2 | ||||
-rw-r--r-- | drivers/video/sharp-lq101r1sx01.c | 1 | ||||
-rw-r--r-- | drivers/video/tegra20/Kconfig | 16 | ||||
-rw-r--r-- | drivers/video/tegra20/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/tegra20/tegra-dc.c | 233 | ||||
-rw-r--r-- | drivers/video/tegra20/tegra-dc.h | 3 | ||||
-rw-r--r-- | drivers/video/tegra20/tegra-dsi.c | 12 | ||||
-rw-r--r-- | drivers/video/tegra20/tegra-hdmi.c | 623 | ||||
-rw-r--r-- | drivers/video/tegra20/tegra-hdmi.h | 648 | ||||
-rw-r--r-- | drivers/video/tegra20/tegra-host1x.c | 86 | ||||
-rw-r--r-- | drivers/video/tegra20/tegra-pwm-backlight.c | 12 |
22 files changed, 1923 insertions, 265 deletions
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index e6b957f5537..b04be168bc8 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -23,18 +23,18 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt) return; } - drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", 0); - drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", 0); - drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", 0); - drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", 0); + drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", PMUX_SLWF_NONE); + drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", PMUX_SLWR_NONE); + drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", PMUX_DRVUP_NONE); + drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", PMUX_DRVDN_NONE); #ifdef TEGRA_PMX_GRPS_HAVE_LPMD - drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", 0); + drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", PMUX_LPMD_NONE); #endif #ifdef TEGRA_PMX_GRPS_HAVE_SCHMT - drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0); + drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE); #endif #ifdef TEGRA_PMX_GRPS_HAVE_HSM - drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0); + drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE); #endif for (i = 1; i < drvcnt; i++) @@ -142,31 +142,31 @@ static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) pinmux_group[0].func = i; - pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", 0); - pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", 0); + pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", PMUX_PULL_NORMAL); + pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", PMUX_TRI_TRISTATE); #ifdef TEGRA_PMX_PINS_HAVE_E_INPUT - pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", 0); + pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", PMUX_PIN_NONE); #endif #ifdef TEGRA_PMX_PINS_HAVE_LOCK - pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", 0); + pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", PMUX_PIN_LOCK_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_OD - pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", 0); + pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", PMUX_PIN_OD_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_IO_RESET - pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", 0); + pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", PMUX_PIN_IO_RESET_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_RCV_SEL - pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", 0); + pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", PMUX_PIN_RCV_SEL_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_E_IO_HV - pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", 0); + pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", PMUX_PIN_E_IO_HV_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_SCHMT - pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0); + pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE); #endif #ifdef TEGRA_PMX_PINS_HAVE_HSM - pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0); + pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE); #endif for (i = 1; i < pincnt; i++) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a5263dfbff9..df607303616 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -700,6 +700,12 @@ config I2C_EDID help This enables library for accessing EDID data from an LCD panel. +config I2C_EDID_STANDARD + bool "Enable standard timings EDID library expansion" + depends on I2C_EDID + help + This enables standard timings expansion for EDID data from an LCD panel. + config DISPLAY bool "Enable Display support" depends on DM diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index 21c5a043e02..be53034bd3d 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -9,7 +9,7 @@ config VIDEO_BRIDGE config VIDEO_BRIDGE_PARADE_DP501 bool "Support Parade DP501 DP & DVI/HDMI dual mode transmitter" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select DM_I2C help The Parade DP501 is a DP & DVI/HDMI dual-mode transmitter. It @@ -46,14 +46,14 @@ config VIDEO_BRIDGE_ANALOGIX_ANX6345 config VIDEO_BRIDGE_SOLOMON_SSD2825 bool "Solomon SSD2825 bridge driver" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_MIPI_DSI help - Solomon SSD2824 SPI RGB-DSI bridge driver wrapped into panel uClass. + Solomon SSD2824 SPI RGB-DSI bridge driver. config VIDEO_BRIDGE_TOSHIBA_TC358768 bool "Support Toshiba TC358768 MIPI DSI bridge" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_MIPI_DSI select DM_I2C help diff --git a/drivers/video/bridge/dp501.c b/drivers/video/bridge/dp501.c index 095e3e71fed..9937cfe095b 100644 --- a/drivers/video/bridge/dp501.c +++ b/drivers/video/bridge/dp501.c @@ -9,6 +9,7 @@ #include <log.h> #include <backlight.h> #include <panel.h> +#include <video_bridge.h> #include <linux/delay.h> #include <linux/err.h> #include <power/regulator.h> @@ -206,7 +207,6 @@ struct dp501_priv { struct udevice *chip2; struct udevice *vdd; - struct gpio_desc reset_gpio; struct gpio_desc enable_gpio; }; @@ -484,16 +484,19 @@ static int dp501_panel_timings(struct udevice *dev, return 0; } -static void dp501_hw_init(struct dp501_priv *priv) +static void dp501_hw_init(struct udevice *dev) { - dm_gpio_set_value(&priv->reset_gpio, 1); + struct dp501_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + + dm_gpio_set_value(&uc_priv->reset, 1); regulator_set_enable_if_allowed(priv->vdd, 1); dm_gpio_set_value(&priv->enable_gpio, 1); udelay(100); - dm_gpio_set_value(&priv->reset_gpio, 0); + dm_gpio_set_value(&uc_priv->reset, 0); mdelay(80); } @@ -521,14 +524,6 @@ static int dp501_setup(struct udevice *dev) } /* get gpios */ - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_debug("%s: Could not decode reset-gpios (%d)\n", - __func__, ret); - return ret; - } - ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable_gpio, GPIOD_IS_OUT); if (ret) { @@ -544,7 +539,7 @@ static int dp501_setup(struct udevice *dev) return ret; } - dp501_hw_init(priv); + dp501_hw_init(dev); /* get EDID */ return panel_get_display_timing(priv->panel, &priv->timing); @@ -558,8 +553,8 @@ static int dp501_probe(struct udevice *dev) return dp501_setup(dev); } -struct panel_ops dp501_ops = { - .enable_backlight = dp501_attach, +static const struct video_bridge_ops dp501_ops = { + .attach = dp501_attach, .set_backlight = dp501_set_backlight, .get_display_timing = dp501_panel_timings, }; @@ -571,7 +566,7 @@ static const struct udevice_id dp501_ids[] = { U_BOOT_DRIVER(dp501) = { .name = "dp501", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = dp501_ids, .ops = &dp501_ops, .probe = dp501_probe, diff --git a/drivers/video/bridge/ssd2825.c b/drivers/video/bridge/ssd2825.c index f978021c860..a307993377c 100644 --- a/drivers/video/bridge/ssd2825.c +++ b/drivers/video/bridge/ssd2825.c @@ -5,12 +5,15 @@ #include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <log.h> #include <misc.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> +#include <video_bridge.h> #include <panel.h> +#include <power/regulator.h> #include <spi.h> #include <linux/delay.h> #include <linux/err.h> @@ -106,6 +109,10 @@ #define SSD2825_LP_MIN_CLK 5000 /* KHz */ #define SSD2825_REF_MIN_CLK 2000 /* KHz */ +static const char * const ssd2825_supplies[] = { + "dvdd-supply", "avdd-supply", "vddio-supply" +}; + struct ssd2825_bridge_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; @@ -113,12 +120,16 @@ struct ssd2825_bridge_priv { struct udevice *panel; struct display_timing timing; + struct udevice *supplies[ARRAY_SIZE(ssd2825_supplies)]; + struct gpio_desc power_gpio; - struct gpio_desc reset_gpio; struct clk *tx_clk; u32 pll_freq_kbps; /* PLL in kbps */ + + u32 hzd; /* HS Zero Delay in ns */ + u32 hpd; /* HS Prepare Delay is ns */ }; static int ssd2825_spi_write(struct udevice *dev, int reg, @@ -231,7 +242,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg) { struct udevice *dev = (struct udevice *)host->dev; - u8 buf = *(u8 *)msg->tx_buf; u16 config; int ret; @@ -260,15 +270,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host, ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); ssd2825_write_dsi(dev, msg->tx_buf, msg->tx_len); - if (buf == MIPI_DCS_SET_DISPLAY_ON) { - ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, - SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN | - SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD | - SSD2825_CONF_REG_EOT); - ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); - ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); - } - return 0; } @@ -312,9 +313,14 @@ static void ssd2825_setup_pll(struct udevice *dev) struct mipi_dsi_device *device = &priv->device; struct display_timing *dt = &priv->timing; u16 pll_config, lp_div; + u32 nibble_delay, nibble_freq_khz; u32 pclk_mult, tx_freq_khz, pd_lines; + u8 hzd, hpd; tx_freq_khz = clk_get_rate(priv->tx_clk) / 1000; + if (!tx_freq_khz || tx_freq_khz < 0) + tx_freq_khz = SSD2825_REF_MIN_CLK; + pd_lines = mipi_dsi_pixel_format_to_bpp(device->format); pclk_mult = pd_lines / device->lanes + 1; @@ -324,12 +330,19 @@ static void ssd2825_setup_pll(struct udevice *dev) lp_div = priv->pll_freq_kbps / (SSD2825_LP_MIN_CLK * 8); + /* nibble_delay in nanoseconds */ + nibble_freq_khz = priv->pll_freq_kbps / 4; + nibble_delay = 1000 * 1000 / nibble_freq_khz; + + hzd = priv->hzd / nibble_delay; + hpd = (priv->hpd - 4 * nibble_delay) / nibble_delay; + /* Disable PLL */ ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0000); ssd2825_write_register(dev, SSD2825_LINE_CTRL_REG, 0x0001); /* Set delays */ - ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, 0x2103); + ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, (hzd << 8) | hpd); /* Set PLL coeficients */ ssd2825_write_register(dev, SSD2825_PLL_CONFIGURATION_REG, pll_config); @@ -343,11 +356,30 @@ static void ssd2825_setup_pll(struct udevice *dev) ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); } -static int ssd2825_bridge_enable_panel(struct udevice *dev) +static int ssd2825_bridge_attach(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); struct mipi_dsi_device *device = &priv->device; struct display_timing *dt = &priv->timing; + u8 pixel_format; + int ret; + + /* Set pixel format */ + switch (device->format) { + case MIPI_DSI_FMT_RGB565: + pixel_format = 0x00; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + pixel_format = 0x01; + break; + case MIPI_DSI_FMT_RGB666: + pixel_format = 0x02; + break; + case MIPI_DSI_FMT_RGB888: + default: + pixel_format = 0x03; + break; + } /* Perform SW reset */ ssd2825_write_register(dev, SSD2825_OPERATION_CTRL_REG, 0x0100); @@ -367,7 +399,7 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev) ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_6, SSD2825_HSYNC_HIGH | SSD2825_VSYNC_HIGH | SSD2825_PCKL_HIGH | SSD2825_NON_BURST | - (3 - device->format)); + pixel_format); ssd2825_write_register(dev, SSD2825_LANE_CONFIGURATION_REG, device->lanes - 1); ssd2825_write_register(dev, SSD2825_TEST_REG, 0x0004); @@ -384,7 +416,18 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev) ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); /* Perform panel setup */ - return panel_enable_backlight(priv->panel); + ret = panel_enable_backlight(priv->panel); + if (ret) + return ret; + + ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, + SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN | + SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD | + SSD2825_CONF_REG_EOT); + ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); + ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); + + return 0; } static int ssd2825_bridge_set_panel(struct udevice *dev, int percent) @@ -407,7 +450,8 @@ static int ssd2825_bridge_panel_timings(struct udevice *dev, static int ssd2825_bridge_hw_init(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); - int ret; + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + int i, ret; ret = clk_prepare_enable(priv->tx_clk); if (ret) { @@ -416,25 +460,28 @@ static int ssd2825_bridge_hw_init(struct udevice *dev) return ret; } - ret = dm_gpio_set_value(&priv->power_gpio, 1); - if (ret) { - log_debug("%s: error changing power-gpios (%d)\n", - __func__, ret); - return ret; + /* enable supplies */ + for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) { + ret = regulator_set_enable_if_allowed(priv->supplies[i], 1); + if (ret) { + log_debug("%s: cannot enable %s %d\n", __func__, + ssd2825_supplies[i], ret); + return ret; + } } mdelay(10); - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&uc_priv->reset, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error entering reset (%d)\n", __func__, ret); return ret; } mdelay(10); - ret = dm_gpio_set_value(&priv->reset_gpio, 1); + ret = dm_gpio_set_value(&uc_priv->reset, 0); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error exiting reset (%d)\n", __func__, ret); return ret; } @@ -443,13 +490,33 @@ static int ssd2825_bridge_hw_init(struct udevice *dev) return 0; } +static int ssd2825_bridge_get_panel(struct udevice *dev) +{ + struct ssd2825_bridge_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + static int ssd2825_bridge_probe(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); struct spi_slave *slave = dev_get_parent_priv(dev); struct mipi_dsi_device *device = &priv->device; struct mipi_dsi_panel_plat *mipi_plat; - int ret; + int i, ret; ret = spi_claim_bus(slave); if (ret) { @@ -457,10 +524,9 @@ static int ssd2825_bridge_probe(struct udevice *dev) return ret; } - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + ret = ssd2825_bridge_get_panel(dev); if (ret) { - log_err("cannot get panel: ret=%d\n", ret); + log_debug("%s: panel not found, ret %d\n", __func__, ret); return ret; } @@ -477,33 +543,33 @@ static int ssd2825_bridge_probe(struct udevice *dev) device->format = mipi_plat->format; device->mode_flags = mipi_plat->mode_flags; - /* get panel gpios */ - ret = gpio_request_by_name(dev, "power-gpios", 0, - &priv->power_gpio, GPIOD_IS_OUT); - if (ret) { - log_err("could not decode power-gpios (%d)\n", ret); - return ret; - } - - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_err("could not decode reset-gpios (%d)\n", ret); - return ret; + /* get supplies */ + for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) { + ret = device_get_supply_regulator(dev, ssd2825_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + ssd2825_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } } /* get clk */ - priv->tx_clk = devm_clk_get(dev, "tx_clk"); + priv->tx_clk = devm_clk_get_optional(dev, NULL); if (IS_ERR(priv->tx_clk)) { log_err("cannot get tx_clk: %ld\n", PTR_ERR(priv->tx_clk)); return PTR_ERR(priv->tx_clk); } + priv->hzd = dev_read_u32_default(dev, "solomon,hs-zero-delay-ns", 133); + priv->hpd = dev_read_u32_default(dev, "solomon,hs-prep-delay-ns", 40); + return ssd2825_bridge_hw_init(dev); } -static const struct panel_ops ssd2825_bridge_ops = { - .enable_backlight = ssd2825_bridge_enable_panel, +static const struct video_bridge_ops ssd2825_bridge_ops = { + .attach = ssd2825_bridge_attach, .set_backlight = ssd2825_bridge_set_panel, .get_display_timing = ssd2825_bridge_panel_timings, }; @@ -515,9 +581,10 @@ static const struct udevice_id ssd2825_bridge_ids[] = { U_BOOT_DRIVER(ssd2825) = { .name = "ssd2825", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = ssd2825_bridge_ids, .ops = &ssd2825_bridge_ops, + .bind = dm_scan_fdt_dev, .probe = ssd2825_bridge_probe, .priv_auto = sizeof(struct ssd2825_bridge_priv), }; diff --git a/drivers/video/bridge/tc358768.c b/drivers/video/bridge/tc358768.c index 19b6ca29d3e..358004f30f2 100644 --- a/drivers/video/bridge/tc358768.c +++ b/drivers/video/bridge/tc358768.c @@ -6,12 +6,14 @@ #include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <i2c.h> #include <log.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> #include <panel.h> +#include <video_bridge.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/kernel.h> @@ -122,6 +124,10 @@ #define NANO 1000000000UL #define PICO 1000000000000ULL +static const char * const tc358768_supplies[] = { + "vddc-supply", "vddmipi-supply", "vddio-supply" +}; + struct tc358768_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; @@ -129,9 +135,7 @@ struct tc358768_priv { struct udevice *panel; struct display_timing timing; - struct udevice *vddc; - struct udevice *vddmipi; - struct udevice *vddio; + struct udevice *supplies[ARRAY_SIZE(tc358768_supplies)]; struct clk *refclk; @@ -265,25 +269,27 @@ static void tc358768_sw_reset(struct udevice *dev) tc358768_write(dev, TC358768_SYSCTL, 0); } -static void tc358768_hw_enable(struct tc358768_priv *priv) +static void tc358768_hw_enable(struct udevice *dev) { + struct tc358768_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); int ret; ret = clk_prepare_enable(priv->refclk); if (ret) log_debug("%s: error enabling refclk (%d)\n", __func__, ret); - ret = regulator_set_enable_if_allowed(priv->vddc, true); + ret = regulator_set_enable_if_allowed(priv->supplies[0], true); if (ret) log_debug("%s: error enabling vddc (%d)\n", __func__, ret); - ret = regulator_set_enable_if_allowed(priv->vddmipi, true); + ret = regulator_set_enable_if_allowed(priv->supplies[1], true); if (ret) log_debug("%s: error enabling vddmipi (%d)\n", __func__, ret); mdelay(10); - ret = regulator_set_enable_if_allowed(priv->vddio, true); + ret = regulator_set_enable_if_allowed(priv->supplies[2], true); if (ret) log_debug("%s: error enabling vddio (%d)\n", __func__, ret); @@ -293,7 +299,7 @@ static void tc358768_hw_enable(struct tc358768_priv *priv) * The RESX is active low (GPIO_ACTIVE_LOW). * DEASSERT (value = 0) the reset_gpio to enable the chip */ - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&uc_priv->reset, 0); if (ret) log_debug("%s: error changing reset-gpio (%d)\n", __func__, ret); @@ -477,7 +483,7 @@ static int tc358768_attach(struct udevice *dev) device->mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS; } - tc358768_hw_enable(priv); + tc358768_hw_enable(dev); tc358768_sw_reset(dev); tc358768_setup_pll(dev); @@ -874,12 +880,33 @@ static int tc358768_panel_timings(struct udevice *dev, return 0; } +static int tc358768_get_panel(struct udevice *dev) +{ + struct tc358768_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + static int tc358768_setup(struct udevice *dev) { struct tc358768_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); struct mipi_dsi_device *device = &priv->device; struct mipi_dsi_panel_plat *mipi_plat; - int ret; + int i, ret; /* The bridge uses 16 bit registers */ ret = i2c_set_chip_offset_len(dev, 2); @@ -889,11 +916,10 @@ static int tc358768_setup(struct udevice *dev) return ret; } - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + ret = tc358768_get_panel(dev); if (ret) { - log_debug("%s: Cannot get panel: ret=%d\n", __func__, ret); - return log_ret(ret); + log_debug("%s: panel not found, ret %d\n", __func__, ret); + return ret; } panel_get_display_timing(priv->panel, &priv->timing); @@ -913,44 +939,26 @@ static int tc358768_setup(struct udevice *dev) priv->dsi_lanes = device->lanes; /* get regulators */ - ret = device_get_supply_regulator(dev, "vddc-supply", &priv->vddc); - if (ret) { - log_debug("%s: vddc regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); - } - - ret = device_get_supply_regulator(dev, "vddmipi-supply", &priv->vddmipi); - if (ret) { - log_debug("%s: vddmipi regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); - } - - ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); - if (ret) { - log_debug("%s: vddio regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); + for (i = 0; i < ARRAY_SIZE(tc358768_supplies); i++) { + ret = device_get_supply_regulator(dev, tc358768_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + tc358768_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } } /* get clk */ - priv->refclk = devm_clk_get(dev, "refclk"); + priv->refclk = devm_clk_get(dev, NULL); if (IS_ERR(priv->refclk)) { log_debug("%s: Could not get refclk: %ld\n", __func__, PTR_ERR(priv->refclk)); return PTR_ERR(priv->refclk); } - /* get gpios */ - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret); - return ret; - } - - dm_gpio_set_value(&priv->reset_gpio, 1); + dm_gpio_set_value(&uc_priv->reset, 1); return 0; } @@ -963,8 +971,8 @@ static int tc358768_probe(struct udevice *dev) return tc358768_setup(dev); } -struct panel_ops tc358768_ops = { - .enable_backlight = tc358768_attach, +static const struct video_bridge_ops tc358768_ops = { + .attach = tc358768_attach, .set_backlight = tc358768_set_backlight, .get_display_timing = tc358768_panel_timings, }; @@ -977,9 +985,10 @@ static const struct udevice_id tc358768_ids[] = { U_BOOT_DRIVER(tc358768) = { .name = "tc358768", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = tc358768_ids, .ops = &tc358768_ops, + .bind = dm_scan_fdt_dev, .probe = tc358768_probe, .priv_auto = sizeof(struct tc358768_priv), }; diff --git a/drivers/video/endeavoru-panel.c b/drivers/video/endeavoru-panel.c index d4ba4d8b6da..9950ff8bb05 100644 --- a/drivers/video/endeavoru-panel.c +++ b/drivers/video/endeavoru-panel.c @@ -117,6 +117,18 @@ static int endeavoru_panel_set_backlight(struct udevice *dev, int percent) struct endeavoru_panel_priv *priv = dev_get_priv(dev); int ret; + /* + * Due to the use of the Tegra DC backlight feature, backlight + * requests MUST NOT be made during probe or earlier. This is + * because it creates a loop, as the backlight is a DC child. + */ + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_err("cannot get backlight: ret = %d\n", ret); + return ret; + } + ret = backlight_enable(priv->backlight); if (ret) return ret; @@ -136,13 +148,6 @@ static int endeavoru_panel_of_to_plat(struct udevice *dev) struct endeavoru_panel_priv *priv = dev_get_priv(dev); int ret; - ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, - "backlight", &priv->backlight); - if (ret) { - log_err("cannot get backlight: ret = %d\n", ret); - return ret; - } - ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, "vdd-supply", &priv->vdd); if (ret) { @@ -231,7 +236,7 @@ static int endeavoru_panel_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 2; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return endeavoru_panel_hw_init(dev); } diff --git a/drivers/video/lg-ld070wx3.c b/drivers/video/lg-ld070wx3.c index 610a06ffe7b..3676e45bc65 100644 --- a/drivers/video/lg-ld070wx3.c +++ b/drivers/video/lg-ld070wx3.c @@ -158,7 +158,7 @@ static int lg_ld070wx3_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return lg_ld070wx3_hw_init(dev); } diff --git a/drivers/video/lm3533_backlight.c b/drivers/video/lm3533_backlight.c index 6b51fa0628e..7b87b6bd40b 100644 --- a/drivers/video/lm3533_backlight.c +++ b/drivers/video/lm3533_backlight.c @@ -7,6 +7,7 @@ #include <backlight.h> #include <dm.h> +#include <dm/ofnode.h> #include <i2c.h> #include <log.h> #include <linux/delay.h> @@ -17,56 +18,79 @@ #define LM3533_BL_MAX_BRIGHTNESS 0xFF #define LM3533_SINK_OUTPUT_CONFIG_1 0x10 -#define LM3533_CONTROL_BANK_A_PWM 0x14 +#define LM3533_CONTROL_PWM_BASE 0x14 +#define PWM_MAX GENMASK(5, 0) #define LM3533_CONTROL_BANK_AB_BRIGHTNESS 0x1A -#define LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT 0x1F +#define LM3533_CONTROL_FULLSCALE_CURRENT_BASE 0x1F +#define MAX_CURRENT_MIN 5000 +#define MAX_CURRENT_MAX 29800 +#define MAX_CURRENT_STEP 800 #define LM3533_CONTROL_BANK_ENABLE 0x27 #define LM3533_OVP_FREQUENCY_PWM_POLARITY 0x2C +#define BOOST_OVP_MASK GENMASK(2, 1) +#define BOOST_OVP_SHIFT 1 +#define BOOST_FREQ_MASK BIT(0) +#define BOOST_FREQ_SHIFT 0 #define LM3533_BRIGHTNESS_REGISTER_A 0x40 +#define LM3533_BOOST_OVP_16V 16000000UL +#define LM3533_BOOST_FREQ_500KHZ 500000UL + struct lm3533_backlight_priv { struct gpio_desc enable_gpio; u32 def_bl_lvl; + + /* Core */ + u32 boost_ovp; + u32 boost_freq; + + /* Backlight */ + u32 reg; + u16 max_current; /* 5000 - 29800 uA (800 uA step) */ + u8 pwm; /* 0 - 0x3f */ + bool linear; + bool hvled; }; static int lm3533_backlight_enable(struct udevice *dev) { struct lm3533_backlight_priv *priv = dev_get_priv(dev); + u8 val, id = priv->reg; int ret; - dm_gpio_set_value(&priv->enable_gpio, 1); - mdelay(5); + if (priv->linear) { + ret = dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, + BIT(2 * id + 1), BIT(2 * id + 1)); + if (ret) + return ret; + } - /* HVLED 1 & 2 are controlled by Bank A */ - ret = dm_i2c_reg_write(dev, LM3533_SINK_OUTPUT_CONFIG_1, 0x00); - if (ret) - return ret; + if (priv->hvled) { + ret = dm_i2c_reg_clrset(dev, LM3533_SINK_OUTPUT_CONFIG_1, + BIT(0) | BIT(1), id | id << 1); + if (ret) + return ret; + } - /* PWM input is disabled for CABC */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_PWM, 0x00); - if (ret) - return ret; + /* Set current */ + if (priv->max_current < MAX_CURRENT_MIN || priv->max_current > MAX_CURRENT_MAX) + return -EINVAL; - /* Linear & Control Bank A is configured for register Current control */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, 0x02); + val = (priv->max_current - MAX_CURRENT_MIN) / MAX_CURRENT_STEP; + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_FULLSCALE_CURRENT_BASE + id, val); if (ret) return ret; - /* Full-Scale Current (20.2mA) */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT, 0x13); - if (ret) - return ret; + /* Set PWM mask */ + if (priv->pwm > PWM_MAX) + return -EINVAL; - /* Control Bank A is enable */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_ENABLE, 0x01); + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_PWM_BASE + id, priv->pwm); if (ret) return ret; - ret = dm_i2c_reg_write(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, 0x0A); - if (ret) - return ret; - - return 0; + /* Enable Control Bank */ + return dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_ENABLE, BIT(id), BIT(id)); } static int lm3533_backlight_set_brightness(struct udevice *dev, int percent) @@ -92,6 +116,56 @@ static int lm3533_backlight_set_brightness(struct udevice *dev, int percent) return 0; } +static int lm3533_backlight_of_to_plat(struct udevice *dev) +{ + struct lm3533_backlight_priv *priv = dev_get_priv(dev); + ofnode child; + int ret; + + ret = gpio_request_by_name(dev, "enable-gpios", 0, + &priv->enable_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("Could not decode enable-gpios (%d)\n", ret); + return ret; + } + + priv->boost_ovp = dev_read_u32_default(dev, "ti,boost-ovp-microvolt", + LM3533_BOOST_OVP_16V); + + /* boost_ovp is defined in microvolts, convert to enum value */ + priv->boost_ovp = priv->boost_ovp / (8 * 1000 * 1000) - 2; + + priv->boost_freq = dev_read_u32_default(dev, "ti,boost-freq-hz", + LM3533_BOOST_FREQ_500KHZ); + + /* boost_freq is defined in Hz, convert to enum value */ + priv->boost_freq = priv->boost_freq / (500 * 1000) - 1; + + /* Backlight is one of children but has no dedicated driver */ + ofnode_for_each_subnode(child, dev_ofnode(dev)) { + if (ofnode_device_is_compatible(child, "ti,lm3533-backlight")) { + const char *node_name = ofnode_get_name(child); + + if (!strcmp(&node_name[10], "1")) + priv->reg = 1; + else + priv->reg = 0; + + priv->max_current = ofnode_read_u32_default(child, "ti,max-current-microamp", + 5000); + priv->pwm = ofnode_read_u32_default(child, "ti,pwm-config-mask", 0); + + priv->def_bl_lvl = ofnode_read_u32_default(child, "default-brightness", + LM3533_BL_MAX_BRIGHTNESS); + + priv->linear = ofnode_read_bool(child, "ti,linear-mapping-mode"); + priv->hvled = ofnode_read_bool(child, "ti,hardware-controlled"); + } + } + + return 0; +} + static int lm3533_backlight_probe(struct udevice *dev) { struct lm3533_backlight_priv *priv = dev_get_priv(dev); @@ -100,15 +174,22 @@ static int lm3533_backlight_probe(struct udevice *dev) if (device_get_uclass_id(dev->parent) != UCLASS_I2C) return -EPROTONOSUPPORT; - ret = gpio_request_by_name(dev, "enable-gpios", 0, - &priv->enable_gpio, GPIOD_IS_OUT); + dm_gpio_set_value(&priv->enable_gpio, 1); + mdelay(5); + + ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, + BOOST_FREQ_MASK, priv->boost_freq << BOOST_FREQ_SHIFT); if (ret) { - log_err("Could not decode enable-gpios (%d)\n", ret); + log_debug("%s: freq config failed %d\n", __func__, ret); return ret; } - priv->def_bl_lvl = dev_read_u32_default(dev, "default-brightness-level", - LM3533_BL_MAX_BRIGHTNESS); + ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, + BOOST_OVP_MASK, priv->boost_ovp << BOOST_OVP_SHIFT); + if (ret) { + log_debug("%s: ovp config failed %d\n", __func__, ret); + return ret; + } return 0; } @@ -127,6 +208,7 @@ U_BOOT_DRIVER(lm3533_backlight) = { .name = "lm3533_backlight", .id = UCLASS_PANEL_BACKLIGHT, .of_match = lm3533_backlight_ids, + .of_to_plat = lm3533_backlight_of_to_plat, .probe = lm3533_backlight_probe, .ops = &lm3533_backlight_ops, .priv_auto = sizeof(struct lm3533_backlight_priv), diff --git a/drivers/video/renesas-r61307.c b/drivers/video/renesas-r61307.c index a3697bce5ee..ef6fab1e953 100644 --- a/drivers/video/renesas-r61307.c +++ b/drivers/video/renesas-r61307.c @@ -254,17 +254,17 @@ static int renesas_r61307_hw_init(struct udevice *dev) return ret; } - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&priv->reset_gpio, 1); if (ret) { - log_debug("%s: changing reset-gpio failed (%d)\n", + log_debug("%s: entering reset failed (%d)\n", __func__, ret); return ret; } mdelay(5); - ret = dm_gpio_set_value(&priv->reset_gpio, 1); + ret = dm_gpio_set_value(&priv->reset_gpio, 0); if (ret) { - log_debug("%s: changing reset-gpio failed (%d)\n", + log_debug("%s: exiting reset failed (%d)\n", __func__, ret); return ret; } @@ -281,7 +281,8 @@ static int renesas_r61307_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; return renesas_r61307_hw_init(dev); } @@ -294,7 +295,7 @@ static const struct panel_ops renesas_r61307_ops = { static const struct udevice_id renesas_r61307_ids[] = { { .compatible = "koe,tx13d100vm0eaa" }, - { .compatible = "hitachi,tx13d100vm0eaa" }, + { .compatible = "hit,tx13d100vm0eaa" }, { } }; diff --git a/drivers/video/renesas-r69328.c b/drivers/video/renesas-r69328.c index 9861c3fef11..0954b04b62e 100644 --- a/drivers/video/renesas-r69328.c +++ b/drivers/video/renesas-r69328.c @@ -32,9 +32,11 @@ #define R69328_POWER_SET 0xD1 struct renesas_r69328_priv { + struct udevice *vdd; + struct udevice *vddio; + struct udevice *backlight; - struct gpio_desc enable_gpio; struct gpio_desc reset_gpio; }; @@ -159,10 +161,15 @@ static int renesas_r69328_of_to_plat(struct udevice *dev) return ret; } - ret = gpio_request_by_name(dev, "enable-gpios", 0, - &priv->enable_gpio, GPIOD_IS_OUT); + ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd); if (ret) { - log_err("could not decode enable-gpios (%d)\n", ret); + log_err("Cannot get vdd-supply: ret = %d\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); + if (ret) { + log_err("Cannot get vddio-supply: ret = %d\n", ret); return ret; } @@ -181,25 +188,32 @@ static int renesas_r69328_hw_init(struct udevice *dev) struct renesas_r69328_priv *priv = dev_get_priv(dev); int ret; - ret = dm_gpio_set_value(&priv->enable_gpio, 1); + ret = regulator_set_enable_if_allowed(priv->vddio, 1); if (ret) { - log_debug("%s: error changing enable-gpios (%d)\n", + log_debug("%s: enabling vddio-supply failed (%d)\n", __func__, ret); return ret; } mdelay(5); - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = regulator_set_enable_if_allowed(priv->vdd, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: enabling vdd-supply failed (%d)\n", __func__, ret); return ret; } - mdelay(5); ret = dm_gpio_set_value(&priv->reset_gpio, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error entering reset (%d)\n", + __func__, ret); + return ret; + } + mdelay(5); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error exiting reset (%d)\n", __func__, ret); return ret; } @@ -216,7 +230,8 @@ static int renesas_r69328_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; return renesas_r69328_hw_init(dev); } diff --git a/drivers/video/samsung-ltl106hl02.c b/drivers/video/samsung-ltl106hl02.c index 5e6c11c4be3..1efc9fca610 100644 --- a/drivers/video/samsung-ltl106hl02.c +++ b/drivers/video/samsung-ltl106hl02.c @@ -129,7 +129,7 @@ static int samsung_ltl106hl02_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return samsung_ltl106hl02_hw_init(dev); } diff --git a/drivers/video/sharp-lq101r1sx01.c b/drivers/video/sharp-lq101r1sx01.c index 5d8453fd796..4fdf0da8a94 100644 --- a/drivers/video/sharp-lq101r1sx01.c +++ b/drivers/video/sharp-lq101r1sx01.c @@ -255,6 +255,7 @@ static int sharp_lq101r1sx01_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_LPM; return sharp_lq101r1sx01_hw_init(dev); } diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig index f5c4843e119..598f9ea1f21 100644 --- a/drivers/video/tegra20/Kconfig +++ b/drivers/video/tegra20/Kconfig @@ -1,6 +1,11 @@ +config HOST1X_TEGRA + bool "NVIDIA Tegra host1x BUS support" + depends on SIMPLE_BUS + config VIDEO_TEGRA20 bool "Enable Display Controller support on Tegra20 and Tegra 30" depends on OF_CONTROL + select HOST1X_TEGRA help T20/T30 support video output to an attached LCD panel as well as other options such as HDMI. Only the LCD is supported in U-Boot. @@ -9,13 +14,22 @@ config VIDEO_TEGRA20 config VIDEO_DSI_TEGRA30 bool "Enable Tegra 30 DSI support" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_TEGRA20 select VIDEO_MIPI_DSI help T30 has native support for DSI panels. This option enables support for such panels which can be used on endeavoru and tf600t. +config VIDEO_HDMI_TEGRA + bool "Enable Tegra HDMI support" + depends on VIDEO_BRIDGE && DM_I2C + select I2C_EDID + select VIDEO_TEGRA20 + help + Tegra has native support for HDMI. This option enables support + for such connection and can be used for any supported device. + config TEGRA_BACKLIGHT_PWM bool "Enable Tegra DC PWM backlight support" depends on BACKLIGHT diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile index a75aea2a875..78521405749 100644 --- a/drivers/video/tegra20/Makefile +++ b/drivers/video/tegra20/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ +obj-$(CONFIG_HOST1X_TEGRA) += tegra-host1x.o obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o +obj-$(CONFIG_VIDEO_HDMI_TEGRA) += tegra-hdmi.o obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index 16a2b5281bf..1f43153ff27 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -1,16 +1,19 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> */ #include <backlight.h> #include <cpu_func.h> #include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <fdtdec.h> #include <log.h> #include <panel.h> #include <video.h> +#include <video_bridge.h> #include <asm/system.h> #include <asm/io.h> #include <asm/arch/clock.h> @@ -31,7 +34,8 @@ struct tegra_lcd_priv { int height; /* height in pixels */ enum video_log2_bpp log2_bpp; /* colour depth */ struct display_timing timing; - struct udevice *panel; + struct udevice *panel; /* Panels attached to RGB */ + struct udevice *bridge; /* Bridge linked with DC */ struct dc_ctlr *dc; /* Display controller regmap */ const struct tegra_dc_soc_info *soc; fdt_addr_t frame_buffer; /* Address of frame buffer */ @@ -319,11 +323,6 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, / priv->pixel_clock) - 2; log_debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); - /* - * HOST1X is init by default at 150MHz with PLLC as parent - */ - clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_CGENERAL, - 150 * 1000000); clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, rate); @@ -378,11 +377,12 @@ static int tegra_lcd_probe(struct udevice *dev) } /* Get shift clock divider from Tegra DSI if used */ - if (!strcmp(priv->panel->name, TEGRA_DSI_A) || - !strcmp(priv->panel->name, TEGRA_DSI_B)) { - struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel); + if (priv->bridge) { + if (!strcmp(priv->bridge->driver->name, "tegra_dsi")) { + struct tegra_dc_plat *dc_plat = dev_get_plat(priv->bridge); - priv->scdiv = dc_plat->scdiv; + priv->scdiv = dc_plat->scdiv; + } } /* Clean the framebuffer area */ @@ -395,10 +395,20 @@ static int tegra_lcd_probe(struct udevice *dev) return ret; } - ret = panel_enable_backlight(priv->panel); - if (ret) { - log_debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); - return ret; + if (priv->panel) { + ret = panel_enable_backlight(priv->panel); + if (ret) { + log_debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); + return ret; + } + } + + if (priv->bridge) { + ret = video_bridge_attach(priv->bridge); + if (ret) { + log_debug("%s: Cannot attach bridge, ret=%d\n", __func__, ret); + return ret; + } } mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, @@ -413,17 +423,129 @@ static int tegra_lcd_probe(struct udevice *dev) log_debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, plat->size); - return panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); + if (priv->panel) { + ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); + if (ret) + return ret; + } + + if (priv->bridge) { + ret = video_bridge_set_backlight(priv->bridge, BACKLIGHT_DEFAULT); + if (ret) + return ret; + } + + return 0; +} + +static int tegra_lcd_configure_rgb(struct udevice *dev, ofnode rgb) +{ + struct tegra_lcd_priv *priv = dev_get_priv(dev); + ofnode remote; + int ret; + + /* DC can have only 1 port */ + remote = ofnode_graph_get_remote_node(rgb, -1, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel); + if (!ret) + return 0; + + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &priv->bridge); + if (!ret) + return 0; + + /* Try legacy method if graph did not work */ + remote = ofnode_parse_phandle(rgb, "nvidia,panel", 0); + if (!ofnode_valid(remote)) + return -EINVAL; + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel); + if (ret) { + log_debug("%s: Cannot find panel for '%s' (ret=%d)\n", + __func__, dev->name, ret); + + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, + &priv->bridge); + if (ret) { + log_err("%s: Cannot find panel or bridge for '%s' (ret=%d)\n", + __func__, dev->name, ret); + return ret; + } + } + + return 0; +} + +static int tegra_lcd_configure_internal(struct udevice *dev) +{ + struct tegra_lcd_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat; + ofnode host1x = ofnode_get_parent(dev_ofnode(dev)); + ofnode node; + int ret; + + switch (priv->pipe) { + case 0: /* DC0 is usually used for DSI */ + /* Check for ganged DSI configuration */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node) && + ofnode_read_bool(node, "nvidia,ganged-mode")) + goto exit; + + /* If no master DSI found loop for any active DSI */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node)) + goto exit; + + log_err("%s: failed to find DSI device for '%s'\n", + __func__, dev->name); + + return -ENODEV; + case 1: /* DC1 is usually used for HDMI */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "hdmi")) + goto exit; + + log_err("%s: failed to find HDMI device for '%s'\n", + __func__, dev->name); + + return -ENODEV; + default: + log_debug("Unsupported DC selection\n"); + return -EINVAL; + } + +exit: + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, node, &priv->bridge); + if (ret) { + log_err("%s: failed to get DSI/HDMI device for '%s' (ret %d)\n", + __func__, dev->name, ret); + return ret; + } + + priv->clk_parent = devm_clk_get(priv->bridge, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DC clock parent from DSI/HDMI: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + dc_plat = dev_get_plat(priv->bridge); + + /* Fill the platform data for internal devices */ + dc_plat->dev = dev; + dc_plat->dc = priv->dc; + dc_plat->pipe = priv->pipe; + + return 0; } static int tegra_lcd_of_to_plat(struct udevice *dev) { struct tegra_lcd_priv *priv = dev_get_priv(dev); - const void *blob = gd->fdt_blob; struct display_timing *timing; - int node = dev_of_offset(dev); - int panel_node; - int rgb; + ofnode rgb; int ret; priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev); @@ -451,44 +573,42 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) priv->rotation = dev_read_bool(dev, "nvidia,180-rotation"); priv->pipe = dev_read_u32_default(dev, "nvidia,head", 0); - rgb = fdt_subnode_offset(blob, node, "rgb"); - if (rgb < 0) { - log_debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n", - __func__, dev->name, rgb); - return -EINVAL; - } - /* - * Sadly the panel phandle is in an rgb subnode so we cannot use - * uclass_get_device_by_phandle(). + * Usual logic of Tegra video routing should be next: + * 1. Check rgb subnode for RGB/LVDS panels or bridges + * 2. If none found, then iterate through bridges bound, + * looking for DSIA or DSIB for DC0 and HDMI for DC1. + * If none of above is valid, then configuration is not + * valid. */ - panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); - if (panel_node < 0) { - 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) { - log_debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, - dev->name, ret); - return ret; + rgb = dev_read_subnode(dev, "rgb"); + if (ofnode_valid(rgb) && ofnode_is_enabled(rgb)) { + /* RGB is available, use it */ + ret = tegra_lcd_configure_rgb(dev, rgb); + if (ret) + return ret; + } else { + /* RGB is not available, check for internal devices */ + ret = tegra_lcd_configure_internal(dev); + if (ret) + return ret; } - /* Fill the platform data for internal devices */ - if (!strcmp(priv->panel->name, TEGRA_DSI_A) || - !strcmp(priv->panel->name, TEGRA_DSI_B)) { - struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel); - - dc_plat->dev = dev; - dc_plat->dc = priv->dc; - dc_plat->pipe = priv->pipe; + if (priv->panel) { + ret = panel_get_display_timing(priv->panel, &priv->timing); + if (ret) { + ret = ofnode_decode_display_timing(rgb, 0, &priv->timing); + if (ret) { + log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n", + __func__, dev->name, ret); + return -EINVAL; + } + } } - ret = panel_get_display_timing(priv->panel, &priv->timing); - if (ret) { - ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing); + if (priv->bridge) { + ret = video_bridge_get_display_timing(priv->bridge, &priv->timing); if (ret) { log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n", __func__, dev->name, ret); @@ -508,23 +628,13 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) static int tegra_lcd_bind(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); - const void *blob = gd->fdt_blob; - int node = dev_of_offset(dev); - int rgb; - - rgb = fdt_subnode_offset(blob, node, "rgb"); - if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb)) - return -ENODEV; plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * (1 << LCD_MAX_LOG2_BPP) / 8; - return 0; + return dm_scan_fdt_dev(dev); } -static const struct video_ops tegra_lcd_ops = { -}; - static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .has_timer = true, .has_rgb = true, @@ -565,7 +675,6 @@ U_BOOT_DRIVER(tegra_lcd) = { .name = "tegra_lcd", .id = UCLASS_VIDEO, .of_match = tegra_lcd_ids, - .ops = &tegra_lcd_ops, .bind = tegra_lcd_bind, .probe = tegra_lcd_probe, .of_to_plat = tegra_lcd_of_to_plat, diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra20/tegra-dc.h index 7d0c189ec80..2a4013b3355 100644 --- a/drivers/video/tegra20/tegra-dc.h +++ b/drivers/video/tegra20/tegra-dc.h @@ -14,9 +14,6 @@ /* arch-tegra/dc exists only because T124 uses it */ #include <asm/arch-tegra/dc.h> -#define TEGRA_DSI_A "dsi@54300000" -#define TEGRA_DSI_B "dsi@54400000" - struct tegra_dc_plat { struct udevice *dev; /* Display controller device */ struct dc_ctlr *dc; /* Display controller regmap */ diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 9f39ac7589b..a2a22fa0fe2 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -11,6 +11,7 @@ #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> +#include <video_bridge.h> #include <panel.h> #include <reset.h> #include <linux/delay.h> @@ -250,6 +251,9 @@ static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; + if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) + value |= DSI_HOST_CONTROL_HS; + /* * The host FIFO has a maximum of 64 words, so larger transmissions * need to use the video FIFO. @@ -991,7 +995,7 @@ static int tegra_dsi_ganged_probe(struct udevice *dev) struct tegra_dsi_priv *mpriv = dev_get_priv(dev); struct udevice *gangster; - uclass_get_device_by_phandle(UCLASS_PANEL, dev, + uclass_get_device_by_phandle(UCLASS_VIDEO_BRIDGE, dev, "nvidia,ganged-mode", &gangster); if (gangster) { /* Ganged mode is set */ @@ -1118,8 +1122,8 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) return 0; } -static const struct panel_ops tegra_dsi_bridge_ops = { - .enable_backlight = tegra_dsi_encoder_enable, +static const struct video_bridge_ops tegra_dsi_bridge_ops = { + .attach = tegra_dsi_encoder_enable, .set_backlight = tegra_dsi_bridge_set_panel, .get_display_timing = tegra_dsi_panel_timings, }; @@ -1133,7 +1137,7 @@ static const struct udevice_id tegra_dsi_bridge_ids[] = { U_BOOT_DRIVER(tegra_dsi) = { .name = "tegra_dsi", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = tegra_dsi_bridge_ids, .ops = &tegra_dsi_bridge_ops, .bind = dm_scan_fdt_dev, diff --git a/drivers/video/tegra20/tegra-hdmi.c b/drivers/video/tegra20/tegra-hdmi.c new file mode 100644 index 00000000000..bda69919d92 --- /dev/null +++ b/drivers/video/tegra20/tegra-hdmi.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <clk.h> +#include <dm.h> +#include <edid.h> +#include <i2c.h> +#include <log.h> +#include <misc.h> +#include <panel.h> +#include <reset.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/time.h> +#include <power/regulator.h> +#include <video_bridge.h> + +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> + +#include "tegra-dc.h" +#include "tegra-hdmi.h" + +#define DDCCI_ENTRY_ADDR 0x37 +#define DDCCI_SOURSE_ADDR 0x51 +#define DDCCI_COMMAND_WRITE 0x03 +#define DDCCI_CTRL_BRIGHTNESS 0x10 + +#define HDMI_EDID_I2C_ADDR 0x50 +#define HDMI_REKEY_DEFAULT 56 + +static const char * const hdmi_supplies[] = { + "hdmi-supply", "pll-supply", "vdd-supply" +}; + +struct tmds_config { + unsigned int pclk; + u32 pll0; + u32 pll1; + u32 pe_current; + u32 drive_current; + u32 peak_current; +}; + +struct tegra_hdmi_config { + const struct tmds_config *tmds; + unsigned int num_tmds; + unsigned int max_pclk; + + /* to be filled */ +}; + +struct tegra_hdmi_priv { + struct hdmi_ctlr *hdmi_regmap; + + struct udevice *supplies[ARRAY_SIZE(hdmi_supplies)]; + struct udevice *hdmi_ddc; + + struct gpio_desc hpd; /* hotplug detection gpio */ + struct display_timing timing; + + struct clk *clk; + struct clk *clk_parent; + + int panel_bits_per_colourp; + const struct tegra_hdmi_config *config; +}; + +/* 1280x720p 60hz: EIA/CEA-861-B Format 4 */ +static struct display_timing default_720p_timing = { + .pixelclock.typ = 74250000, + .hactive.typ = 1280, + .hfront_porch.typ = 110, + .hback_porch.typ = 220, + .hsync_len.typ = 40, + .vactive.typ = 720, + .vfront_porch.typ = 5, + .vback_porch.typ = 20, + .vsync_len.typ = 5, + .flags = DISPLAY_FLAGS_HSYNC_HIGH | + DISPLAY_FLAGS_VSYNC_HIGH, +}; + +static const struct tmds_config tegra20_tmds_config[] = { + { /* slow pixel clock modes */ + .pclk = 27000000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | + SOR_PLL_TX_REG_LOAD(3), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE, + .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | + PE_CURRENT1(PE_CURRENT_0_0_mA) | + PE_CURRENT2(PE_CURRENT_0_0_mA) | + PE_CURRENT3(PE_CURRENT_0_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), + }, + { /* high pixel clock modes */ + .pclk = UINT_MAX, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | + SOR_PLL_TX_REG_LOAD(3), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) | + PE_CURRENT1(PE_CURRENT_6_0_mA) | + PE_CURRENT2(PE_CURRENT_6_0_mA) | + PE_CURRENT3(PE_CURRENT_6_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), + }, +}; + +static const struct tmds_config tegra30_tmds_config[] = { + { /* 480p modes */ + .pclk = 27000000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE, + .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | + PE_CURRENT1(PE_CURRENT_0_0_mA) | + PE_CURRENT2(PE_CURRENT_0_0_mA) | + PE_CURRENT3(PE_CURRENT_0_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, { /* 720p modes */ + .pclk = 74250000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | + PE_CURRENT1(PE_CURRENT_5_0_mA) | + PE_CURRENT2(PE_CURRENT_5_0_mA) | + PE_CURRENT3(PE_CURRENT_5_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, { /* 1080p modes */ + .pclk = UINT_MAX, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(3) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | + PE_CURRENT1(PE_CURRENT_5_0_mA) | + PE_CURRENT2(PE_CURRENT_5_0_mA) | + PE_CURRENT3(PE_CURRENT_5_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, +}; + +static void tegra_dc_enable_controller(struct udevice *dev) +{ + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct dc_ctlr *dc = dc_plat->dc; + u32 value; + + value = readl(&dc->disp.disp_win_opt); + value |= HDMI_ENABLE; + writel(value, &dc->disp.disp_win_opt); + + writel(GENERAL_UPDATE, &dc->cmd.state_ctrl); + writel(GENERAL_ACT_REQ, &dc->cmd.state_ctrl); +} + +static void tegra_hdmi_setup_tmds(struct tegra_hdmi_priv *priv, + const struct tmds_config *tmds) +{ + struct hdmi_ctlr *hdmi = priv->hdmi_regmap; + u32 value; + + writel(tmds->pll0, &hdmi->nv_pdisp_sor_pll0); + writel(tmds->pll1, &hdmi->nv_pdisp_sor_pll1); + writel(tmds->pe_current, &hdmi->nv_pdisp_pe_current); + + writel(tmds->drive_current, &hdmi->nv_pdisp_sor_lane_drive_current); + + value = readl(&hdmi->nv_pdisp_sor_lane_drive_current); + value |= BIT(31); + writel(value, &hdmi->nv_pdisp_sor_lane_drive_current); +} + +static int tegra_hdmi_encoder_enable(struct udevice *dev) +{ + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct dc_ctlr *dc = dc_plat->dc; + struct display_timing *dt = &priv->timing; + struct hdmi_ctlr *hdmi = priv->hdmi_regmap; + unsigned long rate, div82; + unsigned int pulse_start, rekey; + int retries = 1000; + u32 value; + int i; + + /* power up sequence */ + value = readl(&hdmi->nv_pdisp_sor_pll0); + value &= ~SOR_PLL_PDBG; + writel(value, &hdmi->nv_pdisp_sor_pll0); + + udelay(20); + + value = readl(&hdmi->nv_pdisp_sor_pll0); + value &= ~SOR_PLL_PWR; + writel(value, &hdmi->nv_pdisp_sor_pll0); + + writel(VSYNC_H_POSITION(1), &dc->disp.disp_timing_opt); + writel(DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE_888, + &dc->disp.disp_color_ctrl); + + /* video_preamble uses h_pulse2 */ + pulse_start = 1 + dt->hsync_len.typ + dt->hback_porch.typ - 10; + + writel(H_PULSE2_ENABLE, &dc->disp.disp_signal_opt0); + + value = PULSE_MODE_NORMAL | PULSE_POLARITY_HIGH | + PULSE_QUAL_VACTIVE | PULSE_LAST_END_A; + writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_ctrl); + + value = PULSE_START(pulse_start) | PULSE_END(pulse_start + 8); + writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_pos[H_PULSE0_POSITION_A]); + + value = VSYNC_WINDOW_END(0x210) | VSYNC_WINDOW_START(0x200) | + VSYNC_WINDOW_ENABLE; + writel(value, &hdmi->nv_pdisp_hdmi_vsync_window); + + if (dc_plat->pipe) + value = HDMI_SRC_DISPLAYB; + else + value = HDMI_SRC_DISPLAYA; + + if (dt->hactive.typ == 720 && (dt->vactive.typ == 480 || + dt->vactive.typ == 576)) + writel(value | ARM_VIDEO_RANGE_FULL, + &hdmi->nv_pdisp_input_control); + else + writel(value | ARM_VIDEO_RANGE_LIMITED, + &hdmi->nv_pdisp_input_control); + + rate = clock_get_periph_rate(priv->clk->id, priv->clk_parent->id); + div82 = rate / USEC_PER_SEC * 4; + value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82); + writel(value, &hdmi->nv_pdisp_sor_refclk); + + rekey = HDMI_REKEY_DEFAULT; + value = HDMI_CTRL_REKEY(rekey); + value |= HDMI_CTRL_MAX_AC_PACKET((dt->hsync_len.typ + dt->hback_porch.typ + + dt->hfront_porch.typ - rekey - 18) / 32); + writel(value, &hdmi->nv_pdisp_hdmi_ctrl); + + /* TMDS CONFIG */ + for (i = 0; i < priv->config->num_tmds; i++) { + if (dt->pixelclock.typ <= priv->config->tmds[i].pclk) { + tegra_hdmi_setup_tmds(priv, &priv->config->tmds[i]); + break; + } + } + + writel(SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | SOR_SEQ_PD_PC(8) | + SOR_SEQ_PD_PC_ALT(8), &hdmi->nv_pdisp_sor_seq_ctl); + + value = SOR_SEQ_INST_WAIT_TIME(1) | SOR_SEQ_INST_WAIT_UNITS_VSYNC | + SOR_SEQ_INST_HALT | SOR_SEQ_INST_PIN_A_LOW | + SOR_SEQ_INST_PIN_B_LOW | SOR_SEQ_INST_DRIVE_PWM_OUT_LO; + + writel(value, &hdmi->nv_pdisp_sor_seq_inst0); + writel(value, &hdmi->nv_pdisp_sor_seq_inst8); + + value = readl(&hdmi->nv_pdisp_sor_cstm); + + value &= ~SOR_CSTM_ROTCLK(~0); + value |= SOR_CSTM_ROTCLK(2); + value |= SOR_CSTM_PLLDIV; + value &= ~SOR_CSTM_LVDS_ENABLE; + value &= ~SOR_CSTM_MODE_MASK; + value |= SOR_CSTM_MODE_TMDS; + + writel(value, &hdmi->nv_pdisp_sor_cstm); + + /* start SOR */ + writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL | + SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_TRIGGER, + &hdmi->nv_pdisp_sor_pwr); + writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL | + SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_DONE, + &hdmi->nv_pdisp_sor_pwr); + + do { + if (--retries < 0) + return -ETIME; + value = readl(&hdmi->nv_pdisp_sor_pwr); + } while (value & SOR_PWR_SETTING_NEW_PENDING); + + value = SOR_STATE_ASY_CRCMODE_COMPLETE | + SOR_STATE_ASY_OWNER_HEAD0 | + SOR_STATE_ASY_SUBOWNER_BOTH | + SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A | + SOR_STATE_ASY_DEPOL_POS; + + /* setup sync polarities */ + if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH) + value |= SOR_STATE_ASY_HSYNCPOL_POS; + + if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW) + value |= SOR_STATE_ASY_HSYNCPOL_NEG; + + if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH) + value |= SOR_STATE_ASY_VSYNCPOL_POS; + + if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW) + value |= SOR_STATE_ASY_VSYNCPOL_NEG; + + writel(value, &hdmi->nv_pdisp_sor_state2); + + value = SOR_STATE_ASY_HEAD_OPMODE_AWAKE | SOR_STATE_ASY_ORMODE_NORMAL; + writel(value, &hdmi->nv_pdisp_sor_state1); + + writel(0, &hdmi->nv_pdisp_sor_state0); + writel(SOR_STATE_UPDATE, &hdmi->nv_pdisp_sor_state0); + writel(value | SOR_STATE_ATTACHED, + &hdmi->nv_pdisp_sor_state1); + writel(0, &hdmi->nv_pdisp_sor_state0); + + tegra_dc_enable_controller(dev); + + return 0; +} + +/* DDC/CI backlight control */ +static int tegra_hdmi_set_connector(struct udevice *dev, int percent) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct udevice *ddc_entry; + struct i2c_msg msg[1]; + u8 checksum = DDCCI_ENTRY_ADDR << 1; + int i, ret; + + ret = dm_i2c_probe(priv->hdmi_ddc, DDCCI_ENTRY_ADDR, 0, &ddc_entry); + if (ret) { + log_debug("%s: cannot probe DDC/CI entry: error %d\n", + __func__, ret); + return 0; + } + + /* + * payload[1] is length: hithest bit OR last 4 bits indicate + * the number of following bytes (excluding checksum) + */ + u8 payload[7] = { DDCCI_SOURSE_ADDR, BIT(7) | (sizeof(payload) - 3), + DDCCI_COMMAND_WRITE, DDCCI_CTRL_BRIGHTNESS, + (u8)(percent & 0xff), (u8)(percent & 0xff), 0 }; + + /* DDC/CI checksum is a simple XOR of all preceding bytes */ + for (i = 0; i < (sizeof(payload) - 1); i++) + checksum ^= payload[i]; + + payload[6] = checksum; + + msg->addr = DDCCI_ENTRY_ADDR; + msg->flags = 0; + msg->len = sizeof(payload); + msg->buf = payload; + + dm_i2c_xfer(ddc_entry, msg, 1); + + return 0; +} + +static int tegra_hdmi_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +static void tegra_hdmi_init_clocks(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + u32 n = priv->timing.pixelclock.typ * 2 / USEC_PER_SEC; + + 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(priv->clk_parent->id, n, 12, 0, 8); + break; + + case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ + clock_set_rate(priv->clk_parent->id, n, 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(priv->clk_parent->id, n, 13, 0, 8); + break; + + case CLOCK_OSC_FREQ_19_2: + case CLOCK_OSC_FREQ_38_4: + default: + /* + * These are not supported. + */ + break; + } + + clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, + priv->timing.pixelclock.typ); +} + +static bool tegra_hdmi_mode_valid(void *hdmi_priv, const struct display_timing *timing) +{ + struct tegra_hdmi_priv *priv = hdmi_priv; + + if (timing->pixelclock.typ > priv->config->max_pclk) + return false; + + return true; +} + +static int tegra_hdmi_decode_edid(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct udevice *hdmi_edid; + uchar edid_buf[EDID_SIZE] = { 0 }; + int i, ret; + + /* Poll for 1 sec in case EDID is not ready right after hpd */ + for (i = 0; i < 10; i++) { + ret = dm_i2c_probe(priv->hdmi_ddc, HDMI_EDID_I2C_ADDR, 0, + &hdmi_edid); + if (!ret) + break; + + mdelay(100); + } + if (ret) { + log_debug("%s: cannot probe EDID: error %d\n", + __func__, ret); + return ret; + } + + ret = dm_i2c_read(hdmi_edid, 0, edid_buf, sizeof(edid_buf)); + if (ret) { + log_debug("%s: cannot dump EDID buffer: error %d\n", + __func__, ret); + return ret; + } + + ret = edid_get_timing_validate(edid_buf, sizeof(edid_buf), &priv->timing, + &priv->panel_bits_per_colourp, + tegra_hdmi_mode_valid, priv); + if (ret) { + log_debug("%s: cannot decode EDID info: error %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int tegra_hdmi_wait_hpd(struct tegra_hdmi_priv *priv) +{ + int i; + + /* Poll 1 second for HPD signal */ + for (i = 0; i < 10; i++) { + if (dm_gpio_get_value(&priv->hpd)) + return 0; + + mdelay(100); + } + + return -ETIMEDOUT; +} + +static int tegra_hdmi_probe(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct reset_ctl reset_ctl; + int i, ret; + + priv->hdmi_regmap = (struct hdmi_ctlr *)dev_read_addr_ptr(dev); + if (!priv->hdmi_regmap) { + log_debug("%s: no display controller address\n", __func__); + return -EINVAL; + } + + priv->config = (struct tegra_hdmi_config *)dev_get_driver_data(dev); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get HDMI 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 HDMI clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + for (i = 0; i < ARRAY_SIZE(hdmi_supplies); i++) { + ret = device_get_supply_regulator(dev, hdmi_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + hdmi_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ret = regulator_set_enable_if_allowed(priv->supplies[i], true); + if (ret && ret != -ENOSYS) { + log_debug("%s: cannot enable %s: error %d\n", + __func__, hdmi_supplies[i], ret); + return ret; + } + } + + ret = reset_get_by_name(dev, "hdmi", &reset_ctl); + if (ret) { + log_debug("%s: reset_get_by_name() failed: %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_I2C, dev, + "nvidia,ddc-i2c-bus", + &priv->hdmi_ddc); + if (ret) { + log_debug("%s: cannot get hdmi ddc i2c bus: error %d\n", + __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "nvidia,hpd-gpio", 0, + &priv->hpd, GPIOD_IS_IN); + if (ret) { + log_debug("%s: Could not decode hpd-gpios (%d)\n", + __func__, ret); + return ret; + } + + /* wait for connector */ + ret = tegra_hdmi_wait_hpd(priv); + if (ret) { + /* HPD failed, use default timings */ + memcpy(&priv->timing, &default_720p_timing, + sizeof(default_720p_timing)); + } else { + ret = tegra_hdmi_decode_edid(dev); + if (ret) + memcpy(&priv->timing, &default_720p_timing, + sizeof(default_720p_timing)); + } + + reset_assert(&reset_ctl); + tegra_hdmi_init_clocks(dev); + + mdelay(2); + reset_deassert(&reset_ctl); + + return 0; +} + +static const struct tegra_hdmi_config tegra20_hdmi_config = { + .tmds = tegra20_tmds_config, + .num_tmds = ARRAY_SIZE(tegra20_tmds_config), + .max_pclk = 148500000, /* 1080p */ +}; + +static const struct tegra_hdmi_config tegra30_hdmi_config = { + .tmds = tegra30_tmds_config, + .num_tmds = ARRAY_SIZE(tegra30_tmds_config), + .max_pclk = 148500000, /* 1080p */ +}; + +static const struct video_bridge_ops tegra_hdmi_ops = { + .attach = tegra_hdmi_encoder_enable, + .set_backlight = tegra_hdmi_set_connector, + .get_display_timing = tegra_hdmi_timings, +}; + +static const struct udevice_id tegra_hdmi_ids[] = { + { + .compatible = "nvidia,tegra20-hdmi", + .data = (ulong)&tegra20_hdmi_config + }, { + .compatible = "nvidia,tegra30-hdmi", + .data = (ulong)&tegra30_hdmi_config + }, { + /* sentinel */ + } +}; + +U_BOOT_DRIVER(tegra_hdmi) = { + .name = "tegra_hdmi", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = tegra_hdmi_ids, + .ops = &tegra_hdmi_ops, + .probe = tegra_hdmi_probe, + .plat_auto = sizeof(struct tegra_dc_plat), + .priv_auto = sizeof(struct tegra_hdmi_priv), +}; diff --git a/drivers/video/tegra20/tegra-hdmi.h b/drivers/video/tegra20/tegra-hdmi.h new file mode 100644 index 00000000000..d17655973e3 --- /dev/null +++ b/drivers/video/tegra20/tegra-hdmi.h @@ -0,0 +1,648 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + */ + +#ifndef _TEGRA_HDMI_H +#define _TEGRA_HDMI_H + +#ifndef __ASSEMBLY__ +#include <linux/bitops.h> +#endif + +/* Register definitions for the Tegra high-definition multimedia interface */ + +/* High-Definition Multimedia Interface (HDMI_) regs */ +struct hdmi_ctlr { + /* Address 0x000 ~ 0x0d2 */ + uint ctxsw; /* _CTXSW */ /* 0x00 */ + + uint nv_pdisp_sor_state0; /* _NV_PDISP_SOR_STATE0 */ + uint nv_pdisp_sor_state1; /* _NV_PDISP_SOR_STATE1 */ + uint nv_pdisp_sor_state2; /* _NV_PDISP_SOR_STATE2 */ + + uint nv_pdisp_rg_hdcp_an_msb; /* _NV_PDISP_RG_HDCP_AN_MSB */ + uint nv_pdisp_rg_hdcp_an_lsb; /* _NV_PDISP_RG_HDCP_AN_LSB */ + uint nv_pdisp_rg_hdcp_cn_msb; /* _NV_PDISP_RG_HDCP_CN_MSB */ + uint nv_pdisp_rg_hdcp_cn_lsb; /* _NV_PDISP_RG_HDCP_CN_LSB */ + uint nv_pdisp_rg_hdcp_aksv_msb; /* _NV_PDISP_RG_HDCP_AKSV_MSB */ + uint nv_pdisp_rg_hdcp_aksv_lsb; /* _NV_PDISP_RG_HDCP_AKSV_LSB */ + uint nv_pdisp_rg_hdcp_bksv_msb; /* _NV_PDISP_RG_HDCP_BKSV_MSB */ + uint nv_pdisp_rg_hdcp_bksv_lsb; /* _NV_PDISP_RG_HDCP_BKSV_LSB */ + uint nv_pdisp_rg_hdcp_cksv_msb; /* _NV_PDISP_RG_HDCP_CKSV_MSB */ + uint nv_pdisp_rg_hdcp_cksv_lsb; /* _NV_PDISP_RG_HDCP_CKSV_LSB */ + uint nv_pdisp_rg_hdcp_dksv_msb; /* _NV_PDISP_RG_HDCP_DKSV_MSB */ + uint nv_pdisp_rg_hdcp_dksv_lsb; /* _NV_PDISP_RG_HDCP_DKSV_LSB */ + uint nv_pdisp_rg_hdcp_ctrl; /* _NV_PDISP_RG_HDCP_CTRL */ /* 0x10 */ + uint nv_pdisp_rg_hdcp_cmode; /* _NV_PDISP_RG_HDCP_CMODE */ + uint nv_pdisp_rg_hdcp_mprime_msb; /* _NV_PDISP_RG_HDCP_MPRIME_MSB */ + uint nv_pdisp_rg_hdcp_mprime_lsb; /* _NV_PDISP_RG_HDCP_MPRIME_LSB */ + uint nv_pdisp_rg_hdcp_sprime_msb; /* _NV_PDISP_RG_HDCP_SPRIME_MSB */ + uint nv_pdisp_rg_hdcp_sprime_lsb2; /* _NV_PDISP_RG_HDCP_SPRIME_LSB2 */ + uint nv_pdisp_rg_hdcp_sprime_lsb1; /* _NV_PDISP_RG_HDCP_SPRIME_LSB1 */ + uint nv_pdisp_rg_hdcp_ri; /* _NV_PDISP_RG_HDCP_RI */ + uint nv_pdisp_rg_hdcp_cs_msb; /* _NV_PDISP_RG_HDCP_CS_MSB */ + uint nv_pdisp_rg_hdcp_cs_lsb; /* _NV_PDISP_RG_HDCP_CS_LSB */ + + uint nv_pdisp_hdmi_audio_emu0; /* _NV_PDISP_HDMI_AUDIO_EMU0 */ + uint nv_pdisp_hdmi_audio_emu_rdata0; /* _NV_PDISP_HDMI_AUDIO_EMU_RDATA0 */ + uint nv_pdisp_hdmi_audio_emu1; /* _NV_PDISP_HDMI_AUDIO_EMU1 */ + uint nv_pdisp_hdmi_audio_emu2; /* _NV_PDISP_HDMI_AUDIO_EMU2 */ + uint nv_pdisp_hdmi_audio_infoframe_ctrl; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL */ + uint nv_pdisp_hdmi_audio_infoframe_status; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS */ + uint nv_pdisp_hdmi_audio_infoframe_header; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER */ /* 0x20 */ + uint nv_pdisp_hdmi_audio_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_audio_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH */ + + uint nv_pdisp_hdmi_avi_infoframe_ctrl; /* _NV_PDISP_HDMI_AVI_INFOFRAME_CTRL */ + uint nv_pdisp_hdmi_avi_infoframe_status; /* _NV_PDISP_HDMI_AVI_INFOFRAME_STATUS */ + uint nv_pdisp_hdmi_avi_infoframe_header; /* _NV_PDISP_HDMI_AVI_INFOFRAME_HEADER */ + uint nv_pdisp_hdmi_avi_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_avi_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH */ + uint nv_pdisp_hdmi_avi_infoframe_subpack1_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW */ + uint nv_pdisp_hdmi_avi_infoframe_subpack1_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH */ + + uint nv_pdisp_hdmi_generic_ctrl; /* _NV_PDISP_HDMI_GENERIC_CTRL */ + uint nv_pdisp_hdmi_generic_status; /* _NV_PDISP_HDMI_GENERIC_STATUS */ + uint nv_pdisp_hdmi_generic_header; /* _NV_PDISP_HDMI_GENERIC_HEADER */ + uint nv_pdisp_hdmi_generic_subpack0_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_generic_subpack0_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH */ + uint nv_pdisp_hdmi_generic_subpack1_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW */ + uint nv_pdisp_hdmi_generic_subpack1_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH */ + uint nv_pdisp_hdmi_generic_subpack2_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW */ + uint nv_pdisp_hdmi_generic_subpack2_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH */ + uint nv_pdisp_hdmi_generic_subpack3_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW */ + uint nv_pdisp_hdmi_generic_subpack3_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH */ + + uint nv_pdisp_hdmi_acr_ctrl; /* _NV_PDISP_HDMI_ACR_CTRL */ + uint nv_pdisp_hdmi_acr_0320_subpack_low; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0320_subpack_high; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0441_subpack_low; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0441_subpack_high; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0882_subpack_low; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0882_subpack_high; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_1764_subpack_low; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_1764_subpack_high; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0480_subpack_low; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0480_subpack_high; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0960_subpack_low; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0960_subpack_high; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_1920_subpack_low; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_1920_subpack_high; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH */ + + uint nv_pdisp_hdmi_ctrl; /* _NV_PDISP_HDMI_CTRL */ + uint nv_pdisp_hdmi_vsync_keepout; /* _NV_PDISP_HDMI_VSYNC_KEEPOUT */ + uint nv_pdisp_hdmi_vsync_window; /* _NV_PDISP_HDMI_VSYNC_WINDOW */ + uint nv_pdisp_hdmi_gcp_ctrl; /* _NV_PDISP_HDMI_GCP_CTRL */ + uint nv_pdisp_hdmi_gcp_status; /* _NV_PDISP_HDMI_GCP_STATUS */ + uint nv_pdisp_hdmi_gcp_subpack; /* _NV_PDISP_HDMI_GCP_SUBPACK */ + uint nv_pdisp_hdmi_channel_status1; /* _NV_PDISP_HDMI_CHANNEL_STATUS1 */ + uint nv_pdisp_hdmi_channel_status2; /* _NV_PDISP_HDMI_CHANNEL_STATUS2 */ + uint nv_pdisp_hdmi_emu0; /* _NV_PDISP_HDMI_EMU0 */ + uint nv_pdisp_hdmi_emu1; /* _NV_PDISP_HDMI_EMU1 */ + uint nv_pdisp_hdmi_emu1_rdata; /* _NV_PDISP_HDMI_EMU1_RDATA */ + uint nv_pdisp_hdmi_spare; /* _NV_PDISP_HDMI_SPARE */ + uint nv_pdisp_hdmi_spdif_chn_status1; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS1 */ + uint nv_pdisp_hdmi_spdif_chn_status2; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS2 */ + + uint nv_pdisp_hdcprif_rom_ctrl; /* _NV_PDISP_HDCPRIF_ROM_CTRL */ + + uint unused; + + uint nv_pdisp_sor_cap; /* _NV_PDISP_SOR_CAP */ + uint nv_pdisp_sor_pwr; /* _NV_PDISP_SOR_PWR */ + uint nv_pdisp_sor_test; /* _NV_PDISP_SOR_TEST */ + uint nv_pdisp_sor_pll0; /* _NV_PDISP_SOR_PLL0 */ + uint nv_pdisp_sor_pll1; /* _NV_PDISP_SOR_PLL1 */ + uint nv_pdisp_sor_pll2; /* _NV_PDISP_SOR_PLL2 */ + uint nv_pdisp_sor_cstm; /* _NV_PDISP_SOR_CSTM */ + uint nv_pdisp_sor_lvds; /* _NV_PDISP_SOR_LVDS */ + uint nv_pdisp_sor_crca; /* _NV_PDISP_SOR_CRCA */ + uint nv_pdisp_sor_crcb; /* _NV_PDISP_SOR_CRCB */ + uint nv_pdisp_sor_blank; /* _NV_PDISP_SOR_BLANK */ + + uint nv_pdisp_sor_seq_ctl; /* _NV_PDISP_SOR_SEQ_CTL */ + uint nv_pdisp_sor_seq_inst0; /* _NV_PDISP_SOR_SEQ_INST0 */ + uint nv_pdisp_sor_seq_inst1; /* _NV_PDISP_SOR_SEQ_INST1 */ + uint nv_pdisp_sor_seq_inst2; /* _NV_PDISP_SOR_SEQ_INST2 */ + uint nv_pdisp_sor_seq_inst3; /* _NV_PDISP_SOR_SEQ_INST3 */ + uint nv_pdisp_sor_seq_inst4; /* _NV_PDISP_SOR_SEQ_INST4 */ + uint nv_pdisp_sor_seq_inst5; /* _NV_PDISP_SOR_SEQ_INST5 */ + uint nv_pdisp_sor_seq_inst6; /* _NV_PDISP_SOR_SEQ_INST6 */ + uint nv_pdisp_sor_seq_inst7; /* _NV_PDISP_SOR_SEQ_INST7 */ + uint nv_pdisp_sor_seq_inst8; /* _NV_PDISP_SOR_SEQ_INST8 */ + uint nv_pdisp_sor_seq_inst9; /* _NV_PDISP_SOR_SEQ_INST9 */ + uint nv_pdisp_sor_seq_insta; /* _NV_PDISP_SOR_SEQ_INSTA */ + uint nv_pdisp_sor_seq_instb; /* _NV_PDISP_SOR_SEQ_INSTB */ + uint nv_pdisp_sor_seq_instc; /* _NV_PDISP_SOR_SEQ_INSTC */ + uint nv_pdisp_sor_seq_instd; /* _NV_PDISP_SOR_SEQ_INSTD */ + uint nv_pdisp_sor_seq_inste; /* _NV_PDISP_SOR_SEQ_INSTE */ + uint nv_pdisp_sor_seq_instf; /* _NV_PDISP_SOR_SEQ_INSTF */ + + uint unused1[2]; + + uint nv_pdisp_sor_vcrca0; /* _NV_PDISP_SOR_VCRCA0 */ + uint nv_pdisp_sor_vcrca1; /* _NV_PDISP_SOR_VCRCA1 */ + uint nv_pdisp_sor_ccrca0; /* _NV_PDISP_SOR_CCRCA0 */ + uint nv_pdisp_sor_ccrca1; /* _NV_PDISP_SOR_CCRCA1 */ + + uint nv_pdisp_sor_edataa0; /* _NV_PDISP_SOR_EDATAA0 */ + uint nv_pdisp_sor_edataa1; /* _NV_PDISP_SOR_EDATAA1 */ + + uint nv_pdisp_sor_counta0; /* _NV_PDISP_SOR_COUNTA0 */ + uint nv_pdisp_sor_counta1; /* _NV_PDISP_SOR_COUNTA1 */ + + uint nv_pdisp_sor_debuga0; /* _NV_PDISP_SOR_DEBUGA0 */ + uint nv_pdisp_sor_debuga1; /* _NV_PDISP_SOR_DEBUGA1 */ + + uint nv_pdisp_sor_trig; /* _NV_PDISP_SOR_TRIG */ + uint nv_pdisp_sor_mscheck; /* _NV_PDISP_SOR_MSCHECK */ + uint nv_pdisp_sor_lane_drive_current; /* _NV_PDISP_SOR_LANE_DRIVE_CURRENT */ + + uint nv_pdisp_audio_debug0; /* _NV_PDISP_AUDIO_DEBUG0 0x7f */ + uint nv_pdisp_audio_debug1; /* _NV_PDISP_AUDIO_DEBUG1 0x80 */ + uint nv_pdisp_audio_debug2; /* _NV_PDISP_AUDIO_DEBUG2 0x81 */ + + uint nv_pdisp_audio_fs1; /* _NV_PDISP_AUDIO_FS1 0x82 */ + uint nv_pdisp_audio_fs2; /* _NV_PDISP_AUDIO_FS2 */ + uint nv_pdisp_audio_fs3; /* _NV_PDISP_AUDIO_FS3 */ + uint nv_pdisp_audio_fs4; /* _NV_PDISP_AUDIO_FS4 */ + uint nv_pdisp_audio_fs5; /* _NV_PDISP_AUDIO_FS5 */ + uint nv_pdisp_audio_fs6; /* _NV_PDISP_AUDIO_FS6 */ + uint nv_pdisp_audio_fs7; /* _NV_PDISP_AUDIO_FS7 0x88 */ + + uint nv_pdisp_audio_pulse_width; /* _NV_PDISP_AUDIO_PULSE_WIDTH */ + uint nv_pdisp_audio_threshold; /* _NV_PDISP_AUDIO_THRESHOLD */ + uint nv_pdisp_audio_cntrl0; /* _NV_PDISP_AUDIO_CNTRL0 */ + uint nv_pdisp_audio_n; /* _NV_PDISP_AUDIO_N */ + uint nv_pdisp_audio_nval[7]; /* _NV_PDISP_AUDIO_NVAL */ + + uint nv_pdisp_hdcprif_rom_timing; /* _NV_PDISP_HDCPRIF_ROM_TIMING */ + uint nv_pdisp_sor_refclk; /* _NV_PDISP_SOR_REFCLK */ + uint nv_pdisp_crc_control; /* _NV_PDISP_CRC_CONTROL */ + uint nv_pdisp_input_control; /* _NV_PDISP_INPUT_CONTROL */ + uint nv_pdisp_scratch; /* _NV_PDISP_SCRATCH */ + uint nv_pdisp_pe_current; /* _NV_PDISP_PE_CURRENT */ + + uint nv_pdisp_key_ctrl; /* _NV_PDISP_KEY_CTRL */ + uint nv_pdisp_key_debug0; /* _NV_PDISP_KEY_DEBUG0 */ + uint nv_pdisp_key_debug1; /* _NV_PDISP_KEY_DEBUG1 */ + uint nv_pdisp_key_debug2; /* _NV_PDISP_KEY_DEBUG2 */ + uint nv_pdisp_key_hdcp_key_0; /* _NV_PDISP_KEY_HDCP_KEY_0 */ + uint nv_pdisp_key_hdcp_key_1; /* _NV_PDISP_KEY_HDCP_KEY_1 */ + uint nv_pdisp_key_hdcp_key_2; /* _NV_PDISP_KEY_HDCP_KEY_2 */ + uint nv_pdisp_key_hdcp_key_3; /* _NV_PDISP_KEY_HDCP_KEY_3 */ + uint nv_pdisp_key_hdcp_key_trig; /* _NV_PDISP_KEY_HDCP_KEY_3 */ + uint nv_pdisp_key_skey_index; /* _NV_PDISP_KEY_HDCP_KEY_3 */ /* 0xa3 */ + + uint unused2[8]; + + uint nv_pdisp_sor_audio_cntrl0; /* _NV_PDISP_SOR_AUDIO_CNTRL0 */ /* 0xac */ + uint nv_pdisp_sor_audio_debug; /* _NV_PDISP_SOR_AUDIO_DEBUG */ + uint nv_pdisp_sor_audio_spare0; /* _NV_PDISP_SOR_AUDIO_SPARE0 */ + uint nv_pdisp_sor_audio_nval[7]; /* _NV_PDISP_SOR_AUDIO_NVAL 0xaf ~ 0xb5 */ + uint nv_pdisp_sor_audio_hda_scratch[4]; /* _NV_PDISP_SOR_AUDIO_HDA_SCRATCH 0xb6 ~ 0xb9 */ + uint nv_pdisp_sor_audio_hda_codec_scratch[2]; /* _NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH 0xba ~ 0xbb */ + + uint nv_pdisp_sor_audio_hda_eld_bufwr; /* _NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR */ + uint nv_pdisp_sor_audio_hda_presense; /* _NV_PDISP_SOR_AUDIO_HDA_PRESENSE */ + uint nv_pdisp_sor_audio_hda_cp; /* _NV_PDISP_SOR_AUDIO_HDA_CP */ + uint nv_pdisp_sor_audio_aval[8]; /* _NV_PDISP_SOR_AUDIO_AVAL */ + uint nv_pdisp_sor_audio_gen_ctrl; /* _NV_PDISP_SOR_AUDIO_GEN_CTRL */ + + uint unused3[4]; + + uint nv_pdisp_int_status; /* _NV_PDISP_INT_STATUS */ + uint nv_pdisp_int_mask; /* _NV_PDISP_INT_MASK */ + uint nv_pdisp_int_enable; /* _NV_PDISP_INT_ENABLE */ + + uint unused4[2]; + + uint nv_pdisp_sor_io_peak_current; /* _NV_PDISP_SOR_IO_PEAK_CURRENT */ + uint nv_pdisp_sor_pad_ctls0; /* _NV_PDISP_SOR_PAD_CTLS0 */ +}; + +/* HDMI_NV_PDISP_SOR_STATE0 0x01 */ +#define SOR_STATE_UPDATE BIT(0) + +/* HDMI_NV_PDISP_SOR_STATE1 0x02 */ +#define SOR_STATE_ASY_HEAD_OPMODE_AWAKE BIT(1) +#define SOR_STATE_ASY_ORMODE_NORMAL BIT(2) +#define SOR_STATE_ATTACHED BIT(3) + +/* HDMI_NV_PDISP_SOR_STATE2 0x03 */ +#define SOR_STATE_ASY_OWNER_NONE (0 << 0) +#define SOR_STATE_ASY_OWNER_HEAD0 (1 << 0) +#define SOR_STATE_ASY_SUBOWNER_NONE (0 << 4) +#define SOR_STATE_ASY_SUBOWNER_SUBHEAD0 (1 << 4) +#define SOR_STATE_ASY_SUBOWNER_SUBHEAD1 (2 << 4) +#define SOR_STATE_ASY_SUBOWNER_BOTH (3 << 4) +#define SOR_STATE_ASY_CRCMODE_ACTIVE (0 << 6) +#define SOR_STATE_ASY_CRCMODE_COMPLETE (1 << 6) +#define SOR_STATE_ASY_CRCMODE_NON_ACTIVE (2 << 6) +#define SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A (1 << 8) +#define SOR_STATE_ASY_PROTOCOL_CUSTOM (15 << 8) +#define SOR_STATE_ASY_HSYNCPOL_POS (0 << 12) +#define SOR_STATE_ASY_HSYNCPOL_NEG (1 << 12) +#define SOR_STATE_ASY_VSYNCPOL_POS (0 << 13) +#define SOR_STATE_ASY_VSYNCPOL_NEG (1 << 13) +#define SOR_STATE_ASY_DEPOL_POS (0 << 14) +#define SOR_STATE_ASY_DEPOL_NEG (1 << 14) + +#define INFOFRAME_CTRL_ENABLE BIT(0) +#define INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0) +#define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8) +#define INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16) + +/* HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x2a */ +#define GENERIC_CTRL_ENABLE BIT(0) +#define GENERIC_CTRL_OTHER BIT(4) +#define GENERIC_CTRL_SINGLE BIT(8) +#define GENERIC_CTRL_HBLANK BIT(12) +#define GENERIC_CTRL_AUDIO BIT(16) + +/* HDMI_NV_PDISP_HDMI_ACR_* */ +#define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8) +#define ACR_SUBPACK_N(x) (((x) & 0xffffff) << 0) +#define ACR_ENABLE BIT(31) + +/* HDMI_NV_PDISP_HDMI_CTRL 0x44 */ +#define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0) +#define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16) +#define HDMI_CTRL_ENABLE BIT(30) + +/* HDMI_NV_PDISP_HDMI_VSYNC_* */ +#define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0) +#define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16) +#define VSYNC_WINDOW_ENABLE BIT(31) + +/* HDMI_NV_PDISP_HDMI_SPARE 0x4f */ +#define SPARE_HW_CTS BIT(0) +#define SPARE_FORCE_SW_CTS BIT(1) +#define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16) + +/* HDMI_NV_PDISP_SOR_PWR 0x55 */ +#define SOR_PWR_NORMAL_STATE_PD (0 << 0) +#define SOR_PWR_NORMAL_STATE_PU (1 << 0) +#define SOR_PWR_NORMAL_START_NORMAL (0 << 1) +#define SOR_PWR_NORMAL_START_ALT (1 << 1) +#define SOR_PWR_SAFE_STATE_PD (0 << 16) +#define SOR_PWR_SAFE_STATE_PU (1 << 16) +#define SOR_PWR_SETTING_NEW_DONE (0 << 31) +#define SOR_PWR_SETTING_NEW_PENDING (1 << 31) +#define SOR_PWR_SETTING_NEW_TRIGGER (1 << 31) + +/* HDMI_NV_PDISP_SOR_PLL0 0x57 */ +#define SOR_PLL_PWR BIT(0) +#define SOR_PLL_PDBG BIT(1) +#define SOR_PLL_VCAPD BIT(2) +#define SOR_PLL_PDPORT BIT(3) +#define SOR_PLL_RESISTORSEL BIT(4) +#define SOR_PLL_PULLDOWN BIT(5) +#define SOR_PLL_VCOCAP(x) (((x) & 0xf) << 8) +#define SOR_PLL_BG_V17_S(x) (((x) & 0xf) << 12) +#define SOR_PLL_FILTER(x) (((x) & 0xf) << 16) +#define SOR_PLL_ICHPMP(x) (((x) & 0xf) << 24) +#define SOR_PLL_TX_REG_LOAD(x) (((x) & 0xf) << 28) + +/* HDMI_NV_PDISP_SOR_PLL1 0x58 */ +#define SOR_PLL_TMDS_TERM_ENABLE BIT(8) +#define SOR_PLL_TMDS_TERMADJ(x) (((x) & 0xf) << 9) +#define SOR_PLL_LOADADJ(x) (((x) & 0xf) << 20) +#define SOR_PLL_PE_EN BIT(28) +#define SOR_PLL_HALF_FULL_PE BIT(29) +#define SOR_PLL_S_D_PIN_PE BIT(30) + +/* HDMI_NV_PDISP_SOR_CSTM 0x5a */ +#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24) +#define SOR_CSTM_PLLDIV BIT(21) +#define SOR_CSTM_LVDS_ENABLE BIT(16) +#define SOR_CSTM_MODE_LVDS (0 << 12) +#define SOR_CSTM_MODE_TMDS (1 << 12) +#define SOR_CSTM_MODE_MASK (3 << 12) + +/* HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f */ +#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0) +#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4) +#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8) +#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12) +#define SOR_SEQ_PC(x) (((x) & 0xf) << 16) +#define SOR_SEQ_STATUS BIT(28) +#define SOR_SEQ_SWITCH BIT(30) + +/* HDMI_NV_PDISP_SOR_SEQ_INST(x) (0x60 + (x)) */ +#define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0) +#define SOR_SEQ_INST_WAIT_UNITS_VSYNC (2 << 12) +#define SOR_SEQ_INST_HALT (1 << 15) +#define SOR_SEQ_INST_PIN_A_LOW (0 << 21) +#define SOR_SEQ_INST_PIN_A_HIGH (1 << 21) +#define SOR_SEQ_INST_PIN_B_LOW (0 << 22) +#define SOR_SEQ_INST_PIN_B_HIGH (1 << 22) +#define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23) + +/* HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x7e */ +#define DRIVE_CURRENT_LANE0(x) (((x) & 0x3f) << 0) +#define DRIVE_CURRENT_LANE1(x) (((x) & 0x3f) << 8) +#define DRIVE_CURRENT_LANE2(x) (((x) & 0x3f) << 16) +#define DRIVE_CURRENT_LANE3(x) (((x) & 0x3f) << 24) +#define DRIVE_CURRENT_LANE0_T114(x) (((x) & 0x7f) << 0) +#define DRIVE_CURRENT_LANE1_T114(x) (((x) & 0x7f) << 8) +#define DRIVE_CURRENT_LANE2_T114(x) (((x) & 0x7f) << 16) +#define DRIVE_CURRENT_LANE3_T114(x) (((x) & 0x7f) << 24) + +/* Drive current list */ +enum { + DRIVE_CURRENT_1_500_mA, + DRIVE_CURRENT_1_875_mA, + DRIVE_CURRENT_2_250_mA, + DRIVE_CURRENT_2_625_mA, + DRIVE_CURRENT_3_000_mA, + DRIVE_CURRENT_3_375_mA, + DRIVE_CURRENT_3_750_mA, + DRIVE_CURRENT_4_125_mA, + DRIVE_CURRENT_4_500_mA, + DRIVE_CURRENT_4_875_mA, + DRIVE_CURRENT_5_250_mA, + DRIVE_CURRENT_5_625_mA, + DRIVE_CURRENT_6_000_mA, + DRIVE_CURRENT_6_375_mA, + DRIVE_CURRENT_6_750_mA, + DRIVE_CURRENT_7_125_mA, + DRIVE_CURRENT_7_500_mA, + DRIVE_CURRENT_7_875_mA, + DRIVE_CURRENT_8_250_mA, + DRIVE_CURRENT_8_625_mA, + DRIVE_CURRENT_9_000_mA, + DRIVE_CURRENT_9_375_mA, + DRIVE_CURRENT_9_750_mA, + DRIVE_CURRENT_10_125_mA, + DRIVE_CURRENT_10_500_mA, + DRIVE_CURRENT_10_875_mA, + DRIVE_CURRENT_11_250_mA, + DRIVE_CURRENT_11_625_mA, + DRIVE_CURRENT_12_000_mA, + DRIVE_CURRENT_12_375_mA, + DRIVE_CURRENT_12_750_mA, + DRIVE_CURRENT_13_125_mA, + DRIVE_CURRENT_13_500_mA, + DRIVE_CURRENT_13_875_mA, + DRIVE_CURRENT_14_250_mA, + DRIVE_CURRENT_14_625_mA, + DRIVE_CURRENT_15_000_mA, + DRIVE_CURRENT_15_375_mA, + DRIVE_CURRENT_15_750_mA, + DRIVE_CURRENT_16_125_mA, + DRIVE_CURRENT_16_500_mA, + DRIVE_CURRENT_16_875_mA, + DRIVE_CURRENT_17_250_mA, + DRIVE_CURRENT_17_625_mA, + DRIVE_CURRENT_18_000_mA, + DRIVE_CURRENT_18_375_mA, + DRIVE_CURRENT_18_750_mA, + DRIVE_CURRENT_19_125_mA, + DRIVE_CURRENT_19_500_mA, + DRIVE_CURRENT_19_875_mA, + DRIVE_CURRENT_20_250_mA, + DRIVE_CURRENT_20_625_mA, + DRIVE_CURRENT_21_000_mA, + DRIVE_CURRENT_21_375_mA, + DRIVE_CURRENT_21_750_mA, + DRIVE_CURRENT_22_125_mA, + DRIVE_CURRENT_22_500_mA, + DRIVE_CURRENT_22_875_mA, + DRIVE_CURRENT_23_250_mA, + DRIVE_CURRENT_23_625_mA, + DRIVE_CURRENT_24_000_mA, + DRIVE_CURRENT_24_375_mA, + DRIVE_CURRENT_24_750_mA, +}; + +/* Drive current list for T114 */ +enum { + DRIVE_CURRENT_0_000_mA_T114, + DRIVE_CURRENT_0_400_mA_T114, + DRIVE_CURRENT_0_800_mA_T114, + DRIVE_CURRENT_1_200_mA_T114, + DRIVE_CURRENT_1_600_mA_T114, + DRIVE_CURRENT_2_000_mA_T114, + DRIVE_CURRENT_2_400_mA_T114, + DRIVE_CURRENT_2_800_mA_T114, + DRIVE_CURRENT_3_200_mA_T114, + DRIVE_CURRENT_3_600_mA_T114, + DRIVE_CURRENT_4_000_mA_T114, + DRIVE_CURRENT_4_400_mA_T114, + DRIVE_CURRENT_4_800_mA_T114, + DRIVE_CURRENT_5_200_mA_T114, + DRIVE_CURRENT_5_600_mA_T114, + DRIVE_CURRENT_6_000_mA_T114, + DRIVE_CURRENT_6_400_mA_T114, + DRIVE_CURRENT_6_800_mA_T114, + DRIVE_CURRENT_7_200_mA_T114, + DRIVE_CURRENT_7_600_mA_T114, + DRIVE_CURRENT_8_000_mA_T114, + DRIVE_CURRENT_8_400_mA_T114, + DRIVE_CURRENT_8_800_mA_T114, + DRIVE_CURRENT_9_200_mA_T114, + DRIVE_CURRENT_9_600_mA_T114, + DRIVE_CURRENT_10_000_mA_T114, + DRIVE_CURRENT_10_400_mA_T114, + DRIVE_CURRENT_10_800_mA_T114, + DRIVE_CURRENT_11_200_mA_T114, + DRIVE_CURRENT_11_600_mA_T114, + DRIVE_CURRENT_12_000_mA_T114, + DRIVE_CURRENT_12_400_mA_T114, + DRIVE_CURRENT_12_800_mA_T114, + DRIVE_CURRENT_13_200_mA_T114, + DRIVE_CURRENT_13_600_mA_T114, + DRIVE_CURRENT_14_000_mA_T114, + DRIVE_CURRENT_14_400_mA_T114, + DRIVE_CURRENT_14_800_mA_T114, + DRIVE_CURRENT_15_200_mA_T114, + DRIVE_CURRENT_15_600_mA_T114, + DRIVE_CURRENT_16_000_mA_T114, + DRIVE_CURRENT_16_400_mA_T114, + DRIVE_CURRENT_16_800_mA_T114, + DRIVE_CURRENT_17_200_mA_T114, + DRIVE_CURRENT_17_600_mA_T114, + DRIVE_CURRENT_18_000_mA_T114, + DRIVE_CURRENT_18_400_mA_T114, + DRIVE_CURRENT_18_800_mA_T114, + DRIVE_CURRENT_19_200_mA_T114, + DRIVE_CURRENT_19_600_mA_T114, + DRIVE_CURRENT_20_000_mA_T114, + DRIVE_CURRENT_20_400_mA_T114, + DRIVE_CURRENT_20_800_mA_T114, + DRIVE_CURRENT_21_200_mA_T114, + DRIVE_CURRENT_21_600_mA_T114, + DRIVE_CURRENT_22_000_mA_T114, + DRIVE_CURRENT_22_400_mA_T114, + DRIVE_CURRENT_22_800_mA_T114, + DRIVE_CURRENT_23_200_mA_T114, + DRIVE_CURRENT_23_600_mA_T114, + DRIVE_CURRENT_24_000_mA_T114, + DRIVE_CURRENT_24_400_mA_T114, + DRIVE_CURRENT_24_800_mA_T114, + DRIVE_CURRENT_25_200_mA_T114, + DRIVE_CURRENT_25_400_mA_T114, + DRIVE_CURRENT_25_800_mA_T114, + DRIVE_CURRENT_26_200_mA_T114, + DRIVE_CURRENT_26_600_mA_T114, + DRIVE_CURRENT_27_000_mA_T114, + DRIVE_CURRENT_27_400_mA_T114, + DRIVE_CURRENT_27_800_mA_T114, + DRIVE_CURRENT_28_200_mA_T114, +}; + +/* HDMI_NV_PDISP_AUDIO_FS */ +#define AUDIO_FS_LOW(x) (((x) & 0xfff) << 0) +#define AUDIO_FS_HIGH(x) (((x) & 0xfff) << 16) + +/* HDMI_NV_PDISP_AUDIO_CNTRL0 0x8b */ +#define AUDIO_CNTRL0_ERROR_TOLERANCE(x) (((x) & 0xff) << 0) +#define AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20) +#define AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20) +#define AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20) +#define AUDIO_CNTRL0_FRAMES_PER_BLOCK(x) (((x) & 0xff) << 24) + +/* HDMI_NV_PDISP_AUDIO_N 0x8c */ +#define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0) +#define AUDIO_N_RESETF (1 << 20) +#define AUDIO_N_GENERATE_NORMAL (0 << 24) +#define AUDIO_N_GENERATE_ALTERNATE (1 << 24) + +/* HDMI_NV_PDISP_SOR_REFCLK 0x95 */ +#define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8) +#define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x03) << 6) + +/* HDMI_NV_PDISP_INPUT_CONTROL 0x97 */ +#define HDMI_SRC_DISPLAYA (0 << 0) +#define HDMI_SRC_DISPLAYB (1 << 0) +#define ARM_VIDEO_RANGE_FULL (0 << 1) +#define ARM_VIDEO_RANGE_LIMITED (1 << 1) + +/* HDMI_NV_PDISP_PE_CURRENT 0x99 */ +#define PE_CURRENT0(x) (((x) & 0xf) << 0) +#define PE_CURRENT1(x) (((x) & 0xf) << 8) +#define PE_CURRENT2(x) (((x) & 0xf) << 16) +#define PE_CURRENT3(x) (((x) & 0xf) << 24) + +enum { + PE_CURRENT_0_0_mA, + PE_CURRENT_0_5_mA, + PE_CURRENT_1_0_mA, + PE_CURRENT_1_5_mA, + PE_CURRENT_2_0_mA, + PE_CURRENT_2_5_mA, + PE_CURRENT_3_0_mA, + PE_CURRENT_3_5_mA, + PE_CURRENT_4_0_mA, + PE_CURRENT_4_5_mA, + PE_CURRENT_5_0_mA, + PE_CURRENT_5_5_mA, + PE_CURRENT_6_0_mA, + PE_CURRENT_6_5_mA, + PE_CURRENT_7_0_mA, + PE_CURRENT_7_5_mA, +}; + +enum { + PE_CURRENT_0_mA_T114, + PE_CURRENT_1_mA_T114, + PE_CURRENT_2_mA_T114, + PE_CURRENT_3_mA_T114, + PE_CURRENT_4_mA_T114, + PE_CURRENT_5_mA_T114, + PE_CURRENT_6_mA_T114, + PE_CURRENT_7_mA_T114, + PE_CURRENT_8_mA_T114, + PE_CURRENT_9_mA_T114, + PE_CURRENT_10_mA_T114, + PE_CURRENT_11_mA_T114, + PE_CURRENT_12_mA_T114, + PE_CURRENT_13_mA_T114, + PE_CURRENT_14_mA_T114, + PE_CURRENT_15_mA_T114, +}; + +/* HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0xac */ +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20) +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20) +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20) +#define SOR_AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29) + +/* HDMI_NV_PDISP_SOR_AUDIO_SPARE0 0xae */ +#define SOR_AUDIO_SPARE0_HBR_ENABLE BIT(27) + +/* HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0xba */ +#define SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID BIT(30) +#define SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK 0xffff + +/* HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0xbd */ +#define SOR_AUDIO_HDA_PRESENSE_VALID BIT(1) +#define SOR_AUDIO_HDA_PRESENSE_PRESENT BIT(0) + +/* HDMI_NV_PDISP_INT_STATUS 0xcc */ +#define INT_SCRATCH BIT(3) +#define INT_CP_REQUEST BIT(2) +#define INT_CODEC_SCRATCH1 BIT(1) +#define INT_CODEC_SCRATCH0 BIT(0) + +/* HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0xd1 */ +#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) << 0) +#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) << 8) +#define PEAK_CURRENT_LANE2(x) (((x) & 0x7f) << 16) +#define PEAK_CURRENT_LANE3(x) (((x) & 0x7f) << 24) + +enum { + PEAK_CURRENT_0_000_mA, + PEAK_CURRENT_0_200_mA, + PEAK_CURRENT_0_400_mA, + PEAK_CURRENT_0_600_mA, + PEAK_CURRENT_0_800_mA, + PEAK_CURRENT_1_000_mA, + PEAK_CURRENT_1_200_mA, + PEAK_CURRENT_1_400_mA, + PEAK_CURRENT_1_600_mA, + PEAK_CURRENT_1_800_mA, + PEAK_CURRENT_2_000_mA, + PEAK_CURRENT_2_200_mA, + PEAK_CURRENT_2_400_mA, + PEAK_CURRENT_2_600_mA, + PEAK_CURRENT_2_800_mA, + PEAK_CURRENT_3_000_mA, + PEAK_CURRENT_3_200_mA, + PEAK_CURRENT_3_400_mA, + PEAK_CURRENT_3_600_mA, + PEAK_CURRENT_3_800_mA, + PEAK_CURRENT_4_000_mA, + PEAK_CURRENT_4_200_mA, + PEAK_CURRENT_4_400_mA, + PEAK_CURRENT_4_600_mA, + PEAK_CURRENT_4_800_mA, + PEAK_CURRENT_5_000_mA, + PEAK_CURRENT_5_200_mA, + PEAK_CURRENT_5_400_mA, + PEAK_CURRENT_5_600_mA, + PEAK_CURRENT_5_800_mA, + PEAK_CURRENT_6_000_mA, + PEAK_CURRENT_6_200_mA, + PEAK_CURRENT_6_400_mA, + PEAK_CURRENT_6_600_mA, + PEAK_CURRENT_6_800_mA, + PEAK_CURRENT_7_000_mA, + PEAK_CURRENT_7_200_mA, + PEAK_CURRENT_7_400_mA, + PEAK_CURRENT_7_600_mA, + PEAK_CURRENT_7_800_mA, + PEAK_CURRENT_8_000_mA, + PEAK_CURRENT_8_200_mA, + PEAK_CURRENT_8_400_mA, + PEAK_CURRENT_8_600_mA, + PEAK_CURRENT_8_800_mA, + PEAK_CURRENT_9_000_mA, + PEAK_CURRENT_9_200_mA, + PEAK_CURRENT_9_400_mA, +}; + +#endif /* _TEGRA_HDMI_H */ diff --git a/drivers/video/tegra20/tegra-host1x.c b/drivers/video/tegra20/tegra-host1x.c new file mode 100644 index 00000000000..58ab871a3b4 --- /dev/null +++ b/drivers/video/tegra20/tegra-host1x.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <clk.h> +#include <log.h> +#include <reset.h> +#include <linux/delay.h> + +#include <asm/arch/clock.h> +#include <asm/arch-tegra/clk_rst.h> + +struct tegra_host1x_info { + u32 clk_parent; + u32 rate; +}; + +static int tegra_host1x_probe(struct udevice *dev) +{ + struct clk *clk; + struct reset_ctl reset_ctl; + const struct tegra_host1x_info *info; + int ret; + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + log_debug("%s: cannot get HOST1X clock: %ld\n", + __func__, PTR_ERR(clk)); + return PTR_ERR(clk); + } + + ret = reset_get_by_name(dev, "host1x", &reset_ctl); + if (ret) { + log_debug("%s: cannot get HOST1X reset: %d\n", + __func__, ret); + return ret; + } + + info = (struct tegra_host1x_info *)dev_get_driver_data(dev); + + reset_assert(&reset_ctl); + clock_start_periph_pll(clk->id, info->clk_parent, info->rate); + + mdelay(2); + reset_deassert(&reset_ctl); + + return 0; +} + +static const struct tegra_host1x_info tegra20_host1x_info = { + .clk_parent = CLOCK_ID_CGENERAL, + .rate = 150000000, /* 150 MHz */ +}; + +static const struct tegra_host1x_info tegra114_host1x_info = { + .clk_parent = CLOCK_ID_PERIPH, + .rate = 136000000, /* 136 MHz */ +}; + +static const struct udevice_id tegra_host1x_ids[] = { + { + .compatible = "nvidia,tegra20-host1x", + .data = (ulong)&tegra20_host1x_info + }, { + .compatible = "nvidia,tegra30-host1x", + .data = (ulong)&tegra20_host1x_info + }, { + .compatible = "nvidia,tegra114-host1x", + .data = (ulong)&tegra114_host1x_info + }, { + .compatible = "nvidia,tegra124-host1x", + .data = (ulong)&tegra114_host1x_info + }, { + /* sentinel */ + } +}; + +U_BOOT_DRIVER(tegra_host1x) = { + .name = "tegra_host1x", + .id = UCLASS_SIMPLE_BUS, + .of_match = tegra_host1x_ids, + .probe = tegra_host1x_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra20/tegra-pwm-backlight.c index 79d8a021a3a..998f0df1991 100644 --- a/drivers/video/tegra20/tegra-pwm-backlight.c +++ b/drivers/video/tegra20/tegra-pwm-backlight.c @@ -17,9 +17,6 @@ #include "tegra-dc.h" -#define TEGRA_DISPLAY_A_BASE 0x54200000 -#define TEGRA_DISPLAY_B_BASE 0x54240000 - #define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10 #define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF @@ -106,14 +103,11 @@ static int tegra_pwm_backlight_enable(struct udevice *dev) static int tegra_pwm_backlight_probe(struct udevice *dev) { struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + ofnode dc = ofnode_get_parent(dev_ofnode(dev)); - if (dev_read_bool(dev, "nvidia,display-b-base")) - priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_B_BASE; - else - priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_A_BASE; - + priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc); if (!priv->dc) { - log_err("no display controller address\n"); + log_err("%s: failed to get DC controller\n", __func__); return -EINVAL; } |