From 901f249fdb6b0ab287ac23dbdcada44c4c697dca Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sat, 29 Mar 2025 16:49:53 +0200 Subject: video: rename tegra20 to tegra Since this set of drivers suports four Tegra SoC generations, lets name it just 'tegra'. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Peter Robinson --- drivers/video/Kconfig | 2 +- drivers/video/Makefile | 2 +- drivers/video/tegra/Kconfig | 38 + drivers/video/tegra/Makefile | 7 + drivers/video/tegra/mipi-phy.c | 133 ++++ drivers/video/tegra/mipi-phy.h | 48 ++ drivers/video/tegra/tegra-dc.c | 682 ++++++++++++++++ drivers/video/tegra/tegra-dc.h | 40 + drivers/video/tegra/tegra-dsi.c | 1148 +++++++++++++++++++++++++++ drivers/video/tegra/tegra-dsi.h | 246 ++++++ drivers/video/tegra/tegra-hdmi.c | 623 +++++++++++++++ drivers/video/tegra/tegra-hdmi.h | 648 +++++++++++++++ drivers/video/tegra/tegra-host1x.c | 86 ++ drivers/video/tegra/tegra-mipi.c | 294 +++++++ drivers/video/tegra/tegra-pwm-backlight.c | 150 ++++ drivers/video/tegra20/Kconfig | 38 - drivers/video/tegra20/Makefile | 7 - drivers/video/tegra20/mipi-phy.c | 133 ---- drivers/video/tegra20/mipi-phy.h | 48 -- drivers/video/tegra20/tegra-dc.c | 682 ---------------- drivers/video/tegra20/tegra-dc.h | 40 - drivers/video/tegra20/tegra-dsi.c | 1148 --------------------------- drivers/video/tegra20/tegra-dsi.h | 246 ------ drivers/video/tegra20/tegra-hdmi.c | 623 --------------- drivers/video/tegra20/tegra-hdmi.h | 648 --------------- drivers/video/tegra20/tegra-host1x.c | 86 -- drivers/video/tegra20/tegra-mipi.c | 294 ------- drivers/video/tegra20/tegra-pwm-backlight.c | 150 ---- 28 files changed, 4145 insertions(+), 4145 deletions(-) create mode 100644 drivers/video/tegra/Kconfig create mode 100644 drivers/video/tegra/Makefile create mode 100644 drivers/video/tegra/mipi-phy.c create mode 100644 drivers/video/tegra/mipi-phy.h create mode 100644 drivers/video/tegra/tegra-dc.c create mode 100644 drivers/video/tegra/tegra-dc.h create mode 100644 drivers/video/tegra/tegra-dsi.c create mode 100644 drivers/video/tegra/tegra-dsi.h create mode 100644 drivers/video/tegra/tegra-hdmi.c create mode 100644 drivers/video/tegra/tegra-hdmi.h create mode 100644 drivers/video/tegra/tegra-host1x.c create mode 100644 drivers/video/tegra/tegra-mipi.c create mode 100644 drivers/video/tegra/tegra-pwm-backlight.c delete mode 100644 drivers/video/tegra20/Kconfig delete mode 100644 drivers/video/tegra20/Makefile delete mode 100644 drivers/video/tegra20/mipi-phy.c delete mode 100644 drivers/video/tegra20/mipi-phy.h delete mode 100644 drivers/video/tegra20/tegra-dc.c delete mode 100644 drivers/video/tegra20/tegra-dc.h delete mode 100644 drivers/video/tegra20/tegra-dsi.c delete mode 100644 drivers/video/tegra20/tegra-dsi.h delete mode 100644 drivers/video/tegra20/tegra-hdmi.c delete mode 100644 drivers/video/tegra20/tegra-hdmi.h delete mode 100644 drivers/video/tegra20/tegra-host1x.c delete mode 100644 drivers/video/tegra20/tegra-mipi.c delete mode 100644 drivers/video/tegra20/tegra-pwm-backlight.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index df607303616..afcdb16c599 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -819,7 +819,7 @@ config VIDEO_TEGRA124 source "drivers/video/bridge/Kconfig" -source "drivers/video/tegra20/Kconfig" +source "drivers/video/tegra/Kconfig" source "drivers/video/imx/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index bbd5db46553..a4896c0dfce 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -85,4 +85,4 @@ obj-$(CONFIG_VIDEO_ZYNQMP_DPSUB) += zynqmp/ obj-y += bridge/ obj-y += sunxi/ -obj-y += tegra20/ +obj-y += tegra/ diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig new file mode 100644 index 00000000000..598f9ea1f21 --- /dev/null +++ b/drivers/video/tegra/Kconfig @@ -0,0 +1,38 @@ +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. + This option enables this support which can be used on devices which + have an LCD display connected. + +config VIDEO_DSI_TEGRA30 + bool "Enable Tegra 30 DSI support" + 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 + select VIDEO_TEGRA20 + help + Tegra DC dependent backlight. diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile new file mode 100644 index 00000000000..78521405749 --- /dev/null +++ b/drivers/video/tegra/Makefile @@ -0,0 +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/tegra/mipi-phy.c b/drivers/video/tegra/mipi-phy.c new file mode 100644 index 00000000000..576262e405d --- /dev/null +++ b/drivers/video/tegra/mipi-phy.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 NVIDIA Corporation + */ + +#include + +#include "mipi-phy.h" + +/* + * Default D-PHY timings based on MIPI D-PHY specification. Derived from the + * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY + * specification (v1.2) with minor adjustments. + */ +int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, + unsigned long period) +{ + timing->clkmiss = 0; + timing->clkpost = 70 + 52 * period; + timing->clkpre = 8; + timing->clkprepare = 65; + timing->clksettle = 95; + timing->clktermen = 0; + timing->clktrail = 80; + timing->clkzero = 260; + timing->dtermen = 0; + timing->eot = 0; + timing->hsexit = 120; + timing->hsprepare = 65 + 5 * period; + timing->hszero = 145 + 5 * period; + timing->hssettle = 85 + 6 * period; + timing->hsskip = 40; + + /* + * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40) + * contains this formula as: + * + * T_HS-TRAIL = max(n * 8 * period, 60 + n * 4 * period) + * + * where n = 1 for forward-direction HS mode and n = 4 for reverse- + * direction HS mode. There's only one setting and this function does + * not parameterize on anything other that period, so this code will + * assumes that reverse-direction HS mode is supported and uses n = 4. + */ + timing->hstrail = max(4 * 8 * period, 60 + 4 * 4 * period); + + timing->init = 100000; + timing->lpx = 60; + timing->taget = 5 * timing->lpx; + timing->tago = 4 * timing->lpx; + timing->tasure = 2 * timing->lpx; + timing->wakeup = 1000000; + + return 0; +} + +/* + * Validate D-PHY timing according to MIPI D-PHY specification + * (v1.2, Section 6.9 "Global Operation Timing Parameters"). + */ +int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, + unsigned long period) +{ + if (timing->clkmiss > 60) + return -EINVAL; + + if (timing->clkpost < (60 + 52 * period)) + return -EINVAL; + + if (timing->clkpre < 8) + return -EINVAL; + + if (timing->clkprepare < 38 || timing->clkprepare > 95) + return -EINVAL; + + if (timing->clksettle < 95 || timing->clksettle > 300) + return -EINVAL; + + if (timing->clktermen > 38) + return -EINVAL; + + if (timing->clktrail < 60) + return -EINVAL; + + if (timing->clkprepare + timing->clkzero < 300) + return -EINVAL; + + if (timing->dtermen > 35 + 4 * period) + return -EINVAL; + + if (timing->eot > 105 + 12 * period) + return -EINVAL; + + if (timing->hsexit < 100) + return -EINVAL; + + if (timing->hsprepare < 40 + 4 * period || + timing->hsprepare > 85 + 6 * period) + return -EINVAL; + + if (timing->hsprepare + timing->hszero < 145 + 10 * period) + return -EINVAL; + + if ((timing->hssettle < 85 + 6 * period) || + (timing->hssettle > 145 + 10 * period)) + return -EINVAL; + + if (timing->hsskip < 40 || timing->hsskip > 55 + 4 * period) + return -EINVAL; + + if (timing->hstrail < max(8 * period, 60 + 4 * period)) + return -EINVAL; + + if (timing->init < 100000) + return -EINVAL; + + if (timing->lpx < 50) + return -EINVAL; + + if (timing->taget != 5 * timing->lpx) + return -EINVAL; + + if (timing->tago != 4 * timing->lpx) + return -EINVAL; + + if (timing->tasure < timing->lpx || timing->tasure > 2 * timing->lpx) + return -EINVAL; + + if (timing->wakeup < 1000000) + return -EINVAL; + + return 0; +} diff --git a/drivers/video/tegra/mipi-phy.h b/drivers/video/tegra/mipi-phy.h new file mode 100644 index 00000000000..41889a75035 --- /dev/null +++ b/drivers/video/tegra/mipi-phy.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 NVIDIA Corporation + */ + +#ifndef DRM_TEGRA_MIPI_PHY_H +#define DRM_TEGRA_MIPI_PHY_H + +/* + * D-PHY timing parameters + * + * A detailed description of these parameters can be found in the MIPI + * Alliance Specification for D-PHY, Section 5.9 "Global Operation Timing + * Parameters". + * + * All parameters are specified in nanoseconds. + */ +struct mipi_dphy_timing { + unsigned int clkmiss; + unsigned int clkpost; + unsigned int clkpre; + unsigned int clkprepare; + unsigned int clksettle; + unsigned int clktermen; + unsigned int clktrail; + unsigned int clkzero; + unsigned int dtermen; + unsigned int eot; + unsigned int hsexit; + unsigned int hsprepare; + unsigned int hszero; + unsigned int hssettle; + unsigned int hsskip; + unsigned int hstrail; + unsigned int init; + unsigned int lpx; + unsigned int taget; + unsigned int tago; + unsigned int tasure; + unsigned int wakeup; +}; + +int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, + unsigned long period); +int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, + unsigned long period); + +#endif diff --git a/drivers/video/tegra/tegra-dc.c b/drivers/video/tegra/tegra-dc.c new file mode 100644 index 00000000000..1f43153ff27 --- /dev/null +++ b/drivers/video/tegra/tegra-dc.c @@ -0,0 +1,682 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (c) 2024 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tegra-dc.h" + +/* Holder of Tegra per-SOC DC differences */ +struct tegra_dc_soc_info { + bool has_timer; + bool has_rgb; + bool has_pgate; +}; + +/* Information about the display controller */ +struct tegra_lcd_priv { + int width; /* width in pixels */ + int height; /* height in pixels */ + enum video_log2_bpp log2_bpp; /* colour depth */ + struct display_timing timing; + 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 */ + unsigned pixel_clock; /* Pixel clock in Hz */ + struct clk *clk; + struct clk *clk_parent; + ulong scdiv; /* Clock divider used by disp_clk_ctrl */ + bool rotation; /* 180 degree panel turn */ + int pipe; /* DC controller: 0 for A, 1 for B */ +}; + +enum { + /* Maximum LCD size we support */ + LCD_MAX_WIDTH = 2560, + LCD_MAX_HEIGHT = 1600, + LCD_MAX_LOG2_BPP = VIDEO_BPP16, +}; + +static void update_window(struct tegra_lcd_priv *priv, + struct disp_ctl_win *win) +{ + struct dc_ctlr *dc = priv->dc; + unsigned h_dda, v_dda; + unsigned long val; + + val = readl(&dc->cmd.disp_win_header); + val |= WINDOW_A_SELECT; + writel(val, &dc->cmd.disp_win_header); + + writel(win->fmt, &dc->win.color_depth); + + clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, + BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); + + val = win->out_x << H_POSITION_SHIFT; + val |= win->out_y << V_POSITION_SHIFT; + writel(val, &dc->win.pos); + + val = win->out_w << H_SIZE_SHIFT; + val |= win->out_h << V_SIZE_SHIFT; + writel(val, &dc->win.size); + + val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; + val |= win->h << V_PRESCALED_SIZE_SHIFT; + writel(val, &dc->win.prescaled_size); + + writel(0, &dc->win.h_initial_dda); + writel(0, &dc->win.v_initial_dda); + + h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); + v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); + + val = h_dda << H_DDA_INC_SHIFT; + val |= v_dda << V_DDA_INC_SHIFT; + writel(val, &dc->win.dda_increment); + + writel(win->stride, &dc->win.line_stride); + writel(0, &dc->win.buf_stride); + + val = WIN_ENABLE; + if (win->bpp < 24) + val |= COLOR_EXPAND; + + if (priv->rotation) + val |= H_DIRECTION | V_DIRECTION; + + writel(val, &dc->win.win_opt); + + writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); + writel(win->x, &dc->winbuf.addr_h_offset); + writel(win->y, &dc->winbuf.addr_v_offset); + + writel(0xff00, &dc->win.blend_nokey); + writel(0xff00, &dc->win.blend_1win); + + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + val |= GENERAL_UPDATE | WIN_A_UPDATE; + writel(val, &dc->cmd.state_ctrl); +} + +static int update_display_mode(struct tegra_lcd_priv *priv) +{ + struct dc_disp_reg *disp = &priv->dc->disp; + struct display_timing *dt = &priv->timing; + unsigned long val; + + writel(0x0, &disp->disp_timing_opt); + + writel(1 | 1 << 16, &disp->ref_to_sync); + writel(dt->hsync_len.typ | dt->vsync_len.typ << 16, &disp->sync_width); + writel(dt->hback_porch.typ | dt->vback_porch.typ << 16, + &disp->back_porch); + writel((dt->hfront_porch.typ - 1) | (dt->vfront_porch.typ - 1) << 16, + &disp->front_porch); + writel(dt->hactive.typ | (dt->vactive.typ << 16), &disp->disp_active); + + if (priv->soc->has_rgb) { + val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; + val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; + writel(val, &disp->data_enable_opt); + + val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; + val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; + val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; + writel(val, &disp->disp_interface_ctrl); + + writel(0x00010001, &disp->shift_clk_opt); + } + + val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; + val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT; + writel(val, &disp->disp_clk_ctrl); + + return 0; +} + +/* Start up the display and turn on power to PWMs */ +static void basic_init(struct dc_cmd_reg *cmd) +{ + u32 val; + + writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); + writel(0x0000011a, &cmd->cont_syncpt_vsync); + writel(0x00000000, &cmd->int_type); + writel(0x00000000, &cmd->int_polarity); + writel(0x00000000, &cmd->int_mask); + writel(0x00000000, &cmd->int_enb); + + val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; + val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; + val |= PM1_ENABLE; + writel(val, &cmd->disp_pow_ctrl); + + val = readl(&cmd->disp_cmd); + val &= ~CTRL_MODE_MASK; + val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; + writel(val, &cmd->disp_cmd); +} + +static void basic_init_timer(struct dc_disp_reg *disp) +{ + writel(0x00000020, &disp->mem_high_pri); + writel(0x00000001, &disp->mem_high_pri_timer); +} + +static const u32 rgb_enb_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x01000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_data_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00210222, + 0x00002200, + 0x00020000, +}; + +static void rgb_enable(struct tegra_lcd_priv *priv) +{ + struct dc_com_reg *com = &priv->dc->com; + struct display_timing *dt = &priv->timing; + u32 value; + int i; + + for (i = 0; i < PIN_REG_COUNT; i++) { + writel(rgb_enb_tab[i], &com->pin_output_enb[i]); + writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); + writel(rgb_data_tab[i], &com->pin_output_data[i]); + } + + /* configure H- and V-sync signal polarities */ + value = readl(&com->pin_output_polarity[1]); + + if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW) + value |= LHS_OUTPUT_POLARITY_LOW; + else + value &= ~LHS_OUTPUT_POLARITY_LOW; + + if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW) + value |= LVS_OUTPUT_POLARITY_LOW; + else + value &= ~LVS_OUTPUT_POLARITY_LOW; + + writel(value, &com->pin_output_polarity[1]); + + for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) + writel(rgb_sel_tab[i], &com->pin_output_sel[i]); +} + +static int setup_window(struct tegra_lcd_priv *priv, + struct disp_ctl_win *win) +{ + if (priv->rotation) { + win->x = priv->width * 2 - 1; + win->y = priv->height - 1; + } else { + win->x = 0; + win->y = 0; + } + + win->w = priv->width; + win->h = priv->height; + win->out_x = 0; + win->out_y = 0; + win->out_w = priv->width; + win->out_h = priv->height; + win->phys_addr = priv->frame_buffer; + win->stride = priv->width * (1 << priv->log2_bpp) / 8; + + log_debug("%s: depth = %d\n", __func__, priv->log2_bpp); + + switch (priv->log2_bpp) { + case VIDEO_BPP32: + win->fmt = COLOR_DEPTH_R8G8B8A8; + win->bpp = 32; + break; + case VIDEO_BPP16: + win->fmt = COLOR_DEPTH_B5G6R5; + win->bpp = 16; + break; + + default: + log_debug("Unsupported LCD bit depth\n"); + return -1; + } + + return 0; +} + +/** + * Register a new display based on device tree configuration. + * + * The frame buffer can be positioned by U-Boot or overridden by the fdt. + * You should pass in the U-Boot address here, and check the contents of + * struct tegra_lcd_priv to see what was actually chosen. + * + * @param priv Driver's private data + * @param default_lcd_base Default address of LCD frame buffer + * Return: 0 if ok, -1 on error (unsupported bits per pixel) + */ +static int tegra_display_probe(struct tegra_lcd_priv *priv, + void *default_lcd_base) +{ + struct disp_ctl_win window; + unsigned long rate = clk_get_rate(priv->clk_parent); + int ret; + + priv->frame_buffer = (u32)default_lcd_base; + + /* + * We halve the rate if DISP1 parent is PLLD, since actual parent + * is plld_out0 which is PLLD divided by 2. + */ + if (priv->clk_parent->id == CLOCK_ID_DISPLAY || + priv->clk_parent->id == CLOCK_ID_DISPLAY2) + rate /= 2; + + /* + * The pixel clock divider is in 7.1 format (where the bottom bit + * represents 0.5). Here we calculate the divider needed to get from + * the display clock (typically 600MHz) to the pixel clock. We round + * up or down as required. + */ + if (!priv->scdiv) + priv->scdiv = ((rate * 2 + priv->pixel_clock / 2) + / priv->pixel_clock) - 2; + log_debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); + + clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, + rate); + + basic_init(&priv->dc->cmd); + + if (priv->soc->has_timer) + basic_init_timer(&priv->dc->disp); + + if (priv->soc->has_rgb) + rgb_enable(priv); + + if (priv->pixel_clock) + update_display_mode(priv); + + ret = setup_window(priv, &window); + if (ret) + return ret; + + update_window(priv, &window); + + return 0; +} + +static int tegra_lcd_probe(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct tegra_lcd_priv *priv = dev_get_priv(dev); + int ret; + + /* Initialize the Tegra display controller */ + if (priv->soc->has_pgate) { + uint powergate; + + if (priv->pipe) + powergate = TEGRA_POWERGATE_DISB; + else + powergate = TEGRA_POWERGATE_DIS; + + ret = tegra_powergate_power_off(powergate); + if (ret < 0) { + log_debug("failed to power off DISP gate: %d", ret); + return ret; + } + + ret = tegra_powergate_sequence_power_up(powergate, + priv->clk->id); + if (ret < 0) { + log_debug("failed to power up DISP gate: %d", ret); + return ret; + } + } + + /* Get shift clock divider from Tegra DSI if used */ + 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; + } + } + + /* Clean the framebuffer area */ + memset((u8 *)plat->base, 0, plat->size); + flush_dcache_all(); + + ret = tegra_display_probe(priv, (void *)plat->base); + if (ret) { + log_debug("%s: Failed to probe display driver\n", __func__); + 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, + DCACHE_WRITETHROUGH); + + /* Enable flushing after LCD writes if requested */ + video_set_flush_dcache(dev, true); + + uc_priv->xsize = priv->width; + uc_priv->ysize = priv->height; + uc_priv->bpix = priv->log2_bpp; + log_debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, + plat->size); + + 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); + struct display_timing *timing; + ofnode rgb; + int ret; + + priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev); + if (!priv->dc) { + log_debug("%s: No display controller address\n", __func__); + return -EINVAL; + } + + priv->soc = (struct tegra_dc_soc_info *)dev_get_driver_data(dev); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DC clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DC clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + priv->rotation = dev_read_bool(dev, "nvidia,180-rotation"); + priv->pipe = dev_read_u32_default(dev, "nvidia,head", 0); + + /* + * 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. + */ + + 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; + } + + 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; + } + } + } + + 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); + return -EINVAL; + } + } + + timing = &priv->timing; + priv->width = timing->hactive.typ; + priv->height = timing->vactive.typ; + priv->pixel_clock = timing->pixelclock.typ; + priv->log2_bpp = VIDEO_BPP16; + + return 0; +} + +static int tegra_lcd_bind(struct udevice *dev) +{ + struct video_uc_plat *plat = dev_get_uclass_plat(dev); + + plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * + (1 << LCD_MAX_LOG2_BPP) / 8; + + return dm_scan_fdt_dev(dev); +} + +static const struct tegra_dc_soc_info tegra20_dc_soc_info = { + .has_timer = true, + .has_rgb = true, + .has_pgate = false, +}; + +static const struct tegra_dc_soc_info tegra30_dc_soc_info = { + .has_timer = false, + .has_rgb = true, + .has_pgate = false, +}; + +static const struct tegra_dc_soc_info tegra114_dc_soc_info = { + .has_timer = false, + .has_rgb = false, + .has_pgate = true, +}; + +static const struct udevice_id tegra_lcd_ids[] = { + { + .compatible = "nvidia,tegra20-dc", + .data = (ulong)&tegra20_dc_soc_info + }, { + .compatible = "nvidia,tegra30-dc", + .data = (ulong)&tegra30_dc_soc_info + }, { + .compatible = "nvidia,tegra114-dc", + .data = (ulong)&tegra114_dc_soc_info + }, { + .compatible = "nvidia,tegra124-dc", + .data = (ulong)&tegra114_dc_soc_info + }, { + /* sentinel */ + } +}; + +U_BOOT_DRIVER(tegra_lcd) = { + .name = "tegra_lcd", + .id = UCLASS_VIDEO, + .of_match = tegra_lcd_ids, + .bind = tegra_lcd_bind, + .probe = tegra_lcd_probe, + .of_to_plat = tegra_lcd_of_to_plat, + .priv_auto = sizeof(struct tegra_lcd_priv), +}; diff --git a/drivers/video/tegra/tegra-dc.h b/drivers/video/tegra/tegra-dc.h new file mode 100644 index 00000000000..2a4013b3355 --- /dev/null +++ b/drivers/video/tegra/tegra-dc.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation + */ + +#ifndef _TEGRA_DC_H +#define _TEGRA_DC_H + +#ifndef __ASSEMBLY__ +#include +#endif + +/* arch-tegra/dc exists only because T124 uses it */ +#include + +struct tegra_dc_plat { + struct udevice *dev; /* Display controller device */ + struct dc_ctlr *dc; /* Display controller regmap */ + int pipe; /* DC number: 0 for A, 1 for B */ + ulong scdiv; /* Shift clock divider */ +}; + +/* This holds information about a window which can be displayed */ +struct disp_ctl_win { + enum win_color_depth_id fmt; /* Color depth/format */ + unsigned int bpp; /* Bits per pixel */ + phys_addr_t phys_addr; /* Physical address in memory */ + unsigned int x; /* Horizontal address offset (bytes) */ + unsigned int y; /* Veritical address offset (bytes) */ + unsigned int w; /* Width of source window */ + unsigned int h; /* Height of source window */ + unsigned int stride; /* Number of bytes per line */ + unsigned int out_x; /* Left edge of output window (col) */ + unsigned int out_y; /* Top edge of output window (row) */ + unsigned int out_w; /* Width of output window in pixels */ + unsigned int out_h; /* Height of output window in pixels */ +}; + +#endif /* _TEGRA_DC_H */ diff --git a/drivers/video/tegra/tegra-dsi.c b/drivers/video/tegra/tegra-dsi.c new file mode 100644 index 00000000000..a96fba01ee4 --- /dev/null +++ b/drivers/video/tegra/tegra-dsi.c @@ -0,0 +1,1148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "tegra-dc.h" +#include "tegra-dsi.h" +#include "mipi-phy.h" + +/* List of supported DSI bridges */ +enum { + DSI_V0, + DSI_V1, +}; + +struct tegra_dsi_priv { + struct mipi_dsi_host host; + struct mipi_dsi_device device; + struct mipi_dphy_timing dphy_timing; + + struct udevice *panel; + struct udevice *mipi; + struct display_timing timing; + + struct dsi_ctlr *dsi; + struct udevice *avdd; + + enum tegra_dsi_format format; + + struct clk *clk; + struct clk *clk_parent; + + int video_fifo_depth; + int host_fifo_depth; + + u32 calibration_pads; + u32 version; + + /* for ganged-mode support */ + struct udevice *master; + struct udevice *slave; +}; + +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 |= DSI_ENABLE; + writel(value, &dc->disp.disp_win_opt); + + writel(GENERAL_UPDATE, &dc->cmd.state_ctrl); + writel(GENERAL_ACT_REQ, &dc->cmd.state_ctrl); +} + +static const char * const error_report[16] = { + "SoT Error", + "SoT Sync Error", + "EoT Sync Error", + "Escape Mode Entry Command Error", + "Low-Power Transmit Sync Error", + "Peripheral Timeout Error", + "False Control Error", + "Contention Detected", + "ECC Error, single-bit", + "ECC Error, multi-bit", + "Checksum Error", + "DSI Data Type Not Recognized", + "DSI VC ID Invalid", + "Invalid Transmission Length", + "Reserved", + "DSI Protocol Violation", +}; + +static ssize_t tegra_dsi_read_response(struct dsi_misc_reg *misc, + const struct mipi_dsi_msg *msg, + size_t count) +{ + u8 *rx = msg->rx_buf; + unsigned int i, j, k; + size_t size = 0; + u16 errors; + u32 value; + + /* read and parse packet header */ + value = readl(&misc->dsi_rd_data); + + switch (value & 0x3f) { + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: + errors = (value >> 8) & 0xffff; + printf("%s: Acknowledge and error report: %04x\n", + __func__, errors); + for (i = 0; i < ARRAY_SIZE(error_report); i++) + if (errors & BIT(i)) + printf("%s: %2u: %s\n", __func__, i, + error_report[i]); + break; + + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: + rx[0] = (value >> 8) & 0xff; + size = 1; + break; + + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: + rx[0] = (value >> 8) & 0xff; + rx[1] = (value >> 16) & 0xff; + size = 2; + break; + + case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: + size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); + break; + + case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: + size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); + break; + + default: + printf("%s: unhandled response type: %02x\n", + __func__, value & 0x3f); + return -EPROTO; + } + + size = min(size, msg->rx_len); + + if (msg->rx_buf && size > 0) { + for (i = 0, j = 0; i < count - 1; i++, j += 4) { + u8 *rx = msg->rx_buf + j; + + value = readl(&misc->dsi_rd_data); + + for (k = 0; k < 4 && (j + k) < msg->rx_len; k++) + rx[j + k] = (value >> (k << 3)) & 0xff; + } + } + + return size; +} + +static int tegra_dsi_transmit(struct dsi_misc_reg *misc, + unsigned long timeout) +{ + writel(DSI_TRIGGER_HOST, &misc->dsi_trigger); + + while (timeout--) { + u32 value = readl(&misc->dsi_trigger); + + if ((value & DSI_TRIGGER_HOST) == 0) + return 0; + + udelay(1000); + } + + debug("timeout waiting for transmission to complete\n"); + return -ETIMEDOUT; +} + +static int tegra_dsi_wait_for_response(struct dsi_misc_reg *misc, + unsigned long timeout) +{ + while (timeout--) { + u32 value = readl(&misc->dsi_status); + u8 count = value & 0x1f; + + if (count > 0) + return count; + + udelay(1000); + } + + debug("peripheral returned no data\n"); + return -ETIMEDOUT; +} + +static void tegra_dsi_writesl(struct dsi_misc_reg *misc, + const void *buffer, size_t size) +{ + const u8 *buf = buffer; + size_t i, j; + u32 value; + + for (j = 0; j < size; j += 4) { + value = 0; + + for (i = 0; i < 4 && j + i < size; i++) + value |= buf[j + i] << (i << 3); + + writel(value, &misc->dsi_wr_data); + } +} + +static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct udevice *dev = (struct udevice *)host->dev; + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_misc_reg *misc = &priv->dsi->misc; + struct mipi_dsi_packet packet; + const u8 *header; + size_t count; + ssize_t err; + u32 value; + + err = mipi_dsi_create_packet(&packet, msg); + if (err < 0) + return err; + + header = packet.header; + + /* maximum FIFO depth is 1920 words */ + if (packet.size > priv->video_fifo_depth * 4) + return -ENOSPC; + + /* reset underflow/overflow flags */ + value = readl(&misc->dsi_status); + if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) { + value = DSI_HOST_CONTROL_FIFO_RESET; + writel(value, &misc->host_dsi_ctrl); + udelay(10); + } + + value = readl(&misc->dsi_pwr_ctrl); + value |= DSI_POWER_CONTROL_ENABLE; + writel(value, &misc->dsi_pwr_ctrl); + + mdelay(5); + + value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | + DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; + + if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) + value |= DSI_HOST_CONTROL_HS; + + /* + * The host FIFO has a maximum of 64 words, so larger transmissions + * need to use the video FIFO. + */ + if (packet.size > priv->host_fifo_depth * 4) + value |= DSI_HOST_CONTROL_FIFO_SEL; + + writel(value, &misc->host_dsi_ctrl); + + /* + * For reads and messages with explicitly requested ACK, generate a + * BTA sequence after the transmission of the packet. + */ + if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || + (msg->rx_buf && msg->rx_len > 0)) { + value = readl(&misc->host_dsi_ctrl); + value |= DSI_HOST_CONTROL_PKT_BTA; + writel(value, &misc->host_dsi_ctrl); + } + + value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE; + writel(value, &misc->dsi_ctrl); + + /* write packet header, ECC is generated by hardware */ + value = header[2] << 16 | header[1] << 8 | header[0]; + writel(value, &misc->dsi_wr_data); + + /* write payload (if any) */ + if (packet.payload_length > 0) + tegra_dsi_writesl(misc, packet.payload, + packet.payload_length); + + err = tegra_dsi_transmit(misc, 250); + if (err < 0) + return err; + + if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || + (msg->rx_buf && msg->rx_len > 0)) { + err = tegra_dsi_wait_for_response(misc, 250); + if (err < 0) + return err; + + count = err; + + value = readl(&misc->dsi_rd_data); + switch (value) { + case 0x84: + debug("%s: ACK\n", __func__); + break; + + case 0x87: + debug("%s: ESCAPE\n", __func__); + break; + + default: + printf("%s: unknown status: %08x\n", __func__, value); + break; + } + + if (count > 1) { + err = tegra_dsi_read_response(misc, msg, count); + if (err < 0) { + printf("%s: failed to parse response: %zd\n", + __func__, err); + } else { + /* + * For read commands, return the number of + * bytes returned by the peripheral. + */ + count = err; + } + } + } else { + /* + * For write commands, we have transmitted the 4-byte header + * plus the variable-length payload. + */ + count = 4 + packet.payload_length; + } + + return count; +} + +struct mipi_dsi_host_ops tegra_dsi_bridge_host_ops = { + .transfer = tegra_dsi_host_transfer, +}; + +#define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) +#define PKT_LEN0(len) (((len) & 0x07) << 0) +#define PKT_ID1(id) ((((id) & 0x3f) << 13) | (1 << 19)) +#define PKT_LEN1(len) (((len) & 0x07) << 10) +#define PKT_ID2(id) ((((id) & 0x3f) << 23) | (1 << 29)) +#define PKT_LEN2(len) (((len) & 0x07) << 20) + +#define PKT_LP BIT(30) +#define NUM_PKT_SEQ 12 + +/* + * non-burst mode with sync pulses + */ +static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = { + [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 1] = 0, + [ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 3] = 0, + [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 5] = 0, + [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), + [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | + PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | + PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), + [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 9] = 0, + [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), + [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | + PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | + PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), +}; + +/* + * non-burst mode with sync events + */ +static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = { + [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 1] = 0, + [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 3] = 0, + [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 5] = 0, + [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | + PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), + [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), + [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 9] = 0, + [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | + PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), + [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), +}; + +static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { + [ 0] = 0, + [ 1] = 0, + [ 2] = 0, + [ 3] = 0, + [ 4] = 0, + [ 5] = 0, + [ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP, + [ 7] = 0, + [ 8] = 0, + [ 9] = 0, + [10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP, + [11] = 0, +}; + +static void tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, + unsigned int *mulp, unsigned int *divp) +{ + switch (format) { + case MIPI_DSI_FMT_RGB666_PACKED: + case MIPI_DSI_FMT_RGB888: + *mulp = 3; + *divp = 1; + break; + + case MIPI_DSI_FMT_RGB565: + *mulp = 2; + *divp = 1; + break; + + case MIPI_DSI_FMT_RGB666: + *mulp = 9; + *divp = 4; + break; + + default: + break; + } +} + +static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format, + enum tegra_dsi_format *fmt) +{ + switch (format) { + case MIPI_DSI_FMT_RGB888: + *fmt = TEGRA_DSI_FORMAT_24P; + break; + + case MIPI_DSI_FMT_RGB666: + *fmt = TEGRA_DSI_FORMAT_18NP; + break; + + case MIPI_DSI_FMT_RGB666_PACKED: + *fmt = TEGRA_DSI_FORMAT_18P; + break; + + case MIPI_DSI_FMT_RGB565: + *fmt = TEGRA_DSI_FORMAT_16P; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad) +{ + u32 value; + + /* start calibration */ + value = DSI_PAD_CONTROL_PAD_LPUPADJ(0x1) | + DSI_PAD_CONTROL_PAD_LPDNADJ(0x1) | + DSI_PAD_CONTROL_PAD_PREEMP_EN(0x1) | + DSI_PAD_CONTROL_PAD_SLEWDNADJ(0x6) | + DSI_PAD_CONTROL_PAD_SLEWUPADJ(0x6) | + DSI_PAD_CONTROL_PAD_PDIO(0) | + DSI_PAD_CONTROL_PAD_PDIO_CLK(0) | + DSI_PAD_CONTROL_PAD_PULLDN_ENAB(0); + writel(value, &pad->pad_ctrl); + + clock_enable(PERIPH_ID_VI); + clock_enable(PERIPH_ID_CSI); + udelay(2); + reset_set_enable(PERIPH_ID_VI, 0); + reset_set_enable(PERIPH_ID_CSI, 0); + + value = MIPI_CAL_TERMOSA(0x4); + writel(value, TEGRA_VI_BASE + (CSI_CILA_MIPI_CAL_CONFIG_0 << 2)); + + value = MIPI_CAL_TERMOSB(0x4); + writel(value, TEGRA_VI_BASE + (CSI_CILB_MIPI_CAL_CONFIG_0 << 2)); + + value = MIPI_CAL_HSPUOSD(0x3) | MIPI_CAL_HSPDOSD(0x4); + writel(value, TEGRA_VI_BASE + (CSI_DSI_MIPI_CAL_CONFIG << 2)); + + value = PAD_DRIV_DN_REF(0x5) | PAD_DRIV_UP_REF(0x7); + writel(value, TEGRA_VI_BASE + (CSI_MIPIBIAS_PAD_CONFIG << 2)); + + value = PAD_CIL_PDVREG(0x0); + writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2)); +} + +static void tegra_dsi_mipi_calibrate(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad; + u32 value; + int ret; + + ret = misc_set_enabled(priv->mipi, true); + if (ret) + log_debug("%s: failed to enable MIPI calibration: %d\n", + __func__, ret); + + writel(0, &pad->pad_ctrl); + writel(0, &pad->pad_ctrl_1); + writel(0, &pad->pad_ctrl_2); + writel(0, &pad->pad_ctrl_3); + writel(0, &pad->pad_ctrl_4); + + /* DSI pad enable */ + value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); + writel(value, &pad->pad_ctrl); + + value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | + DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | + DSI_PAD_OUT_CLK(0x0); + writel(value, &pad->pad_ctrl_2); + + value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | + DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); + writel(value, &pad->pad_ctrl_3); + + ret = misc_write(priv->mipi, priv->calibration_pads, NULL, 0); + if (ret) + log_debug("%s: MIPI calibration failed %d\n", __func__, ret); + + if (priv->slave) + tegra_dsi_mipi_calibrate(priv->slave); +} + +static void tegra_dsi_set_timeout(struct udevice *dev, + unsigned long bclk, + unsigned int vrefresh) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timeout_reg *rtimeout = &priv->dsi->timeout; + unsigned int timeout; + u32 value; + + /* one frame high-speed transmission timeout */ + timeout = (bclk / vrefresh) / 512; + value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); + writel(value, &rtimeout->dsi_timeout_0); + + /* 2 ms peripheral timeout for panel */ + timeout = 2 * bclk / 512 * 1000; + value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); + writel(value, &rtimeout->dsi_timeout_1); + + value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); + writel(value, &rtimeout->dsi_to_tally); + + if (priv->slave) + tegra_dsi_set_timeout(priv->slave, bclk, vrefresh); +} + +static void tegra_dsi_set_phy_timing(struct udevice *dev, + unsigned long period, + const struct mipi_dphy_timing *dphy_timing) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timing_reg *ptiming = &priv->dsi->ptiming; + u32 value; + + value = DSI_TIMING_FIELD(dphy_timing->hsexit, period, 1) << 24 | + DSI_TIMING_FIELD(dphy_timing->hstrail, period, 0) << 16 | + DSI_TIMING_FIELD(dphy_timing->hszero, period, 3) << 8 | + DSI_TIMING_FIELD(dphy_timing->hsprepare, period, 1); + writel(value, &ptiming->dsi_phy_timing_0); + + value = DSI_TIMING_FIELD(dphy_timing->clktrail, period, 1) << 24 | + DSI_TIMING_FIELD(dphy_timing->clkpost, period, 1) << 16 | + DSI_TIMING_FIELD(dphy_timing->clkzero, period, 1) << 8 | + DSI_TIMING_FIELD(dphy_timing->lpx, period, 1); + writel(value, &ptiming->dsi_phy_timing_1); + + value = DSI_TIMING_FIELD(dphy_timing->clkprepare, period, 1) << 16 | + DSI_TIMING_FIELD(dphy_timing->clkpre, period, 1) << 8 | + DSI_TIMING_FIELD(0xff * period, period, 0) << 0; + writel(value, &ptiming->dsi_phy_timing_2); + + value = DSI_TIMING_FIELD(dphy_timing->taget, period, 1) << 16 | + DSI_TIMING_FIELD(dphy_timing->tasure, period, 1) << 8 | + DSI_TIMING_FIELD(dphy_timing->tago, period, 1); + writel(value, &ptiming->dsi_bta_timing); + + if (priv->slave) + tegra_dsi_set_phy_timing(priv->slave, period, dphy_timing); +} + +static u32 tegra_dsi_get_lanes(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + + if (priv->master) { + struct tegra_dsi_priv *mpriv = dev_get_priv(priv->master); + struct mipi_dsi_device *mdevice = &mpriv->device; + + return mdevice->lanes + device->lanes; + } + + if (priv->slave) { + struct tegra_dsi_priv *spriv = dev_get_priv(priv->slave); + struct mipi_dsi_device *sdevice = &spriv->device; + + return device->lanes + sdevice->lanes; + } + + return device->lanes; +} + +static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, + unsigned int size) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_ganged_mode_reg *ganged = &priv->dsi->ganged; + + writel(start, &ganged->ganged_mode_start); + writel(size << 16 | size, &ganged->ganged_mode_size); + writel(DSI_GANGED_MODE_CONTROL_ENABLE, &ganged->ganged_mode_ctrl); +} + +static void tegra_dsi_configure(struct udevice *dev, unsigned int pipe, + unsigned long mode_flags) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct display_timing *timing = &priv->timing; + + struct dsi_misc_reg *misc = &priv->dsi->misc; + struct dsi_pkt_seq_reg *pkt = &priv->dsi->pkt; + struct dsi_pkt_len_reg *len = &priv->dsi->len; + + unsigned int hact, hsw, hbp, hfp, i, mul, div; + const u32 *pkt_seq; + u32 value; + + tegra_dsi_get_muldiv(device->format, &mul, &div); + + if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { + printf("[DSI] Non-burst video mode with sync pulses\n"); + pkt_seq = pkt_seq_video_non_burst_sync_pulses; + } else if (mode_flags & MIPI_DSI_MODE_VIDEO) { + printf("[DSI] Non-burst video mode with sync events\n"); + pkt_seq = pkt_seq_video_non_burst_sync_events; + } else { + printf("[DSI] Command mode\n"); + pkt_seq = pkt_seq_command_mode; + } + + value = DSI_CONTROL_CHANNEL(0) | + DSI_CONTROL_FORMAT(priv->format) | + DSI_CONTROL_LANES(device->lanes - 1) | + DSI_CONTROL_SOURCE(pipe); + writel(value, &misc->dsi_ctrl); + + writel(priv->video_fifo_depth, &misc->dsi_max_threshold); + + value = DSI_HOST_CONTROL_HS; + writel(value, &misc->host_dsi_ctrl); + + value = readl(&misc->dsi_ctrl); + + if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) + value |= DSI_CONTROL_HS_CLK_CTRL; + + value &= ~DSI_CONTROL_TX_TRIG(3); + + /* enable DCS commands for command mode */ + if (mode_flags & MIPI_DSI_MODE_VIDEO) + value &= ~DSI_CONTROL_DCS_ENABLE; + else + value |= DSI_CONTROL_DCS_ENABLE; + + value |= DSI_CONTROL_VIDEO_ENABLE; + value &= ~DSI_CONTROL_HOST_ENABLE; + writel(value, &misc->dsi_ctrl); + + for (i = 0; i < NUM_PKT_SEQ; i++) + writel(pkt_seq[i], &pkt->dsi_pkt_seq_0_lo + i); + + if (mode_flags & MIPI_DSI_MODE_VIDEO) { + /* horizontal active pixels */ + hact = timing->hactive.typ * mul / div; + + /* horizontal sync width */ + hsw = timing->hsync_len.typ * mul / div; + + /* horizontal back porch */ + hbp = timing->hback_porch.typ * mul / div; + + /* horizontal front porch */ + hfp = timing->hfront_porch.typ * mul / div; + + if (priv->master || priv->slave) { + hact /= 2; + hsw /= 2; + hbp = hbp / 2 - 1; + hfp /= 2; + } + + if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) + hbp += hsw; + + /* subtract packet overhead */ + hsw -= 10; + hbp -= 14; + hfp -= 8; + + writel(hsw << 16 | 0, &len->dsi_pkt_len_0_1); + writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3); + writel(hfp, &len->dsi_pkt_len_4_5); + writel(0x0f0f << 16, &len->dsi_pkt_len_6_7); + } else { + if (priv->master || priv->slave) { + /* + * For ganged mode, assume symmetric left-right mode. + */ + value = 1 + (timing->hactive.typ / 2) * mul / div; + } else { + /* 1 byte (DCS command) + pixel data */ + value = 1 + timing->hactive.typ * mul / div; + } + + writel(0, &len->dsi_pkt_len_0_1); + writel(value << 16, &len->dsi_pkt_len_2_3); + writel(value << 16, &len->dsi_pkt_len_4_5); + writel(0, &len->dsi_pkt_len_6_7); + + value = MIPI_DCS_WRITE_MEMORY_START << 8 | + MIPI_DCS_WRITE_MEMORY_CONTINUE; + writel(value, &len->dsi_dcs_cmds); + } + + /* set SOL delay */ + if (priv->master || priv->slave) { + unsigned long delay, bclk, bclk_ganged; + unsigned int lanes = tegra_dsi_get_lanes(dev); + unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + + timing->hback_porch.typ + timing->hsync_len.typ; + + /* SOL to valid, valid to FIFO and FIFO write delay */ + delay = 4 + 4 + 2; + delay = DIV_ROUND_UP(delay * mul, div * lanes); + /* FIFO read delay */ + delay = delay + 6; + + bclk = DIV_ROUND_UP(htotal * mul, div * lanes); + bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); + value = bclk - bclk_ganged + delay + 20; + } else { + /* set SOL delay (for non-burst mode only) */ + value = 8 * mul / div; + } + + writel(value, &misc->dsi_sol_delay); + + if (priv->slave) { + tegra_dsi_configure(priv->slave, pipe, mode_flags); + /* + * TODO: Support modes other than symmetrical left-right + * split. + */ + tegra_dsi_ganged_enable(dev, 0, timing->hactive.typ / 2); + tegra_dsi_ganged_enable(priv->slave, timing->hactive.typ / 2, + timing->hactive.typ / 2); + } +} + +static void tegra_dsi_enable(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_misc_reg *misc = &priv->dsi->misc; + u32 value; + + /* enable DSI controller */ + value = readl(&misc->dsi_pwr_ctrl); + value |= DSI_POWER_CONTROL_ENABLE; + writel(value, &misc->dsi_pwr_ctrl); + + if (priv->slave) + tegra_dsi_enable(priv->slave); +} + +static int tegra_dsi_encoder_enable(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct mipi_dsi_device *device = &priv->device; + struct display_timing *timing = &priv->timing; + struct dsi_misc_reg *misc = &priv->dsi->misc; + unsigned int mul, div; + unsigned long bclk, plld, period; + u32 value, lanes; + int ret; + + /* If for some reasone DSI is enabled then it needs to + * be disabled in order for the panel initialization + * commands to be properly sent. + */ + value = readl(&misc->dsi_pwr_ctrl); + + if (value & DSI_POWER_CONTROL_ENABLE) { + value = readl(&misc->dsi_pwr_ctrl); + value &= ~DSI_POWER_CONTROL_ENABLE; + writel(value, &misc->dsi_pwr_ctrl); + } + + /* Disable interrupt */ + writel(0, &misc->int_enable); + + if (priv->version) + tegra_dsi_mipi_calibrate(dev); + else + tegra_dsi_pad_calibrate(&priv->dsi->pad); + + tegra_dsi_get_muldiv(device->format, &mul, &div); + + /* compute byte clock */ + lanes = tegra_dsi_get_lanes(dev); + bclk = (timing->pixelclock.typ * mul) / (div * lanes); + + tegra_dsi_set_timeout(dev, bclk, 60); + + /* + * Compute bit clock and round up to the next MHz. + */ + plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC; + period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld); + + ret = mipi_dphy_timing_get_default(&priv->dphy_timing, period); + if (ret < 0) { + printf("%s: failed to get D-PHY timing: %d\n", __func__, ret); + return ret; + } + + ret = mipi_dphy_timing_validate(&priv->dphy_timing, period); + if (ret < 0) { + printf("%s: failed to validate D-PHY timing: %d\n", __func__, ret); + return ret; + } + + /* + * The D-PHY timing fields are expressed in byte-clock cycles, so + * multiply the period by 8. + */ + tegra_dsi_set_phy_timing(dev, period * 8, &priv->dphy_timing); + + /* Perform panel HW setup */ + ret = panel_enable_backlight(priv->panel); + if (ret) + return ret; + + tegra_dsi_configure(dev, dc_plat->pipe, device->mode_flags); + + tegra_dc_enable_controller(dev); + + tegra_dsi_enable(dev); + + return 0; +} + +static int tegra_dsi_bridge_set_panel(struct udevice *dev, int percent) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + + /* Turn on/off backlight */ + return panel_set_backlight(priv->panel, percent); +} + +static int tegra_dsi_panel_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +static void tegra_dsi_init_clocks(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct mipi_dsi_device *device = &priv->device; + unsigned int mul, div, lanes; + unsigned long bclk, plld; + + /* Switch parents of DSI clocks in case of not standard parent */ + if (priv->clk->id == PERIPH_ID_DSI && + priv->clk_parent->id == CLOCK_ID_DISPLAY2) { + /* Change DSIA clock parent to PLLD2 */ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + /* DSIA_CLK_SRC */ + setbits_le32(&clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_base, + BIT(25)); + } + + if (priv->clk->id == PERIPH_ID_DSIB && + priv->clk_parent->id == CLOCK_ID_DISPLAY) { + /* Change DSIB clock parent to match DSIA */ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + /* DSIB_CLK_SRC */ + clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); + } + + tegra_dsi_get_muldiv(device->format, &mul, &div); + + lanes = tegra_dsi_get_lanes(dev); + bclk = (priv->timing.pixelclock.typ * mul) / (div * lanes); + + plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC); + + dc_plat->scdiv = ((plld * USEC_PER_SEC + + priv->timing.pixelclock.typ / 2) / + priv->timing.pixelclock.typ) - 2; + + /* + * BUG: If DISP1 is a PLLD/D2 child, it cannot go over 370MHz. The + * cause of this is not quite clear. This can be overcomed by + * halving the PLLD/D2 if the target rate is > 800MHz. This way + * DISP1 and DSI clocks will be equal. + */ + if (plld > 800) + plld /= 2; + + 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, plld, 12, 0, 8); + break; + + case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ + clock_set_rate(priv->clk_parent->id, plld, 26, 0, 8); + break; + + case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ + case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */ + clock_set_rate(priv->clk_parent->id, plld, 13, 0, 8); + break; + + case CLOCK_OSC_FREQ_19_2: + case CLOCK_OSC_FREQ_38_4: + default: + /* + * These are not supported. + */ + break; + } + + clk_enable(priv->clk); +} + +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_VIDEO_BRIDGE, dev, + "nvidia,ganged-mode", &gangster); + if (gangster) { + /* Ganged mode is set */ + struct tegra_dsi_priv *spriv = dev_get_priv(gangster); + + mpriv->slave = gangster; + spriv->master = dev; + } + + return 0; +} + +static int tegra_dsi_bridge_probe(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + struct mipi_dsi_panel_plat *mipi_plat; + struct reset_ctl reset_ctl; + int ret; + + priv->version = dev_get_driver_data(dev); + + priv->dsi = (struct dsi_ctlr *)dev_read_addr_ptr(dev); + if (!priv->dsi) { + printf("%s: No display controller address\n", __func__); + return -EINVAL; + } + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DSI clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DSI clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + priv->video_fifo_depth = 1920; + priv->host_fifo_depth = 64; + + tegra_dsi_ganged_probe(dev); + + ret = reset_get_by_name(dev, "dsi", &reset_ctl); + if (ret) { + log_debug("%s: reset_get_by_name() failed: %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "avdd-dsi-csi-supply", &priv->avdd); + if (ret) + debug("%s: Cannot get avdd-dsi-csi-supply: error %d\n", + __func__, ret); + + /* Check all DSI children */ + device_foreach_child(priv->panel, dev) { + if (device_get_uclass_id(priv->panel) == UCLASS_PANEL) + break; + } + + /* if loop exits without panel device return error */ + if (device_get_uclass_id(priv->panel) != UCLASS_PANEL) { + log_debug("%s: panel not found, ret %d\n", __func__, ret); + return -EINVAL; + } + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, dev_ofnode(priv->panel), + &priv->panel); + if (ret) { + log_debug("%s: Cannot get panel: error %d\n", __func__, ret); + return log_ret(ret); + } + + if (priv->version) { + ret = uclass_get_device_by_phandle(UCLASS_MISC, dev, + "nvidia,mipi-calibrate", + &priv->mipi); + if (ret) { + log_debug("%s: cannot get MIPI: error %d\n", __func__, ret); + return ret; + } + + ret = dev_read_u32_index(dev, "nvidia,mipi-calibrate", 1, + &priv->calibration_pads); + if (ret) { + log_debug("%s: cannot get calibration pads: error %d\n", + __func__, ret); + return ret; + } + } + + panel_get_display_timing(priv->panel, &priv->timing); + + mipi_plat = dev_get_plat(priv->panel); + mipi_plat->device = device; + + priv->host.dev = (struct device *)dev; + priv->host.ops = &tegra_dsi_bridge_host_ops; + + device->host = &priv->host; + device->lanes = mipi_plat->lanes; + device->format = mipi_plat->format; + device->mode_flags = mipi_plat->mode_flags; + + tegra_dsi_get_format(device->format, &priv->format); + + reset_assert(&reset_ctl); + + ret = regulator_set_enable_if_allowed(priv->avdd, true); + if (ret && ret != -ENOSYS) + return ret; + + tegra_dsi_init_clocks(dev); + + mdelay(2); + reset_deassert(&reset_ctl); + + return 0; +} + +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, +}; + +static const struct udevice_id tegra_dsi_bridge_ids[] = { + { .compatible = "nvidia,tegra20-dsi", .data = DSI_V0 }, + { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 }, + { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 }, + { .compatible = "nvidia,tegra124-dsi", .data = DSI_V1 }, + { } +}; + +U_BOOT_DRIVER(tegra_dsi) = { + .name = "tegra_dsi", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = tegra_dsi_bridge_ids, + .ops = &tegra_dsi_bridge_ops, + .bind = dm_scan_fdt_dev, + .probe = tegra_dsi_bridge_probe, + .plat_auto = sizeof(struct tegra_dc_plat), + .priv_auto = sizeof(struct tegra_dsi_priv), +}; diff --git a/drivers/video/tegra/tegra-dsi.h b/drivers/video/tegra/tegra-dsi.h new file mode 100644 index 00000000000..683c5e31a34 --- /dev/null +++ b/drivers/video/tegra/tegra-dsi.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation + */ + +#ifndef _TEGRA_DSI_H +#define _TEGRA_DSI_H + +#ifndef __ASSEMBLY__ +#include +#endif + +/* Register definitions for the Tegra display serial interface */ + +/* DSI syncpoint register 0x000 ~ 0x002 */ +struct dsi_syncpt_reg { + /* Address 0x000 ~ 0x002 */ + uint incr_syncpt; /* _INCR_SYNCPT_0 */ + uint incr_syncpt_ctrl; /* _INCR_SYNCPT_CNTRL_0 */ + uint incr_syncpt_err; /* _INCR_SYNCPT_ERROR_0 */ +}; + +/* DSI misc register 0x008 ~ 0x015 */ +struct dsi_misc_reg { + /* Address 0x008 ~ 0x015 */ + uint ctxsw; /* _CTXSW_0 */ + uint dsi_rd_data; /* _DSI_RD_DATA_0 */ + uint dsi_wr_data; /* _DSI_WR_DATA_0 */ + uint dsi_pwr_ctrl; /* _DSI_POWER_CONTROL_0 */ + uint int_enable; /* _INT_ENABLE_0 */ + uint int_status; /* _INT_STATUS_0 */ + uint int_mask; /* _INT_MASK_0 */ + uint host_dsi_ctrl; /* _HOST_DSI_CONTROL_0 */ + uint dsi_ctrl; /* _DSI_CONTROL_0 */ + uint dsi_sol_delay; /* _DSI_SOL_DELAY_0 */ + uint dsi_max_threshold; /* _DSI_MAX_THRESHOLD_0 */ + uint dsi_trigger; /* _DSI_TRIGGER_0 */ + uint dsi_tx_crc; /* _DSI_TX_CRC_0 */ + uint dsi_status; /* _DSI_STATUS_0 */ +}; + +/* DSI init sequence register 0x01a ~ 0x022 */ +struct dsi_init_seq_reg { + /* Address 0x01a ~ 0x022 */ + uint dsi_init_seq_ctrl; /* _DSI_INIT_SEQ_CONTROL_0 */ + uint dsi_init_seq_data_0; /* _DSI_INIT_SEQ_DATA_0_0 */ + uint dsi_init_seq_data_1; /* _DSI_INIT_SEQ_DATA_1_0 */ + uint dsi_init_seq_data_2; /* _DSI_INIT_SEQ_DATA_2_0 */ + uint dsi_init_seq_data_3; /* _DSI_INIT_SEQ_DATA_3_0 */ + uint dsi_init_seq_data_4; /* _DSI_INIT_SEQ_DATA_4_0 */ + uint dsi_init_seq_data_5; /* _DSI_INIT_SEQ_DATA_5_0 */ + uint dsi_init_seq_data_6; /* _DSI_INIT_SEQ_DATA_6_0 */ + uint dsi_init_seq_data_7; /* _DSI_INIT_SEQ_DATA_7_0 */ +}; + +/* DSI packet sequence register 0x023 ~ 0x02e */ +struct dsi_pkt_seq_reg { + /* Address 0x023 ~ 0x02e */ + uint dsi_pkt_seq_0_lo; /* _DSI_PKT_SEQ_0_LO_0 */ + uint dsi_pkt_seq_0_hi; /* _DSI_PKT_SEQ_0_HI_0 */ + uint dsi_pkt_seq_1_lo; /* _DSI_PKT_SEQ_1_LO_0 */ + uint dsi_pkt_seq_1_hi; /* _DSI_PKT_SEQ_1_HI_0 */ + uint dsi_pkt_seq_2_lo; /* _DSI_PKT_SEQ_2_LO_0 */ + uint dsi_pkt_seq_2_hi; /* _DSI_PKT_SEQ_2_HI_0 */ + uint dsi_pkt_seq_3_lo; /* _DSI_PKT_SEQ_3_LO_0 */ + uint dsi_pkt_seq_3_hi; /* _DSI_PKT_SEQ_3_HI_0 */ + uint dsi_pkt_seq_4_lo; /* _DSI_PKT_SEQ_4_LO_0 */ + uint dsi_pkt_seq_4_hi; /* _DSI_PKT_SEQ_4_HI_0 */ + uint dsi_pkt_seq_5_lo; /* _DSI_PKT_SEQ_5_LO_0 */ + uint dsi_pkt_seq_5_hi; /* _DSI_PKT_SEQ_5_HI_0 */ +}; + +/* DSI packet length register 0x033 ~ 0x037 */ +struct dsi_pkt_len_reg { + /* Address 0x033 ~ 0x037 */ + uint dsi_dcs_cmds; /* _DSI_DCS_CMDS_0 */ + uint dsi_pkt_len_0_1; /* _DSI_PKT_LEN_0_1_0 */ + uint dsi_pkt_len_2_3; /* _DSI_PKT_LEN_2_3_0 */ + uint dsi_pkt_len_4_5; /* _DSI_PKT_LEN_4_5_0 */ + uint dsi_pkt_len_6_7; /* _DSI_PKT_LEN_6_7_0 */ +}; + +/* DSI PHY timing register 0x03c ~ 0x03f */ +struct dsi_timing_reg { + /* Address 0x03c ~ 0x03f */ + uint dsi_phy_timing_0; /* _DSI_PHY_TIMING_0_0 */ + uint dsi_phy_timing_1; /* _DSI_PHY_TIMING_1_0 */ + uint dsi_phy_timing_2; /* _DSI_PHY_TIMING_2_0 */ + uint dsi_bta_timing; /* _DSI_BTA_TIMING_0 */ +}; + +/* DSI timeout register 0x044 ~ 0x046 */ +struct dsi_timeout_reg { + /* Address 0x044 ~ 0x046 */ + uint dsi_timeout_0; /* _DSI_TIMEOUT_0_0 */ + uint dsi_timeout_1; /* _DSI_TIMEOUT_1_0 */ + uint dsi_to_tally; /* _DSI_TO_TALLY_0 */ +}; + +/* DSI PAD control register 0x04b ~ 0x052 */ +struct dsi_pad_ctrl_reg { + /* Address 0x04b ~ 0x052 */ + uint pad_ctrl; /* _PAD_CONTROL_0 */ + uint pad_ctrl_cd; /* _PAD_CONTROL_CD_0 */ + uint pad_cd_status; /* _PAD_CD_STATUS_0 */ + uint dsi_vid_mode_control; /* _DSI_VID_MODE_CONTROL_0 */ + uint pad_ctrl_1; /* _PAD_CONTROL_1 */ + uint pad_ctrl_2; /* _PAD_CONTROL_2 */ + uint pad_ctrl_3; /* _PAD_CONTROL_3 */ + uint pad_ctrl_4; /* _PAD_CONTROL_4 */ +}; + +/* DSI ganged mode register 0x053 ~ 0x04e */ +struct dsi_ganged_mode_reg { + /* Address 0x053 ~ 0x055 */ + uint ganged_mode_ctrl; /* _DSI_GANGED_MODE_CONTROL_0 */ + uint ganged_mode_start; /* _DSI_GANGED_MODE_START_0 */ + uint ganged_mode_size; /* _DSI_GANGED_MODE_SIZE_0 */ +}; + +/* Display Serial Interface (DSI_) regs */ +struct dsi_ctlr { + struct dsi_syncpt_reg syncpt; /* SYNCPT register 0x000 ~ 0x002 */ + uint reserved0[5]; /* reserved_0[5] */ + + struct dsi_misc_reg misc; /* MISC register 0x008 ~ 0x015 */ + uint reserved1[4]; /* reserved_1[4] */ + + struct dsi_init_seq_reg init; /* INIT register 0x01a ~ 0x022 */ + struct dsi_pkt_seq_reg pkt; /* PKT register 0x023 ~ 0x02e */ + uint reserved2[4]; /* reserved_2[4] */ + + struct dsi_pkt_len_reg len; /* LEN registers 0x033 ~ 0x037 */ + uint reserved3[4]; /* reserved_3[4] */ + + struct dsi_timing_reg ptiming; /* TIMING registers 0x03c ~ 0x03f */ + uint reserved4[4]; /* reserved_4[4] */ + + struct dsi_timeout_reg timeout; /* TIMEOUT registers 0x044 ~ 0x046 */ + uint reserved5[4]; /* reserved_5[4] */ + + struct dsi_pad_ctrl_reg pad; /* PAD registers 0x04b ~ 0x04e */ + struct dsi_ganged_mode_reg ganged; /* GANGED registers 0x053 ~ 0x055 */ +}; + +#define DSI_POWER_CONTROL_ENABLE BIT(0) + +#define DSI_HOST_CONTROL_FIFO_RESET BIT(21) +#define DSI_HOST_CONTROL_CRC_RESET BIT(20) +#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12) +#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12) +#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12) +#define DSI_HOST_CONTROL_RAW BIT(6) +#define DSI_HOST_CONTROL_HS BIT(5) +#define DSI_HOST_CONTROL_FIFO_SEL BIT(4) +#define DSI_HOST_CONTROL_IMM_BTA BIT(3) +#define DSI_HOST_CONTROL_PKT_BTA BIT(2) +#define DSI_HOST_CONTROL_CS BIT(1) +#define DSI_HOST_CONTROL_ECC BIT(0) + +#define DSI_CONTROL_HS_CLK_CTRL BIT(20) +#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16) +#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12) +#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8) +#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4) +#define DSI_CONTROL_DCS_ENABLE BIT(3) +#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2) +#define DSI_CONTROL_VIDEO_ENABLE BIT(1) +#define DSI_CONTROL_HOST_ENABLE BIT(0) + +#define DSI_TRIGGER_HOST BIT(1) +#define DSI_TRIGGER_VIDEO BIT(0) + +#define DSI_STATUS_IDLE BIT(10) +#define DSI_STATUS_UNDERFLOW BIT(9) +#define DSI_STATUS_OVERFLOW BIT(8) + +#define DSI_TIMING_FIELD(value, period, hwinc) \ + ((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff) + +#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16) +#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0) +#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16) +#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0) + +#define DSI_TALLY_TA(x) (((x) & 0xff) << 16) +#define DSI_TALLY_LRX(x) (((x) & 0xff) << 8) +#define DSI_TALLY_HTX(x) (((x) & 0xff) << 0) + +#define DSI_PAD_CONTROL_PAD_PULLDN_ENAB(x) (((x) & 0x1) << 28) +#define DSI_PAD_CONTROL_PAD_SLEWUPADJ(x) (((x) & 0x7) << 24) +#define DSI_PAD_CONTROL_PAD_SLEWDNADJ(x) (((x) & 0x7) << 20) +#define DSI_PAD_CONTROL_PAD_PREEMP_EN(x) (((x) & 0x1) << 19) +#define DSI_PAD_CONTROL_PAD_PDIO_CLK(x) (((x) & 0x1) << 18) +#define DSI_PAD_CONTROL_PAD_PDIO(x) (((x) & 0x3) << 16) +#define DSI_PAD_CONTROL_PAD_LPUPADJ(x) (((x) & 0x3) << 14) +#define DSI_PAD_CONTROL_PAD_LPDNADJ(x) (((x) & 0x3) << 12) + +#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0) +#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16) + +#define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0) +#define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4) +#define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8) +#define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12) +#define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16) + +#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12) +#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8) +#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4) +#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0) + +#define DSI_GANGED_MODE_CONTROL_ENABLE BIT(0) + +/* + * pixel format as used in the DSI_CONTROL_FORMAT field + */ +enum tegra_dsi_format { + TEGRA_DSI_FORMAT_16P, + TEGRA_DSI_FORMAT_18NP, + TEGRA_DSI_FORMAT_18P, + TEGRA_DSI_FORMAT_24P, +}; + +/* DSI calibration in VI region */ +#define TEGRA_VI_BASE 0x54080000 + +#define CSI_CILA_MIPI_CAL_CONFIG_0 0x22a +#define MIPI_CAL_TERMOSA(x) (((x) & 0x1f) << 0) + +#define CSI_CILB_MIPI_CAL_CONFIG_0 0x22b +#define MIPI_CAL_TERMOSB(x) (((x) & 0x1f) << 0) + +#define CSI_CIL_PAD_CONFIG 0x229 +#define PAD_CIL_PDVREG(x) (((x) & 0x01) << 1) + +#define CSI_DSI_MIPI_CAL_CONFIG 0x234 +#define MIPI_CAL_HSPDOSD(x) (((x) & 0x1f) << 16) +#define MIPI_CAL_HSPUOSD(x) (((x) & 0x1f) << 8) + +#define CSI_MIPIBIAS_PAD_CONFIG 0x235 +#define PAD_DRIV_DN_REF(x) (((x) & 0x7) << 16) +#define PAD_DRIV_UP_REF(x) (((x) & 0x7) << 8) + +#endif /* _TEGRA_DSI_H */ diff --git a/drivers/video/tegra/tegra-hdmi.c b/drivers/video/tegra/tegra-hdmi.c new file mode 100644 index 00000000000..bda69919d92 --- /dev/null +++ b/drivers/video/tegra/tegra-hdmi.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (c) 2023 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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/tegra/tegra-hdmi.h b/drivers/video/tegra/tegra-hdmi.h new file mode 100644 index 00000000000..d17655973e3 --- /dev/null +++ b/drivers/video/tegra/tegra-hdmi.h @@ -0,0 +1,648 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation + */ + +#ifndef _TEGRA_HDMI_H +#define _TEGRA_HDMI_H + +#ifndef __ASSEMBLY__ +#include +#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/tegra/tegra-host1x.c b/drivers/video/tegra/tegra-host1x.c new file mode 100644 index 00000000000..58ab871a3b4 --- /dev/null +++ b/drivers/video/tegra/tegra-host1x.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include + +#include +#include + +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/tegra/tegra-mipi.c b/drivers/video/tegra/tegra-mipi.c new file mode 100644 index 00000000000..a4f4343d008 --- /dev/null +++ b/drivers/video/tegra/tegra-mipi.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (c) 2023 Svyatoslav Ryhel + */ + +#include +#include +#include +#include +#include + +#include +#include + +/* MIPI control registers 0x00 ~ 0x74 */ +struct mipi_ctlr { + uint mipi_cal_ctrl; + uint mipi_cal_autocal_ctrl; + uint mipi_cal_status; + + uint unused1[2]; + + uint mipi_cal_config_csia; + uint mipi_cal_config_csib; + uint mipi_cal_config_csic; + uint mipi_cal_config_csid; + uint mipi_cal_config_csie; + + uint unused2[4]; + + uint mipi_cal_config_dsia; + uint mipi_cal_config_dsib; + uint mipi_cal_config_dsic; + uint mipi_cal_config_dsid; + + uint unused3[4]; + + uint mipi_cal_bias_pad_cfg0; + uint mipi_cal_bias_pad_cfg1; + uint mipi_cal_bias_pad_cfg2; + + uint mipi_cal_dsia_config_2; + uint mipi_cal_dsib_config_2; + uint mipi_cal_cilc_config_2; + uint mipi_cal_cild_config_2; + uint mipi_cal_csie_config_2; +}; + +#define MIPI_DSIA_PADS 0x60 +#define MIPI_DSIB_PADS 0x180 + +#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) +#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) +#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4) +#define MIPI_CAL_CTRL_START BIT(0) + +#define MIPI_CAL_STATUS_DONE BIT(16) +#define MIPI_CAL_STATUS_ACTIVE BIT(0) + +#define MIPI_CAL_OVERIDE(x) (((x) & 0x1) << 30) +#define MIPI_CAL_SEL(x) (((x) & 0x1) << 21) +#define MIPI_CAL_HSPDOS(x) (((x) & 0x1f) << 16) +#define MIPI_CAL_HSPUOS(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_TERMOS(x) (((x) & 0x1f) << 0) + +#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1) +#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0) + +#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16) +#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8) + +#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16) +#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) +#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) + +#define MIPI_CAL_HSCLKPDOSDSI(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_HSCLKPUOSDSI(x) (((x) & 0x1f) << 0) + +struct tegra_mipi_priv { + struct mipi_ctlr *mipi; + struct clk *mipi_cal; + u32 version; +}; + +enum { + T114, + T124, +}; + +static void tegra114_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) +{ + u32 value; + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) | + MIPI_CAL_TERMOS(0x5); + writel(value, &priv->mipi->mipi_cal_config_dsia); + writel(value, &priv->mipi->mipi_cal_config_dsib); + + /* Deselect PAD C */ + value = readl(&priv->mipi->mipi_cal_config_dsic); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_config_dsic); + + /* Deselect PAD D */ + value = readl(&priv->mipi->mipi_cal_config_dsid); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_config_dsid); +} + +static void tegra124_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) +{ + u32 value; + + /* Calibrate DSI-A */ + if (calibration_pads == MIPI_DSIA_PADS) { + printf("Calibrating DSI-A pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_dsia); + writel(value, &priv->mipi->mipi_cal_config_dsib); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + + /* Deselect PAD C */ + value = readl(&priv->mipi->mipi_cal_cilc_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + + /* Deselect PAD D */ + value = readl(&priv->mipi->mipi_cal_cild_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + } + + /* Calibrate DSI-B */ + if (calibration_pads == MIPI_DSIB_PADS) { + printf("Calibrating DSI-B pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_csic); + writel(value, &priv->mipi->mipi_cal_config_csid); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + + /* Deselect PAD A */ + value = readl(&priv->mipi->mipi_cal_dsia_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + + /* Deselect PAD B */ + value = readl(&priv->mipi->mipi_cal_dsib_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + } +} + +static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, + int size) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + u32 value; + + value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | + MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); + value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); + value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); + + switch (priv->version) { + case T114: + tegra114_mipi_pads_cal(priv, offset); + break; + + case T124: + tegra124_mipi_pads_cal(priv, offset); + break; + + default: + return -EINVAL; + } + + value = readl(&priv->mipi->mipi_cal_ctrl); + value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); + value &= ~MIPI_CAL_CTRL_PRESCALE(0x3); + value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa) | + MIPI_CAL_CTRL_PRESCALE(0x2) | + MIPI_CAL_CTRL_CLKEN_OVR; + writel(value, &priv->mipi->mipi_cal_ctrl); + + /* clear any pending status bits */ + value = readl(&priv->mipi->mipi_cal_status); + writel(value, &priv->mipi->mipi_cal_status); + + value = readl(&priv->mipi->mipi_cal_ctrl); + value |= MIPI_CAL_CTRL_START; + writel(value, &priv->mipi->mipi_cal_ctrl); + + /* + * Wait for min 72uS to let calibration logic finish calibration + * sequence codes before waiting for pads idle state to apply the + * results. + */ + udelay(80); + + return readl_poll_sleep_timeout(&priv->mipi->mipi_cal_status, value, + !(value & MIPI_CAL_STATUS_ACTIVE) && + (value & MIPI_CAL_STATUS_DONE), 100, + 250000); +} + +static int tegra_mipi_enable(struct udevice *dev, bool val) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + u32 value; + + reset_set_enable(priv->mipi_cal->id, 1); + mdelay(100); + reset_set_enable(priv->mipi_cal->id, 0); + mdelay(1); + + clk_enable(priv->mipi_cal); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0); + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg0); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); + value &= ~MIPI_CAL_BIAS_PAD_PDVREG; + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); + + return 0; +} + +static const struct misc_ops tegra_mipi_ops = { + .write = tegra_mipi_calibrate, + .set_enabled = tegra_mipi_enable, +}; + +static int tegra_mipi_probe(struct udevice *dev) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + + priv->version = dev_get_driver_data(dev); + + priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev); + if (!priv->mipi) { + log_debug("%s: no MIPI controller address\n", __func__); + return -EINVAL; + } + + priv->mipi_cal = devm_clk_get(dev, NULL); + if (IS_ERR(priv->mipi_cal)) { + log_debug("%s: Could not get MIPI clock: %ld\n", + __func__, PTR_ERR(priv->mipi_cal)); + return PTR_ERR(priv->mipi_cal); + } + + return 0; +} + +static const struct udevice_id tegra_mipi_ids[] = { + { .compatible = "nvidia,tegra114-mipi", .data = T114 }, + { .compatible = "nvidia,tegra124-mipi", .data = T124 }, + { } +}; + +U_BOOT_DRIVER(tegra_mipi) = { + .name = "tegra_mipi", + .id = UCLASS_MISC, + .ops = &tegra_mipi_ops, + .of_match = tegra_mipi_ids, + .probe = tegra_mipi_probe, + .priv_auto = sizeof(struct tegra_mipi_priv), +}; diff --git a/drivers/video/tegra/tegra-pwm-backlight.c b/drivers/video/tegra/tegra-pwm-backlight.c new file mode 100644 index 00000000000..998f0df1991 --- /dev/null +++ b/drivers/video/tegra/tegra-pwm-backlight.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2022 Svyatoslav Ryhel + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tegra-dc.h" + +#define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10 +#define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF + +#define TEGRA_PWM_BL_PERIOD 0xFF +#define TEGRA_PWM_BL_CLK_DIV 0x14 +#define TEGRA_PWM_BL_CLK_SELECT 0x00 + +#define PM_PERIOD_SHIFT 18 +#define PM_CLK_DIVIDER_SHIFT 4 + +#define TEGRA_PWM_PM0 0 +#define TEGRA_PWM_PM1 1 + +struct tegra_pwm_backlight_priv { + struct dc_ctlr *dc; /* Display controller regmap */ + + u32 pwm_source; + u32 period; + u32 clk_div; + u32 clk_select; + u32 dft_brightness; +}; + +static int tegra_pwm_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + struct dc_cmd_reg *cmd = &priv->dc->cmd; + struct dc_com_reg *com = &priv->dc->com; + unsigned int ctrl; + unsigned long out_sel; + unsigned long cmd_state; + + if (percent == BACKLIGHT_DEFAULT) + percent = priv->dft_brightness; + + if (percent < TEGRA_PWM_BL_MIN_BRIGHTNESS) + percent = TEGRA_PWM_BL_MIN_BRIGHTNESS; + + if (percent > TEGRA_PWM_BL_MAX_BRIGHTNESS) + percent = TEGRA_PWM_BL_MAX_BRIGHTNESS; + + ctrl = ((priv->period << PM_PERIOD_SHIFT) | + (priv->clk_div << PM_CLK_DIVIDER_SHIFT) | + priv->clk_select); + + /* The new value should be effected immediately */ + cmd_state = readl(&cmd->state_access); + writel((cmd_state | (1 << 2)), &cmd->state_access); + + switch (priv->pwm_source) { + case TEGRA_PWM_PM0: + /* Select the LM0 on PM0 */ + out_sel = readl(&com->pin_output_sel[5]); + out_sel &= ~(7 << 0); + out_sel |= (3 << 0); + writel(out_sel, &com->pin_output_sel[5]); + writel(ctrl, &com->pm0_ctrl); + writel(percent, &com->pm0_duty_cycle); + break; + case TEGRA_PWM_PM1: + /* Select the LM1 on PM1 */ + out_sel = readl(&com->pin_output_sel[5]); + out_sel &= ~(7 << 4); + out_sel |= (3 << 4); + writel(out_sel, &com->pin_output_sel[5]); + writel(ctrl, &com->pm1_ctrl); + writel(percent, &com->pm1_duty_cycle); + break; + default: + break; + } + + writel(cmd_state, &cmd->state_access); + return 0; +} + +static int tegra_pwm_backlight_enable(struct udevice *dev) +{ + struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + + return tegra_pwm_backlight_set_brightness(dev, priv->dft_brightness); +} + +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)); + + priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc); + if (!priv->dc) { + log_err("%s: failed to get DC controller\n", __func__); + return -EINVAL; + } + + priv->pwm_source = + dev_read_u32_default(dev, "nvidia,pwm-source", + TEGRA_PWM_PM0); + priv->period = + dev_read_u32_default(dev, "nvidia,period", + TEGRA_PWM_BL_PERIOD); + priv->clk_div = + dev_read_u32_default(dev, "nvidia,clock-div", + TEGRA_PWM_BL_CLK_DIV); + priv->clk_select = + dev_read_u32_default(dev, "nvidia,clock-select", + TEGRA_PWM_BL_CLK_SELECT); + priv->dft_brightness = + dev_read_u32_default(dev, "nvidia,default-brightness", + TEGRA_PWM_BL_MAX_BRIGHTNESS); + + return 0; +} + +static const struct backlight_ops tegra_pwm_backlight_ops = { + .enable = tegra_pwm_backlight_enable, + .set_brightness = tegra_pwm_backlight_set_brightness, +}; + +static const struct udevice_id tegra_pwm_backlight_ids[] = { + { .compatible = "nvidia,tegra-pwm-backlight" }, + { } +}; + +U_BOOT_DRIVER(tegra_pwm_backlight) = { + .name = "tegra_pwm_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = tegra_pwm_backlight_ids, + .probe = tegra_pwm_backlight_probe, + .ops = &tegra_pwm_backlight_ops, + .priv_auto = sizeof(struct tegra_pwm_backlight_priv), +}; diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig deleted file mode 100644 index 598f9ea1f21..00000000000 --- a/drivers/video/tegra20/Kconfig +++ /dev/null @@ -1,38 +0,0 @@ -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. - This option enables this support which can be used on devices which - have an LCD display connected. - -config VIDEO_DSI_TEGRA30 - bool "Enable Tegra 30 DSI support" - 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 - select VIDEO_TEGRA20 - help - Tegra DC dependent backlight. diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile deleted file mode 100644 index 78521405749..00000000000 --- a/drivers/video/tegra20/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# 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/mipi-phy.c b/drivers/video/tegra20/mipi-phy.c deleted file mode 100644 index 576262e405d..00000000000 --- a/drivers/video/tegra20/mipi-phy.c +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 NVIDIA Corporation - */ - -#include - -#include "mipi-phy.h" - -/* - * Default D-PHY timings based on MIPI D-PHY specification. Derived from the - * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY - * specification (v1.2) with minor adjustments. - */ -int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, - unsigned long period) -{ - timing->clkmiss = 0; - timing->clkpost = 70 + 52 * period; - timing->clkpre = 8; - timing->clkprepare = 65; - timing->clksettle = 95; - timing->clktermen = 0; - timing->clktrail = 80; - timing->clkzero = 260; - timing->dtermen = 0; - timing->eot = 0; - timing->hsexit = 120; - timing->hsprepare = 65 + 5 * period; - timing->hszero = 145 + 5 * period; - timing->hssettle = 85 + 6 * period; - timing->hsskip = 40; - - /* - * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40) - * contains this formula as: - * - * T_HS-TRAIL = max(n * 8 * period, 60 + n * 4 * period) - * - * where n = 1 for forward-direction HS mode and n = 4 for reverse- - * direction HS mode. There's only one setting and this function does - * not parameterize on anything other that period, so this code will - * assumes that reverse-direction HS mode is supported and uses n = 4. - */ - timing->hstrail = max(4 * 8 * period, 60 + 4 * 4 * period); - - timing->init = 100000; - timing->lpx = 60; - timing->taget = 5 * timing->lpx; - timing->tago = 4 * timing->lpx; - timing->tasure = 2 * timing->lpx; - timing->wakeup = 1000000; - - return 0; -} - -/* - * Validate D-PHY timing according to MIPI D-PHY specification - * (v1.2, Section 6.9 "Global Operation Timing Parameters"). - */ -int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, - unsigned long period) -{ - if (timing->clkmiss > 60) - return -EINVAL; - - if (timing->clkpost < (60 + 52 * period)) - return -EINVAL; - - if (timing->clkpre < 8) - return -EINVAL; - - if (timing->clkprepare < 38 || timing->clkprepare > 95) - return -EINVAL; - - if (timing->clksettle < 95 || timing->clksettle > 300) - return -EINVAL; - - if (timing->clktermen > 38) - return -EINVAL; - - if (timing->clktrail < 60) - return -EINVAL; - - if (timing->clkprepare + timing->clkzero < 300) - return -EINVAL; - - if (timing->dtermen > 35 + 4 * period) - return -EINVAL; - - if (timing->eot > 105 + 12 * period) - return -EINVAL; - - if (timing->hsexit < 100) - return -EINVAL; - - if (timing->hsprepare < 40 + 4 * period || - timing->hsprepare > 85 + 6 * period) - return -EINVAL; - - if (timing->hsprepare + timing->hszero < 145 + 10 * period) - return -EINVAL; - - if ((timing->hssettle < 85 + 6 * period) || - (timing->hssettle > 145 + 10 * period)) - return -EINVAL; - - if (timing->hsskip < 40 || timing->hsskip > 55 + 4 * period) - return -EINVAL; - - if (timing->hstrail < max(8 * period, 60 + 4 * period)) - return -EINVAL; - - if (timing->init < 100000) - return -EINVAL; - - if (timing->lpx < 50) - return -EINVAL; - - if (timing->taget != 5 * timing->lpx) - return -EINVAL; - - if (timing->tago != 4 * timing->lpx) - return -EINVAL; - - if (timing->tasure < timing->lpx || timing->tasure > 2 * timing->lpx) - return -EINVAL; - - if (timing->wakeup < 1000000) - return -EINVAL; - - return 0; -} diff --git a/drivers/video/tegra20/mipi-phy.h b/drivers/video/tegra20/mipi-phy.h deleted file mode 100644 index 41889a75035..00000000000 --- a/drivers/video/tegra20/mipi-phy.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2013 NVIDIA Corporation - */ - -#ifndef DRM_TEGRA_MIPI_PHY_H -#define DRM_TEGRA_MIPI_PHY_H - -/* - * D-PHY timing parameters - * - * A detailed description of these parameters can be found in the MIPI - * Alliance Specification for D-PHY, Section 5.9 "Global Operation Timing - * Parameters". - * - * All parameters are specified in nanoseconds. - */ -struct mipi_dphy_timing { - unsigned int clkmiss; - unsigned int clkpost; - unsigned int clkpre; - unsigned int clkprepare; - unsigned int clksettle; - unsigned int clktermen; - unsigned int clktrail; - unsigned int clkzero; - unsigned int dtermen; - unsigned int eot; - unsigned int hsexit; - unsigned int hsprepare; - unsigned int hszero; - unsigned int hssettle; - unsigned int hsskip; - unsigned int hstrail; - unsigned int init; - unsigned int lpx; - unsigned int taget; - unsigned int tago; - unsigned int tasure; - unsigned int wakeup; -}; - -int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, - unsigned long period); -int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, - unsigned long period); - -#endif diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c deleted file mode 100644 index 1f43153ff27..00000000000 --- a/drivers/video/tegra20/tegra-dc.c +++ /dev/null @@ -1,682 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2011 The Chromium OS Authors. - * Copyright (c) 2024 Svyatoslav Ryhel - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tegra-dc.h" - -/* Holder of Tegra per-SOC DC differences */ -struct tegra_dc_soc_info { - bool has_timer; - bool has_rgb; - bool has_pgate; -}; - -/* Information about the display controller */ -struct tegra_lcd_priv { - int width; /* width in pixels */ - int height; /* height in pixels */ - enum video_log2_bpp log2_bpp; /* colour depth */ - struct display_timing timing; - 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 */ - unsigned pixel_clock; /* Pixel clock in Hz */ - struct clk *clk; - struct clk *clk_parent; - ulong scdiv; /* Clock divider used by disp_clk_ctrl */ - bool rotation; /* 180 degree panel turn */ - int pipe; /* DC controller: 0 for A, 1 for B */ -}; - -enum { - /* Maximum LCD size we support */ - LCD_MAX_WIDTH = 2560, - LCD_MAX_HEIGHT = 1600, - LCD_MAX_LOG2_BPP = VIDEO_BPP16, -}; - -static void update_window(struct tegra_lcd_priv *priv, - struct disp_ctl_win *win) -{ - struct dc_ctlr *dc = priv->dc; - unsigned h_dda, v_dda; - unsigned long val; - - val = readl(&dc->cmd.disp_win_header); - val |= WINDOW_A_SELECT; - writel(val, &dc->cmd.disp_win_header); - - writel(win->fmt, &dc->win.color_depth); - - clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, - BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); - - val = win->out_x << H_POSITION_SHIFT; - val |= win->out_y << V_POSITION_SHIFT; - writel(val, &dc->win.pos); - - val = win->out_w << H_SIZE_SHIFT; - val |= win->out_h << V_SIZE_SHIFT; - writel(val, &dc->win.size); - - val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; - val |= win->h << V_PRESCALED_SIZE_SHIFT; - writel(val, &dc->win.prescaled_size); - - writel(0, &dc->win.h_initial_dda); - writel(0, &dc->win.v_initial_dda); - - h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); - v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); - - val = h_dda << H_DDA_INC_SHIFT; - val |= v_dda << V_DDA_INC_SHIFT; - writel(val, &dc->win.dda_increment); - - writel(win->stride, &dc->win.line_stride); - writel(0, &dc->win.buf_stride); - - val = WIN_ENABLE; - if (win->bpp < 24) - val |= COLOR_EXPAND; - - if (priv->rotation) - val |= H_DIRECTION | V_DIRECTION; - - writel(val, &dc->win.win_opt); - - writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); - writel(win->x, &dc->winbuf.addr_h_offset); - writel(win->y, &dc->winbuf.addr_v_offset); - - writel(0xff00, &dc->win.blend_nokey); - writel(0xff00, &dc->win.blend_1win); - - val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; - val |= GENERAL_UPDATE | WIN_A_UPDATE; - writel(val, &dc->cmd.state_ctrl); -} - -static int update_display_mode(struct tegra_lcd_priv *priv) -{ - struct dc_disp_reg *disp = &priv->dc->disp; - struct display_timing *dt = &priv->timing; - unsigned long val; - - writel(0x0, &disp->disp_timing_opt); - - writel(1 | 1 << 16, &disp->ref_to_sync); - writel(dt->hsync_len.typ | dt->vsync_len.typ << 16, &disp->sync_width); - writel(dt->hback_porch.typ | dt->vback_porch.typ << 16, - &disp->back_porch); - writel((dt->hfront_porch.typ - 1) | (dt->vfront_porch.typ - 1) << 16, - &disp->front_porch); - writel(dt->hactive.typ | (dt->vactive.typ << 16), &disp->disp_active); - - if (priv->soc->has_rgb) { - val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; - val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; - writel(val, &disp->data_enable_opt); - - val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; - val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; - val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; - writel(val, &disp->disp_interface_ctrl); - - writel(0x00010001, &disp->shift_clk_opt); - } - - val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; - val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT; - writel(val, &disp->disp_clk_ctrl); - - return 0; -} - -/* Start up the display and turn on power to PWMs */ -static void basic_init(struct dc_cmd_reg *cmd) -{ - u32 val; - - writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); - writel(0x0000011a, &cmd->cont_syncpt_vsync); - writel(0x00000000, &cmd->int_type); - writel(0x00000000, &cmd->int_polarity); - writel(0x00000000, &cmd->int_mask); - writel(0x00000000, &cmd->int_enb); - - val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; - val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; - val |= PM1_ENABLE; - writel(val, &cmd->disp_pow_ctrl); - - val = readl(&cmd->disp_cmd); - val &= ~CTRL_MODE_MASK; - val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; - writel(val, &cmd->disp_cmd); -} - -static void basic_init_timer(struct dc_disp_reg *disp) -{ - writel(0x00000020, &disp->mem_high_pri); - writel(0x00000001, &disp->mem_high_pri_timer); -} - -static const u32 rgb_enb_tab[PIN_REG_COUNT] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { - 0x00000000, - 0x01000000, - 0x00000000, - 0x00000000, -}; - -static const u32 rgb_data_tab[PIN_REG_COUNT] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00210222, - 0x00002200, - 0x00020000, -}; - -static void rgb_enable(struct tegra_lcd_priv *priv) -{ - struct dc_com_reg *com = &priv->dc->com; - struct display_timing *dt = &priv->timing; - u32 value; - int i; - - for (i = 0; i < PIN_REG_COUNT; i++) { - writel(rgb_enb_tab[i], &com->pin_output_enb[i]); - writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); - writel(rgb_data_tab[i], &com->pin_output_data[i]); - } - - /* configure H- and V-sync signal polarities */ - value = readl(&com->pin_output_polarity[1]); - - if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW) - value |= LHS_OUTPUT_POLARITY_LOW; - else - value &= ~LHS_OUTPUT_POLARITY_LOW; - - if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW) - value |= LVS_OUTPUT_POLARITY_LOW; - else - value &= ~LVS_OUTPUT_POLARITY_LOW; - - writel(value, &com->pin_output_polarity[1]); - - for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) - writel(rgb_sel_tab[i], &com->pin_output_sel[i]); -} - -static int setup_window(struct tegra_lcd_priv *priv, - struct disp_ctl_win *win) -{ - if (priv->rotation) { - win->x = priv->width * 2 - 1; - win->y = priv->height - 1; - } else { - win->x = 0; - win->y = 0; - } - - win->w = priv->width; - win->h = priv->height; - win->out_x = 0; - win->out_y = 0; - win->out_w = priv->width; - win->out_h = priv->height; - win->phys_addr = priv->frame_buffer; - win->stride = priv->width * (1 << priv->log2_bpp) / 8; - - log_debug("%s: depth = %d\n", __func__, priv->log2_bpp); - - switch (priv->log2_bpp) { - case VIDEO_BPP32: - win->fmt = COLOR_DEPTH_R8G8B8A8; - win->bpp = 32; - break; - case VIDEO_BPP16: - win->fmt = COLOR_DEPTH_B5G6R5; - win->bpp = 16; - break; - - default: - log_debug("Unsupported LCD bit depth\n"); - return -1; - } - - return 0; -} - -/** - * Register a new display based on device tree configuration. - * - * The frame buffer can be positioned by U-Boot or overridden by the fdt. - * You should pass in the U-Boot address here, and check the contents of - * struct tegra_lcd_priv to see what was actually chosen. - * - * @param priv Driver's private data - * @param default_lcd_base Default address of LCD frame buffer - * Return: 0 if ok, -1 on error (unsupported bits per pixel) - */ -static int tegra_display_probe(struct tegra_lcd_priv *priv, - void *default_lcd_base) -{ - struct disp_ctl_win window; - unsigned long rate = clk_get_rate(priv->clk_parent); - int ret; - - priv->frame_buffer = (u32)default_lcd_base; - - /* - * We halve the rate if DISP1 parent is PLLD, since actual parent - * is plld_out0 which is PLLD divided by 2. - */ - if (priv->clk_parent->id == CLOCK_ID_DISPLAY || - priv->clk_parent->id == CLOCK_ID_DISPLAY2) - rate /= 2; - - /* - * The pixel clock divider is in 7.1 format (where the bottom bit - * represents 0.5). Here we calculate the divider needed to get from - * the display clock (typically 600MHz) to the pixel clock. We round - * up or down as required. - */ - if (!priv->scdiv) - priv->scdiv = ((rate * 2 + priv->pixel_clock / 2) - / priv->pixel_clock) - 2; - log_debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); - - clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, - rate); - - basic_init(&priv->dc->cmd); - - if (priv->soc->has_timer) - basic_init_timer(&priv->dc->disp); - - if (priv->soc->has_rgb) - rgb_enable(priv); - - if (priv->pixel_clock) - update_display_mode(priv); - - ret = setup_window(priv, &window); - if (ret) - return ret; - - update_window(priv, &window); - - return 0; -} - -static int tegra_lcd_probe(struct udevice *dev) -{ - struct video_uc_plat *plat = dev_get_uclass_plat(dev); - struct video_priv *uc_priv = dev_get_uclass_priv(dev); - struct tegra_lcd_priv *priv = dev_get_priv(dev); - int ret; - - /* Initialize the Tegra display controller */ - if (priv->soc->has_pgate) { - uint powergate; - - if (priv->pipe) - powergate = TEGRA_POWERGATE_DISB; - else - powergate = TEGRA_POWERGATE_DIS; - - ret = tegra_powergate_power_off(powergate); - if (ret < 0) { - log_debug("failed to power off DISP gate: %d", ret); - return ret; - } - - ret = tegra_powergate_sequence_power_up(powergate, - priv->clk->id); - if (ret < 0) { - log_debug("failed to power up DISP gate: %d", ret); - return ret; - } - } - - /* Get shift clock divider from Tegra DSI if used */ - 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; - } - } - - /* Clean the framebuffer area */ - memset((u8 *)plat->base, 0, plat->size); - flush_dcache_all(); - - ret = tegra_display_probe(priv, (void *)plat->base); - if (ret) { - log_debug("%s: Failed to probe display driver\n", __func__); - 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, - DCACHE_WRITETHROUGH); - - /* Enable flushing after LCD writes if requested */ - video_set_flush_dcache(dev, true); - - uc_priv->xsize = priv->width; - uc_priv->ysize = priv->height; - uc_priv->bpix = priv->log2_bpp; - log_debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, - plat->size); - - 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); - struct display_timing *timing; - ofnode rgb; - int ret; - - priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev); - if (!priv->dc) { - log_debug("%s: No display controller address\n", __func__); - return -EINVAL; - } - - priv->soc = (struct tegra_dc_soc_info *)dev_get_driver_data(dev); - - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) { - log_debug("%s: Could not get DC clock: %ld\n", - __func__, PTR_ERR(priv->clk)); - return PTR_ERR(priv->clk); - } - - priv->clk_parent = devm_clk_get(dev, "parent"); - if (IS_ERR(priv->clk_parent)) { - log_debug("%s: Could not get DC clock parent: %ld\n", - __func__, PTR_ERR(priv->clk_parent)); - return PTR_ERR(priv->clk_parent); - } - - priv->rotation = dev_read_bool(dev, "nvidia,180-rotation"); - priv->pipe = dev_read_u32_default(dev, "nvidia,head", 0); - - /* - * 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. - */ - - 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; - } - - 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; - } - } - } - - 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); - return -EINVAL; - } - } - - timing = &priv->timing; - priv->width = timing->hactive.typ; - priv->height = timing->vactive.typ; - priv->pixel_clock = timing->pixelclock.typ; - priv->log2_bpp = VIDEO_BPP16; - - return 0; -} - -static int tegra_lcd_bind(struct udevice *dev) -{ - struct video_uc_plat *plat = dev_get_uclass_plat(dev); - - plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * - (1 << LCD_MAX_LOG2_BPP) / 8; - - return dm_scan_fdt_dev(dev); -} - -static const struct tegra_dc_soc_info tegra20_dc_soc_info = { - .has_timer = true, - .has_rgb = true, - .has_pgate = false, -}; - -static const struct tegra_dc_soc_info tegra30_dc_soc_info = { - .has_timer = false, - .has_rgb = true, - .has_pgate = false, -}; - -static const struct tegra_dc_soc_info tegra114_dc_soc_info = { - .has_timer = false, - .has_rgb = false, - .has_pgate = true, -}; - -static const struct udevice_id tegra_lcd_ids[] = { - { - .compatible = "nvidia,tegra20-dc", - .data = (ulong)&tegra20_dc_soc_info - }, { - .compatible = "nvidia,tegra30-dc", - .data = (ulong)&tegra30_dc_soc_info - }, { - .compatible = "nvidia,tegra114-dc", - .data = (ulong)&tegra114_dc_soc_info - }, { - .compatible = "nvidia,tegra124-dc", - .data = (ulong)&tegra114_dc_soc_info - }, { - /* sentinel */ - } -}; - -U_BOOT_DRIVER(tegra_lcd) = { - .name = "tegra_lcd", - .id = UCLASS_VIDEO, - .of_match = tegra_lcd_ids, - .bind = tegra_lcd_bind, - .probe = tegra_lcd_probe, - .of_to_plat = tegra_lcd_of_to_plat, - .priv_auto = sizeof(struct tegra_lcd_priv), -}; diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra20/tegra-dc.h deleted file mode 100644 index 2a4013b3355..00000000000 --- a/drivers/video/tegra20/tegra-dc.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2010 - * NVIDIA Corporation - */ - -#ifndef _TEGRA_DC_H -#define _TEGRA_DC_H - -#ifndef __ASSEMBLY__ -#include -#endif - -/* arch-tegra/dc exists only because T124 uses it */ -#include - -struct tegra_dc_plat { - struct udevice *dev; /* Display controller device */ - struct dc_ctlr *dc; /* Display controller regmap */ - int pipe; /* DC number: 0 for A, 1 for B */ - ulong scdiv; /* Shift clock divider */ -}; - -/* This holds information about a window which can be displayed */ -struct disp_ctl_win { - enum win_color_depth_id fmt; /* Color depth/format */ - unsigned int bpp; /* Bits per pixel */ - phys_addr_t phys_addr; /* Physical address in memory */ - unsigned int x; /* Horizontal address offset (bytes) */ - unsigned int y; /* Veritical address offset (bytes) */ - unsigned int w; /* Width of source window */ - unsigned int h; /* Height of source window */ - unsigned int stride; /* Number of bytes per line */ - unsigned int out_x; /* Left edge of output window (col) */ - unsigned int out_y; /* Top edge of output window (row) */ - unsigned int out_w; /* Width of output window in pixels */ - unsigned int out_h; /* Height of output window in pixels */ -}; - -#endif /* _TEGRA_DC_H */ diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c deleted file mode 100644 index a96fba01ee4..00000000000 --- a/drivers/video/tegra20/tegra-dsi.c +++ /dev/null @@ -1,1148 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2013 NVIDIA Corporation - * Copyright (c) 2022 Svyatoslav Ryhel - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "tegra-dc.h" -#include "tegra-dsi.h" -#include "mipi-phy.h" - -/* List of supported DSI bridges */ -enum { - DSI_V0, - DSI_V1, -}; - -struct tegra_dsi_priv { - struct mipi_dsi_host host; - struct mipi_dsi_device device; - struct mipi_dphy_timing dphy_timing; - - struct udevice *panel; - struct udevice *mipi; - struct display_timing timing; - - struct dsi_ctlr *dsi; - struct udevice *avdd; - - enum tegra_dsi_format format; - - struct clk *clk; - struct clk *clk_parent; - - int video_fifo_depth; - int host_fifo_depth; - - u32 calibration_pads; - u32 version; - - /* for ganged-mode support */ - struct udevice *master; - struct udevice *slave; -}; - -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 |= DSI_ENABLE; - writel(value, &dc->disp.disp_win_opt); - - writel(GENERAL_UPDATE, &dc->cmd.state_ctrl); - writel(GENERAL_ACT_REQ, &dc->cmd.state_ctrl); -} - -static const char * const error_report[16] = { - "SoT Error", - "SoT Sync Error", - "EoT Sync Error", - "Escape Mode Entry Command Error", - "Low-Power Transmit Sync Error", - "Peripheral Timeout Error", - "False Control Error", - "Contention Detected", - "ECC Error, single-bit", - "ECC Error, multi-bit", - "Checksum Error", - "DSI Data Type Not Recognized", - "DSI VC ID Invalid", - "Invalid Transmission Length", - "Reserved", - "DSI Protocol Violation", -}; - -static ssize_t tegra_dsi_read_response(struct dsi_misc_reg *misc, - const struct mipi_dsi_msg *msg, - size_t count) -{ - u8 *rx = msg->rx_buf; - unsigned int i, j, k; - size_t size = 0; - u16 errors; - u32 value; - - /* read and parse packet header */ - value = readl(&misc->dsi_rd_data); - - switch (value & 0x3f) { - case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: - errors = (value >> 8) & 0xffff; - printf("%s: Acknowledge and error report: %04x\n", - __func__, errors); - for (i = 0; i < ARRAY_SIZE(error_report); i++) - if (errors & BIT(i)) - printf("%s: %2u: %s\n", __func__, i, - error_report[i]); - break; - - case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: - rx[0] = (value >> 8) & 0xff; - size = 1; - break; - - case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: - rx[0] = (value >> 8) & 0xff; - rx[1] = (value >> 16) & 0xff; - size = 2; - break; - - case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: - size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); - break; - - case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: - size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); - break; - - default: - printf("%s: unhandled response type: %02x\n", - __func__, value & 0x3f); - return -EPROTO; - } - - size = min(size, msg->rx_len); - - if (msg->rx_buf && size > 0) { - for (i = 0, j = 0; i < count - 1; i++, j += 4) { - u8 *rx = msg->rx_buf + j; - - value = readl(&misc->dsi_rd_data); - - for (k = 0; k < 4 && (j + k) < msg->rx_len; k++) - rx[j + k] = (value >> (k << 3)) & 0xff; - } - } - - return size; -} - -static int tegra_dsi_transmit(struct dsi_misc_reg *misc, - unsigned long timeout) -{ - writel(DSI_TRIGGER_HOST, &misc->dsi_trigger); - - while (timeout--) { - u32 value = readl(&misc->dsi_trigger); - - if ((value & DSI_TRIGGER_HOST) == 0) - return 0; - - udelay(1000); - } - - debug("timeout waiting for transmission to complete\n"); - return -ETIMEDOUT; -} - -static int tegra_dsi_wait_for_response(struct dsi_misc_reg *misc, - unsigned long timeout) -{ - while (timeout--) { - u32 value = readl(&misc->dsi_status); - u8 count = value & 0x1f; - - if (count > 0) - return count; - - udelay(1000); - } - - debug("peripheral returned no data\n"); - return -ETIMEDOUT; -} - -static void tegra_dsi_writesl(struct dsi_misc_reg *misc, - const void *buffer, size_t size) -{ - const u8 *buf = buffer; - size_t i, j; - u32 value; - - for (j = 0; j < size; j += 4) { - value = 0; - - for (i = 0; i < 4 && j + i < size; i++) - value |= buf[j + i] << (i << 3); - - writel(value, &misc->dsi_wr_data); - } -} - -static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, - const struct mipi_dsi_msg *msg) -{ - struct udevice *dev = (struct udevice *)host->dev; - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct dsi_misc_reg *misc = &priv->dsi->misc; - struct mipi_dsi_packet packet; - const u8 *header; - size_t count; - ssize_t err; - u32 value; - - err = mipi_dsi_create_packet(&packet, msg); - if (err < 0) - return err; - - header = packet.header; - - /* maximum FIFO depth is 1920 words */ - if (packet.size > priv->video_fifo_depth * 4) - return -ENOSPC; - - /* reset underflow/overflow flags */ - value = readl(&misc->dsi_status); - if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) { - value = DSI_HOST_CONTROL_FIFO_RESET; - writel(value, &misc->host_dsi_ctrl); - udelay(10); - } - - value = readl(&misc->dsi_pwr_ctrl); - value |= DSI_POWER_CONTROL_ENABLE; - writel(value, &misc->dsi_pwr_ctrl); - - mdelay(5); - - value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | - DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; - - if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) - value |= DSI_HOST_CONTROL_HS; - - /* - * The host FIFO has a maximum of 64 words, so larger transmissions - * need to use the video FIFO. - */ - if (packet.size > priv->host_fifo_depth * 4) - value |= DSI_HOST_CONTROL_FIFO_SEL; - - writel(value, &misc->host_dsi_ctrl); - - /* - * For reads and messages with explicitly requested ACK, generate a - * BTA sequence after the transmission of the packet. - */ - if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || - (msg->rx_buf && msg->rx_len > 0)) { - value = readl(&misc->host_dsi_ctrl); - value |= DSI_HOST_CONTROL_PKT_BTA; - writel(value, &misc->host_dsi_ctrl); - } - - value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE; - writel(value, &misc->dsi_ctrl); - - /* write packet header, ECC is generated by hardware */ - value = header[2] << 16 | header[1] << 8 | header[0]; - writel(value, &misc->dsi_wr_data); - - /* write payload (if any) */ - if (packet.payload_length > 0) - tegra_dsi_writesl(misc, packet.payload, - packet.payload_length); - - err = tegra_dsi_transmit(misc, 250); - if (err < 0) - return err; - - if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || - (msg->rx_buf && msg->rx_len > 0)) { - err = tegra_dsi_wait_for_response(misc, 250); - if (err < 0) - return err; - - count = err; - - value = readl(&misc->dsi_rd_data); - switch (value) { - case 0x84: - debug("%s: ACK\n", __func__); - break; - - case 0x87: - debug("%s: ESCAPE\n", __func__); - break; - - default: - printf("%s: unknown status: %08x\n", __func__, value); - break; - } - - if (count > 1) { - err = tegra_dsi_read_response(misc, msg, count); - if (err < 0) { - printf("%s: failed to parse response: %zd\n", - __func__, err); - } else { - /* - * For read commands, return the number of - * bytes returned by the peripheral. - */ - count = err; - } - } - } else { - /* - * For write commands, we have transmitted the 4-byte header - * plus the variable-length payload. - */ - count = 4 + packet.payload_length; - } - - return count; -} - -struct mipi_dsi_host_ops tegra_dsi_bridge_host_ops = { - .transfer = tegra_dsi_host_transfer, -}; - -#define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) -#define PKT_LEN0(len) (((len) & 0x07) << 0) -#define PKT_ID1(id) ((((id) & 0x3f) << 13) | (1 << 19)) -#define PKT_LEN1(len) (((len) & 0x07) << 10) -#define PKT_ID2(id) ((((id) & 0x3f) << 23) | (1 << 29)) -#define PKT_LEN2(len) (((len) & 0x07) << 20) - -#define PKT_LP BIT(30) -#define NUM_PKT_SEQ 12 - -/* - * non-burst mode with sync pulses - */ -static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = { - [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | - PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | - PKT_LP, - [ 1] = 0, - [ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | - PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | - PKT_LP, - [ 3] = 0, - [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | - PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | - PKT_LP, - [ 5] = 0, - [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | - PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), - [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | - PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | - PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), - [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | - PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | - PKT_LP, - [ 9] = 0, - [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | - PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), - [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | - PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | - PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), -}; - -/* - * non-burst mode with sync events - */ -static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = { - [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | - PKT_LP, - [ 1] = 0, - [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | - PKT_LP, - [ 3] = 0, - [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | - PKT_LP, - [ 5] = 0, - [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | - PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), - [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), - [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | - PKT_LP, - [ 9] = 0, - [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | - PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | - PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), - [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), -}; - -static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { - [ 0] = 0, - [ 1] = 0, - [ 2] = 0, - [ 3] = 0, - [ 4] = 0, - [ 5] = 0, - [ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP, - [ 7] = 0, - [ 8] = 0, - [ 9] = 0, - [10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP, - [11] = 0, -}; - -static void tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, - unsigned int *mulp, unsigned int *divp) -{ - switch (format) { - case MIPI_DSI_FMT_RGB666_PACKED: - case MIPI_DSI_FMT_RGB888: - *mulp = 3; - *divp = 1; - break; - - case MIPI_DSI_FMT_RGB565: - *mulp = 2; - *divp = 1; - break; - - case MIPI_DSI_FMT_RGB666: - *mulp = 9; - *divp = 4; - break; - - default: - break; - } -} - -static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format, - enum tegra_dsi_format *fmt) -{ - switch (format) { - case MIPI_DSI_FMT_RGB888: - *fmt = TEGRA_DSI_FORMAT_24P; - break; - - case MIPI_DSI_FMT_RGB666: - *fmt = TEGRA_DSI_FORMAT_18NP; - break; - - case MIPI_DSI_FMT_RGB666_PACKED: - *fmt = TEGRA_DSI_FORMAT_18P; - break; - - case MIPI_DSI_FMT_RGB565: - *fmt = TEGRA_DSI_FORMAT_16P; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad) -{ - u32 value; - - /* start calibration */ - value = DSI_PAD_CONTROL_PAD_LPUPADJ(0x1) | - DSI_PAD_CONTROL_PAD_LPDNADJ(0x1) | - DSI_PAD_CONTROL_PAD_PREEMP_EN(0x1) | - DSI_PAD_CONTROL_PAD_SLEWDNADJ(0x6) | - DSI_PAD_CONTROL_PAD_SLEWUPADJ(0x6) | - DSI_PAD_CONTROL_PAD_PDIO(0) | - DSI_PAD_CONTROL_PAD_PDIO_CLK(0) | - DSI_PAD_CONTROL_PAD_PULLDN_ENAB(0); - writel(value, &pad->pad_ctrl); - - clock_enable(PERIPH_ID_VI); - clock_enable(PERIPH_ID_CSI); - udelay(2); - reset_set_enable(PERIPH_ID_VI, 0); - reset_set_enable(PERIPH_ID_CSI, 0); - - value = MIPI_CAL_TERMOSA(0x4); - writel(value, TEGRA_VI_BASE + (CSI_CILA_MIPI_CAL_CONFIG_0 << 2)); - - value = MIPI_CAL_TERMOSB(0x4); - writel(value, TEGRA_VI_BASE + (CSI_CILB_MIPI_CAL_CONFIG_0 << 2)); - - value = MIPI_CAL_HSPUOSD(0x3) | MIPI_CAL_HSPDOSD(0x4); - writel(value, TEGRA_VI_BASE + (CSI_DSI_MIPI_CAL_CONFIG << 2)); - - value = PAD_DRIV_DN_REF(0x5) | PAD_DRIV_UP_REF(0x7); - writel(value, TEGRA_VI_BASE + (CSI_MIPIBIAS_PAD_CONFIG << 2)); - - value = PAD_CIL_PDVREG(0x0); - writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2)); -} - -static void tegra_dsi_mipi_calibrate(struct udevice *dev) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad; - u32 value; - int ret; - - ret = misc_set_enabled(priv->mipi, true); - if (ret) - log_debug("%s: failed to enable MIPI calibration: %d\n", - __func__, ret); - - writel(0, &pad->pad_ctrl); - writel(0, &pad->pad_ctrl_1); - writel(0, &pad->pad_ctrl_2); - writel(0, &pad->pad_ctrl_3); - writel(0, &pad->pad_ctrl_4); - - /* DSI pad enable */ - value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); - writel(value, &pad->pad_ctrl); - - value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | - DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | - DSI_PAD_OUT_CLK(0x0); - writel(value, &pad->pad_ctrl_2); - - value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | - DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); - writel(value, &pad->pad_ctrl_3); - - ret = misc_write(priv->mipi, priv->calibration_pads, NULL, 0); - if (ret) - log_debug("%s: MIPI calibration failed %d\n", __func__, ret); - - if (priv->slave) - tegra_dsi_mipi_calibrate(priv->slave); -} - -static void tegra_dsi_set_timeout(struct udevice *dev, - unsigned long bclk, - unsigned int vrefresh) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct dsi_timeout_reg *rtimeout = &priv->dsi->timeout; - unsigned int timeout; - u32 value; - - /* one frame high-speed transmission timeout */ - timeout = (bclk / vrefresh) / 512; - value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); - writel(value, &rtimeout->dsi_timeout_0); - - /* 2 ms peripheral timeout for panel */ - timeout = 2 * bclk / 512 * 1000; - value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); - writel(value, &rtimeout->dsi_timeout_1); - - value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); - writel(value, &rtimeout->dsi_to_tally); - - if (priv->slave) - tegra_dsi_set_timeout(priv->slave, bclk, vrefresh); -} - -static void tegra_dsi_set_phy_timing(struct udevice *dev, - unsigned long period, - const struct mipi_dphy_timing *dphy_timing) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct dsi_timing_reg *ptiming = &priv->dsi->ptiming; - u32 value; - - value = DSI_TIMING_FIELD(dphy_timing->hsexit, period, 1) << 24 | - DSI_TIMING_FIELD(dphy_timing->hstrail, period, 0) << 16 | - DSI_TIMING_FIELD(dphy_timing->hszero, period, 3) << 8 | - DSI_TIMING_FIELD(dphy_timing->hsprepare, period, 1); - writel(value, &ptiming->dsi_phy_timing_0); - - value = DSI_TIMING_FIELD(dphy_timing->clktrail, period, 1) << 24 | - DSI_TIMING_FIELD(dphy_timing->clkpost, period, 1) << 16 | - DSI_TIMING_FIELD(dphy_timing->clkzero, period, 1) << 8 | - DSI_TIMING_FIELD(dphy_timing->lpx, period, 1); - writel(value, &ptiming->dsi_phy_timing_1); - - value = DSI_TIMING_FIELD(dphy_timing->clkprepare, period, 1) << 16 | - DSI_TIMING_FIELD(dphy_timing->clkpre, period, 1) << 8 | - DSI_TIMING_FIELD(0xff * period, period, 0) << 0; - writel(value, &ptiming->dsi_phy_timing_2); - - value = DSI_TIMING_FIELD(dphy_timing->taget, period, 1) << 16 | - DSI_TIMING_FIELD(dphy_timing->tasure, period, 1) << 8 | - DSI_TIMING_FIELD(dphy_timing->tago, period, 1); - writel(value, &ptiming->dsi_bta_timing); - - if (priv->slave) - tegra_dsi_set_phy_timing(priv->slave, period, dphy_timing); -} - -static u32 tegra_dsi_get_lanes(struct udevice *dev) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct mipi_dsi_device *device = &priv->device; - - if (priv->master) { - struct tegra_dsi_priv *mpriv = dev_get_priv(priv->master); - struct mipi_dsi_device *mdevice = &mpriv->device; - - return mdevice->lanes + device->lanes; - } - - if (priv->slave) { - struct tegra_dsi_priv *spriv = dev_get_priv(priv->slave); - struct mipi_dsi_device *sdevice = &spriv->device; - - return device->lanes + sdevice->lanes; - } - - return device->lanes; -} - -static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, - unsigned int size) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct dsi_ganged_mode_reg *ganged = &priv->dsi->ganged; - - writel(start, &ganged->ganged_mode_start); - writel(size << 16 | size, &ganged->ganged_mode_size); - writel(DSI_GANGED_MODE_CONTROL_ENABLE, &ganged->ganged_mode_ctrl); -} - -static void tegra_dsi_configure(struct udevice *dev, unsigned int pipe, - unsigned long mode_flags) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct mipi_dsi_device *device = &priv->device; - struct display_timing *timing = &priv->timing; - - struct dsi_misc_reg *misc = &priv->dsi->misc; - struct dsi_pkt_seq_reg *pkt = &priv->dsi->pkt; - struct dsi_pkt_len_reg *len = &priv->dsi->len; - - unsigned int hact, hsw, hbp, hfp, i, mul, div; - const u32 *pkt_seq; - u32 value; - - tegra_dsi_get_muldiv(device->format, &mul, &div); - - if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { - printf("[DSI] Non-burst video mode with sync pulses\n"); - pkt_seq = pkt_seq_video_non_burst_sync_pulses; - } else if (mode_flags & MIPI_DSI_MODE_VIDEO) { - printf("[DSI] Non-burst video mode with sync events\n"); - pkt_seq = pkt_seq_video_non_burst_sync_events; - } else { - printf("[DSI] Command mode\n"); - pkt_seq = pkt_seq_command_mode; - } - - value = DSI_CONTROL_CHANNEL(0) | - DSI_CONTROL_FORMAT(priv->format) | - DSI_CONTROL_LANES(device->lanes - 1) | - DSI_CONTROL_SOURCE(pipe); - writel(value, &misc->dsi_ctrl); - - writel(priv->video_fifo_depth, &misc->dsi_max_threshold); - - value = DSI_HOST_CONTROL_HS; - writel(value, &misc->host_dsi_ctrl); - - value = readl(&misc->dsi_ctrl); - - if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) - value |= DSI_CONTROL_HS_CLK_CTRL; - - value &= ~DSI_CONTROL_TX_TRIG(3); - - /* enable DCS commands for command mode */ - if (mode_flags & MIPI_DSI_MODE_VIDEO) - value &= ~DSI_CONTROL_DCS_ENABLE; - else - value |= DSI_CONTROL_DCS_ENABLE; - - value |= DSI_CONTROL_VIDEO_ENABLE; - value &= ~DSI_CONTROL_HOST_ENABLE; - writel(value, &misc->dsi_ctrl); - - for (i = 0; i < NUM_PKT_SEQ; i++) - writel(pkt_seq[i], &pkt->dsi_pkt_seq_0_lo + i); - - if (mode_flags & MIPI_DSI_MODE_VIDEO) { - /* horizontal active pixels */ - hact = timing->hactive.typ * mul / div; - - /* horizontal sync width */ - hsw = timing->hsync_len.typ * mul / div; - - /* horizontal back porch */ - hbp = timing->hback_porch.typ * mul / div; - - /* horizontal front porch */ - hfp = timing->hfront_porch.typ * mul / div; - - if (priv->master || priv->slave) { - hact /= 2; - hsw /= 2; - hbp = hbp / 2 - 1; - hfp /= 2; - } - - if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) - hbp += hsw; - - /* subtract packet overhead */ - hsw -= 10; - hbp -= 14; - hfp -= 8; - - writel(hsw << 16 | 0, &len->dsi_pkt_len_0_1); - writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3); - writel(hfp, &len->dsi_pkt_len_4_5); - writel(0x0f0f << 16, &len->dsi_pkt_len_6_7); - } else { - if (priv->master || priv->slave) { - /* - * For ganged mode, assume symmetric left-right mode. - */ - value = 1 + (timing->hactive.typ / 2) * mul / div; - } else { - /* 1 byte (DCS command) + pixel data */ - value = 1 + timing->hactive.typ * mul / div; - } - - writel(0, &len->dsi_pkt_len_0_1); - writel(value << 16, &len->dsi_pkt_len_2_3); - writel(value << 16, &len->dsi_pkt_len_4_5); - writel(0, &len->dsi_pkt_len_6_7); - - value = MIPI_DCS_WRITE_MEMORY_START << 8 | - MIPI_DCS_WRITE_MEMORY_CONTINUE; - writel(value, &len->dsi_dcs_cmds); - } - - /* set SOL delay */ - if (priv->master || priv->slave) { - unsigned long delay, bclk, bclk_ganged; - unsigned int lanes = tegra_dsi_get_lanes(dev); - unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + - timing->hback_porch.typ + timing->hsync_len.typ; - - /* SOL to valid, valid to FIFO and FIFO write delay */ - delay = 4 + 4 + 2; - delay = DIV_ROUND_UP(delay * mul, div * lanes); - /* FIFO read delay */ - delay = delay + 6; - - bclk = DIV_ROUND_UP(htotal * mul, div * lanes); - bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); - value = bclk - bclk_ganged + delay + 20; - } else { - /* set SOL delay (for non-burst mode only) */ - value = 8 * mul / div; - } - - writel(value, &misc->dsi_sol_delay); - - if (priv->slave) { - tegra_dsi_configure(priv->slave, pipe, mode_flags); - /* - * TODO: Support modes other than symmetrical left-right - * split. - */ - tegra_dsi_ganged_enable(dev, 0, timing->hactive.typ / 2); - tegra_dsi_ganged_enable(priv->slave, timing->hactive.typ / 2, - timing->hactive.typ / 2); - } -} - -static void tegra_dsi_enable(struct udevice *dev) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct dsi_misc_reg *misc = &priv->dsi->misc; - u32 value; - - /* enable DSI controller */ - value = readl(&misc->dsi_pwr_ctrl); - value |= DSI_POWER_CONTROL_ENABLE; - writel(value, &misc->dsi_pwr_ctrl); - - if (priv->slave) - tegra_dsi_enable(priv->slave); -} - -static int tegra_dsi_encoder_enable(struct udevice *dev) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct tegra_dc_plat *dc_plat = dev_get_plat(dev); - struct mipi_dsi_device *device = &priv->device; - struct display_timing *timing = &priv->timing; - struct dsi_misc_reg *misc = &priv->dsi->misc; - unsigned int mul, div; - unsigned long bclk, plld, period; - u32 value, lanes; - int ret; - - /* If for some reasone DSI is enabled then it needs to - * be disabled in order for the panel initialization - * commands to be properly sent. - */ - value = readl(&misc->dsi_pwr_ctrl); - - if (value & DSI_POWER_CONTROL_ENABLE) { - value = readl(&misc->dsi_pwr_ctrl); - value &= ~DSI_POWER_CONTROL_ENABLE; - writel(value, &misc->dsi_pwr_ctrl); - } - - /* Disable interrupt */ - writel(0, &misc->int_enable); - - if (priv->version) - tegra_dsi_mipi_calibrate(dev); - else - tegra_dsi_pad_calibrate(&priv->dsi->pad); - - tegra_dsi_get_muldiv(device->format, &mul, &div); - - /* compute byte clock */ - lanes = tegra_dsi_get_lanes(dev); - bclk = (timing->pixelclock.typ * mul) / (div * lanes); - - tegra_dsi_set_timeout(dev, bclk, 60); - - /* - * Compute bit clock and round up to the next MHz. - */ - plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC; - period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld); - - ret = mipi_dphy_timing_get_default(&priv->dphy_timing, period); - if (ret < 0) { - printf("%s: failed to get D-PHY timing: %d\n", __func__, ret); - return ret; - } - - ret = mipi_dphy_timing_validate(&priv->dphy_timing, period); - if (ret < 0) { - printf("%s: failed to validate D-PHY timing: %d\n", __func__, ret); - return ret; - } - - /* - * The D-PHY timing fields are expressed in byte-clock cycles, so - * multiply the period by 8. - */ - tegra_dsi_set_phy_timing(dev, period * 8, &priv->dphy_timing); - - /* Perform panel HW setup */ - ret = panel_enable_backlight(priv->panel); - if (ret) - return ret; - - tegra_dsi_configure(dev, dc_plat->pipe, device->mode_flags); - - tegra_dc_enable_controller(dev); - - tegra_dsi_enable(dev); - - return 0; -} - -static int tegra_dsi_bridge_set_panel(struct udevice *dev, int percent) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - - /* Turn on/off backlight */ - return panel_set_backlight(priv->panel, percent); -} - -static int tegra_dsi_panel_timings(struct udevice *dev, - struct display_timing *timing) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - - memcpy(timing, &priv->timing, sizeof(*timing)); - - return 0; -} - -static void tegra_dsi_init_clocks(struct udevice *dev) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct tegra_dc_plat *dc_plat = dev_get_plat(dev); - struct mipi_dsi_device *device = &priv->device; - unsigned int mul, div, lanes; - unsigned long bclk, plld; - - /* Switch parents of DSI clocks in case of not standard parent */ - if (priv->clk->id == PERIPH_ID_DSI && - priv->clk_parent->id == CLOCK_ID_DISPLAY2) { - /* Change DSIA clock parent to PLLD2 */ - struct clk_rst_ctlr *clkrst = - (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - - /* DSIA_CLK_SRC */ - setbits_le32(&clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_base, - BIT(25)); - } - - if (priv->clk->id == PERIPH_ID_DSIB && - priv->clk_parent->id == CLOCK_ID_DISPLAY) { - /* Change DSIB clock parent to match DSIA */ - struct clk_rst_ctlr *clkrst = - (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - - /* DSIB_CLK_SRC */ - clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); - } - - tegra_dsi_get_muldiv(device->format, &mul, &div); - - lanes = tegra_dsi_get_lanes(dev); - bclk = (priv->timing.pixelclock.typ * mul) / (div * lanes); - - plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC); - - dc_plat->scdiv = ((plld * USEC_PER_SEC + - priv->timing.pixelclock.typ / 2) / - priv->timing.pixelclock.typ) - 2; - - /* - * BUG: If DISP1 is a PLLD/D2 child, it cannot go over 370MHz. The - * cause of this is not quite clear. This can be overcomed by - * halving the PLLD/D2 if the target rate is > 800MHz. This way - * DISP1 and DSI clocks will be equal. - */ - if (plld > 800) - plld /= 2; - - 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, plld, 12, 0, 8); - break; - - case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ - clock_set_rate(priv->clk_parent->id, plld, 26, 0, 8); - break; - - case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ - case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */ - clock_set_rate(priv->clk_parent->id, plld, 13, 0, 8); - break; - - case CLOCK_OSC_FREQ_19_2: - case CLOCK_OSC_FREQ_38_4: - default: - /* - * These are not supported. - */ - break; - } - - clk_enable(priv->clk); -} - -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_VIDEO_BRIDGE, dev, - "nvidia,ganged-mode", &gangster); - if (gangster) { - /* Ganged mode is set */ - struct tegra_dsi_priv *spriv = dev_get_priv(gangster); - - mpriv->slave = gangster; - spriv->master = dev; - } - - return 0; -} - -static int tegra_dsi_bridge_probe(struct udevice *dev) -{ - struct tegra_dsi_priv *priv = dev_get_priv(dev); - struct mipi_dsi_device *device = &priv->device; - struct mipi_dsi_panel_plat *mipi_plat; - struct reset_ctl reset_ctl; - int ret; - - priv->version = dev_get_driver_data(dev); - - priv->dsi = (struct dsi_ctlr *)dev_read_addr_ptr(dev); - if (!priv->dsi) { - printf("%s: No display controller address\n", __func__); - return -EINVAL; - } - - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) { - log_debug("%s: Could not get DSI clock: %ld\n", - __func__, PTR_ERR(priv->clk)); - return PTR_ERR(priv->clk); - } - - priv->clk_parent = devm_clk_get(dev, "parent"); - if (IS_ERR(priv->clk_parent)) { - log_debug("%s: Could not get DSI clock parent: %ld\n", - __func__, PTR_ERR(priv->clk_parent)); - return PTR_ERR(priv->clk_parent); - } - - priv->video_fifo_depth = 1920; - priv->host_fifo_depth = 64; - - tegra_dsi_ganged_probe(dev); - - ret = reset_get_by_name(dev, "dsi", &reset_ctl); - if (ret) { - log_debug("%s: reset_get_by_name() failed: %d\n", - __func__, ret); - return ret; - } - - ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, - "avdd-dsi-csi-supply", &priv->avdd); - if (ret) - debug("%s: Cannot get avdd-dsi-csi-supply: error %d\n", - __func__, ret); - - /* Check all DSI children */ - device_foreach_child(priv->panel, dev) { - if (device_get_uclass_id(priv->panel) == UCLASS_PANEL) - break; - } - - /* if loop exits without panel device return error */ - if (device_get_uclass_id(priv->panel) != UCLASS_PANEL) { - log_debug("%s: panel not found, ret %d\n", __func__, ret); - return -EINVAL; - } - - ret = uclass_get_device_by_ofnode(UCLASS_PANEL, dev_ofnode(priv->panel), - &priv->panel); - if (ret) { - log_debug("%s: Cannot get panel: error %d\n", __func__, ret); - return log_ret(ret); - } - - if (priv->version) { - ret = uclass_get_device_by_phandle(UCLASS_MISC, dev, - "nvidia,mipi-calibrate", - &priv->mipi); - if (ret) { - log_debug("%s: cannot get MIPI: error %d\n", __func__, ret); - return ret; - } - - ret = dev_read_u32_index(dev, "nvidia,mipi-calibrate", 1, - &priv->calibration_pads); - if (ret) { - log_debug("%s: cannot get calibration pads: error %d\n", - __func__, ret); - return ret; - } - } - - panel_get_display_timing(priv->panel, &priv->timing); - - mipi_plat = dev_get_plat(priv->panel); - mipi_plat->device = device; - - priv->host.dev = (struct device *)dev; - priv->host.ops = &tegra_dsi_bridge_host_ops; - - device->host = &priv->host; - device->lanes = mipi_plat->lanes; - device->format = mipi_plat->format; - device->mode_flags = mipi_plat->mode_flags; - - tegra_dsi_get_format(device->format, &priv->format); - - reset_assert(&reset_ctl); - - ret = regulator_set_enable_if_allowed(priv->avdd, true); - if (ret && ret != -ENOSYS) - return ret; - - tegra_dsi_init_clocks(dev); - - mdelay(2); - reset_deassert(&reset_ctl); - - return 0; -} - -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, -}; - -static const struct udevice_id tegra_dsi_bridge_ids[] = { - { .compatible = "nvidia,tegra20-dsi", .data = DSI_V0 }, - { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 }, - { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 }, - { .compatible = "nvidia,tegra124-dsi", .data = DSI_V1 }, - { } -}; - -U_BOOT_DRIVER(tegra_dsi) = { - .name = "tegra_dsi", - .id = UCLASS_VIDEO_BRIDGE, - .of_match = tegra_dsi_bridge_ids, - .ops = &tegra_dsi_bridge_ops, - .bind = dm_scan_fdt_dev, - .probe = tegra_dsi_bridge_probe, - .plat_auto = sizeof(struct tegra_dc_plat), - .priv_auto = sizeof(struct tegra_dsi_priv), -}; diff --git a/drivers/video/tegra20/tegra-dsi.h b/drivers/video/tegra20/tegra-dsi.h deleted file mode 100644 index 683c5e31a34..00000000000 --- a/drivers/video/tegra20/tegra-dsi.h +++ /dev/null @@ -1,246 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2010 - * NVIDIA Corporation - */ - -#ifndef _TEGRA_DSI_H -#define _TEGRA_DSI_H - -#ifndef __ASSEMBLY__ -#include -#endif - -/* Register definitions for the Tegra display serial interface */ - -/* DSI syncpoint register 0x000 ~ 0x002 */ -struct dsi_syncpt_reg { - /* Address 0x000 ~ 0x002 */ - uint incr_syncpt; /* _INCR_SYNCPT_0 */ - uint incr_syncpt_ctrl; /* _INCR_SYNCPT_CNTRL_0 */ - uint incr_syncpt_err; /* _INCR_SYNCPT_ERROR_0 */ -}; - -/* DSI misc register 0x008 ~ 0x015 */ -struct dsi_misc_reg { - /* Address 0x008 ~ 0x015 */ - uint ctxsw; /* _CTXSW_0 */ - uint dsi_rd_data; /* _DSI_RD_DATA_0 */ - uint dsi_wr_data; /* _DSI_WR_DATA_0 */ - uint dsi_pwr_ctrl; /* _DSI_POWER_CONTROL_0 */ - uint int_enable; /* _INT_ENABLE_0 */ - uint int_status; /* _INT_STATUS_0 */ - uint int_mask; /* _INT_MASK_0 */ - uint host_dsi_ctrl; /* _HOST_DSI_CONTROL_0 */ - uint dsi_ctrl; /* _DSI_CONTROL_0 */ - uint dsi_sol_delay; /* _DSI_SOL_DELAY_0 */ - uint dsi_max_threshold; /* _DSI_MAX_THRESHOLD_0 */ - uint dsi_trigger; /* _DSI_TRIGGER_0 */ - uint dsi_tx_crc; /* _DSI_TX_CRC_0 */ - uint dsi_status; /* _DSI_STATUS_0 */ -}; - -/* DSI init sequence register 0x01a ~ 0x022 */ -struct dsi_init_seq_reg { - /* Address 0x01a ~ 0x022 */ - uint dsi_init_seq_ctrl; /* _DSI_INIT_SEQ_CONTROL_0 */ - uint dsi_init_seq_data_0; /* _DSI_INIT_SEQ_DATA_0_0 */ - uint dsi_init_seq_data_1; /* _DSI_INIT_SEQ_DATA_1_0 */ - uint dsi_init_seq_data_2; /* _DSI_INIT_SEQ_DATA_2_0 */ - uint dsi_init_seq_data_3; /* _DSI_INIT_SEQ_DATA_3_0 */ - uint dsi_init_seq_data_4; /* _DSI_INIT_SEQ_DATA_4_0 */ - uint dsi_init_seq_data_5; /* _DSI_INIT_SEQ_DATA_5_0 */ - uint dsi_init_seq_data_6; /* _DSI_INIT_SEQ_DATA_6_0 */ - uint dsi_init_seq_data_7; /* _DSI_INIT_SEQ_DATA_7_0 */ -}; - -/* DSI packet sequence register 0x023 ~ 0x02e */ -struct dsi_pkt_seq_reg { - /* Address 0x023 ~ 0x02e */ - uint dsi_pkt_seq_0_lo; /* _DSI_PKT_SEQ_0_LO_0 */ - uint dsi_pkt_seq_0_hi; /* _DSI_PKT_SEQ_0_HI_0 */ - uint dsi_pkt_seq_1_lo; /* _DSI_PKT_SEQ_1_LO_0 */ - uint dsi_pkt_seq_1_hi; /* _DSI_PKT_SEQ_1_HI_0 */ - uint dsi_pkt_seq_2_lo; /* _DSI_PKT_SEQ_2_LO_0 */ - uint dsi_pkt_seq_2_hi; /* _DSI_PKT_SEQ_2_HI_0 */ - uint dsi_pkt_seq_3_lo; /* _DSI_PKT_SEQ_3_LO_0 */ - uint dsi_pkt_seq_3_hi; /* _DSI_PKT_SEQ_3_HI_0 */ - uint dsi_pkt_seq_4_lo; /* _DSI_PKT_SEQ_4_LO_0 */ - uint dsi_pkt_seq_4_hi; /* _DSI_PKT_SEQ_4_HI_0 */ - uint dsi_pkt_seq_5_lo; /* _DSI_PKT_SEQ_5_LO_0 */ - uint dsi_pkt_seq_5_hi; /* _DSI_PKT_SEQ_5_HI_0 */ -}; - -/* DSI packet length register 0x033 ~ 0x037 */ -struct dsi_pkt_len_reg { - /* Address 0x033 ~ 0x037 */ - uint dsi_dcs_cmds; /* _DSI_DCS_CMDS_0 */ - uint dsi_pkt_len_0_1; /* _DSI_PKT_LEN_0_1_0 */ - uint dsi_pkt_len_2_3; /* _DSI_PKT_LEN_2_3_0 */ - uint dsi_pkt_len_4_5; /* _DSI_PKT_LEN_4_5_0 */ - uint dsi_pkt_len_6_7; /* _DSI_PKT_LEN_6_7_0 */ -}; - -/* DSI PHY timing register 0x03c ~ 0x03f */ -struct dsi_timing_reg { - /* Address 0x03c ~ 0x03f */ - uint dsi_phy_timing_0; /* _DSI_PHY_TIMING_0_0 */ - uint dsi_phy_timing_1; /* _DSI_PHY_TIMING_1_0 */ - uint dsi_phy_timing_2; /* _DSI_PHY_TIMING_2_0 */ - uint dsi_bta_timing; /* _DSI_BTA_TIMING_0 */ -}; - -/* DSI timeout register 0x044 ~ 0x046 */ -struct dsi_timeout_reg { - /* Address 0x044 ~ 0x046 */ - uint dsi_timeout_0; /* _DSI_TIMEOUT_0_0 */ - uint dsi_timeout_1; /* _DSI_TIMEOUT_1_0 */ - uint dsi_to_tally; /* _DSI_TO_TALLY_0 */ -}; - -/* DSI PAD control register 0x04b ~ 0x052 */ -struct dsi_pad_ctrl_reg { - /* Address 0x04b ~ 0x052 */ - uint pad_ctrl; /* _PAD_CONTROL_0 */ - uint pad_ctrl_cd; /* _PAD_CONTROL_CD_0 */ - uint pad_cd_status; /* _PAD_CD_STATUS_0 */ - uint dsi_vid_mode_control; /* _DSI_VID_MODE_CONTROL_0 */ - uint pad_ctrl_1; /* _PAD_CONTROL_1 */ - uint pad_ctrl_2; /* _PAD_CONTROL_2 */ - uint pad_ctrl_3; /* _PAD_CONTROL_3 */ - uint pad_ctrl_4; /* _PAD_CONTROL_4 */ -}; - -/* DSI ganged mode register 0x053 ~ 0x04e */ -struct dsi_ganged_mode_reg { - /* Address 0x053 ~ 0x055 */ - uint ganged_mode_ctrl; /* _DSI_GANGED_MODE_CONTROL_0 */ - uint ganged_mode_start; /* _DSI_GANGED_MODE_START_0 */ - uint ganged_mode_size; /* _DSI_GANGED_MODE_SIZE_0 */ -}; - -/* Display Serial Interface (DSI_) regs */ -struct dsi_ctlr { - struct dsi_syncpt_reg syncpt; /* SYNCPT register 0x000 ~ 0x002 */ - uint reserved0[5]; /* reserved_0[5] */ - - struct dsi_misc_reg misc; /* MISC register 0x008 ~ 0x015 */ - uint reserved1[4]; /* reserved_1[4] */ - - struct dsi_init_seq_reg init; /* INIT register 0x01a ~ 0x022 */ - struct dsi_pkt_seq_reg pkt; /* PKT register 0x023 ~ 0x02e */ - uint reserved2[4]; /* reserved_2[4] */ - - struct dsi_pkt_len_reg len; /* LEN registers 0x033 ~ 0x037 */ - uint reserved3[4]; /* reserved_3[4] */ - - struct dsi_timing_reg ptiming; /* TIMING registers 0x03c ~ 0x03f */ - uint reserved4[4]; /* reserved_4[4] */ - - struct dsi_timeout_reg timeout; /* TIMEOUT registers 0x044 ~ 0x046 */ - uint reserved5[4]; /* reserved_5[4] */ - - struct dsi_pad_ctrl_reg pad; /* PAD registers 0x04b ~ 0x04e */ - struct dsi_ganged_mode_reg ganged; /* GANGED registers 0x053 ~ 0x055 */ -}; - -#define DSI_POWER_CONTROL_ENABLE BIT(0) - -#define DSI_HOST_CONTROL_FIFO_RESET BIT(21) -#define DSI_HOST_CONTROL_CRC_RESET BIT(20) -#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12) -#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12) -#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12) -#define DSI_HOST_CONTROL_RAW BIT(6) -#define DSI_HOST_CONTROL_HS BIT(5) -#define DSI_HOST_CONTROL_FIFO_SEL BIT(4) -#define DSI_HOST_CONTROL_IMM_BTA BIT(3) -#define DSI_HOST_CONTROL_PKT_BTA BIT(2) -#define DSI_HOST_CONTROL_CS BIT(1) -#define DSI_HOST_CONTROL_ECC BIT(0) - -#define DSI_CONTROL_HS_CLK_CTRL BIT(20) -#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16) -#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12) -#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8) -#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4) -#define DSI_CONTROL_DCS_ENABLE BIT(3) -#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2) -#define DSI_CONTROL_VIDEO_ENABLE BIT(1) -#define DSI_CONTROL_HOST_ENABLE BIT(0) - -#define DSI_TRIGGER_HOST BIT(1) -#define DSI_TRIGGER_VIDEO BIT(0) - -#define DSI_STATUS_IDLE BIT(10) -#define DSI_STATUS_UNDERFLOW BIT(9) -#define DSI_STATUS_OVERFLOW BIT(8) - -#define DSI_TIMING_FIELD(value, period, hwinc) \ - ((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff) - -#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16) -#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0) -#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16) -#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0) - -#define DSI_TALLY_TA(x) (((x) & 0xff) << 16) -#define DSI_TALLY_LRX(x) (((x) & 0xff) << 8) -#define DSI_TALLY_HTX(x) (((x) & 0xff) << 0) - -#define DSI_PAD_CONTROL_PAD_PULLDN_ENAB(x) (((x) & 0x1) << 28) -#define DSI_PAD_CONTROL_PAD_SLEWUPADJ(x) (((x) & 0x7) << 24) -#define DSI_PAD_CONTROL_PAD_SLEWDNADJ(x) (((x) & 0x7) << 20) -#define DSI_PAD_CONTROL_PAD_PREEMP_EN(x) (((x) & 0x1) << 19) -#define DSI_PAD_CONTROL_PAD_PDIO_CLK(x) (((x) & 0x1) << 18) -#define DSI_PAD_CONTROL_PAD_PDIO(x) (((x) & 0x3) << 16) -#define DSI_PAD_CONTROL_PAD_LPUPADJ(x) (((x) & 0x3) << 14) -#define DSI_PAD_CONTROL_PAD_LPDNADJ(x) (((x) & 0x3) << 12) - -#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0) -#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16) - -#define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0) -#define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4) -#define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8) -#define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12) -#define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16) - -#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12) -#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8) -#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4) -#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0) - -#define DSI_GANGED_MODE_CONTROL_ENABLE BIT(0) - -/* - * pixel format as used in the DSI_CONTROL_FORMAT field - */ -enum tegra_dsi_format { - TEGRA_DSI_FORMAT_16P, - TEGRA_DSI_FORMAT_18NP, - TEGRA_DSI_FORMAT_18P, - TEGRA_DSI_FORMAT_24P, -}; - -/* DSI calibration in VI region */ -#define TEGRA_VI_BASE 0x54080000 - -#define CSI_CILA_MIPI_CAL_CONFIG_0 0x22a -#define MIPI_CAL_TERMOSA(x) (((x) & 0x1f) << 0) - -#define CSI_CILB_MIPI_CAL_CONFIG_0 0x22b -#define MIPI_CAL_TERMOSB(x) (((x) & 0x1f) << 0) - -#define CSI_CIL_PAD_CONFIG 0x229 -#define PAD_CIL_PDVREG(x) (((x) & 0x01) << 1) - -#define CSI_DSI_MIPI_CAL_CONFIG 0x234 -#define MIPI_CAL_HSPDOSD(x) (((x) & 0x1f) << 16) -#define MIPI_CAL_HSPUOSD(x) (((x) & 0x1f) << 8) - -#define CSI_MIPIBIAS_PAD_CONFIG 0x235 -#define PAD_DRIV_DN_REF(x) (((x) & 0x7) << 16) -#define PAD_DRIV_UP_REF(x) (((x) & 0x7) << 8) - -#endif /* _TEGRA_DSI_H */ diff --git a/drivers/video/tegra20/tegra-hdmi.c b/drivers/video/tegra20/tegra-hdmi.c deleted file mode 100644 index bda69919d92..00000000000 --- a/drivers/video/tegra20/tegra-hdmi.c +++ /dev/null @@ -1,623 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2013 NVIDIA Corporation - * Copyright (c) 2023 Svyatoslav Ryhel - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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 deleted file mode 100644 index d17655973e3..00000000000 --- a/drivers/video/tegra20/tegra-hdmi.h +++ /dev/null @@ -1,648 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2010 - * NVIDIA Corporation - */ - -#ifndef _TEGRA_HDMI_H -#define _TEGRA_HDMI_H - -#ifndef __ASSEMBLY__ -#include -#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 deleted file mode 100644 index 58ab871a3b4..00000000000 --- a/drivers/video/tegra20/tegra-host1x.c +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2025 Svyatoslav Ryhel - */ - -#include -#include -#include -#include -#include - -#include -#include - -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-mipi.c b/drivers/video/tegra20/tegra-mipi.c deleted file mode 100644 index a4f4343d008..00000000000 --- a/drivers/video/tegra20/tegra-mipi.c +++ /dev/null @@ -1,294 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2013 NVIDIA Corporation - * Copyright (c) 2023 Svyatoslav Ryhel - */ - -#include -#include -#include -#include -#include - -#include -#include - -/* MIPI control registers 0x00 ~ 0x74 */ -struct mipi_ctlr { - uint mipi_cal_ctrl; - uint mipi_cal_autocal_ctrl; - uint mipi_cal_status; - - uint unused1[2]; - - uint mipi_cal_config_csia; - uint mipi_cal_config_csib; - uint mipi_cal_config_csic; - uint mipi_cal_config_csid; - uint mipi_cal_config_csie; - - uint unused2[4]; - - uint mipi_cal_config_dsia; - uint mipi_cal_config_dsib; - uint mipi_cal_config_dsic; - uint mipi_cal_config_dsid; - - uint unused3[4]; - - uint mipi_cal_bias_pad_cfg0; - uint mipi_cal_bias_pad_cfg1; - uint mipi_cal_bias_pad_cfg2; - - uint mipi_cal_dsia_config_2; - uint mipi_cal_dsib_config_2; - uint mipi_cal_cilc_config_2; - uint mipi_cal_cild_config_2; - uint mipi_cal_csie_config_2; -}; - -#define MIPI_DSIA_PADS 0x60 -#define MIPI_DSIB_PADS 0x180 - -#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) -#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) -#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4) -#define MIPI_CAL_CTRL_START BIT(0) - -#define MIPI_CAL_STATUS_DONE BIT(16) -#define MIPI_CAL_STATUS_ACTIVE BIT(0) - -#define MIPI_CAL_OVERIDE(x) (((x) & 0x1) << 30) -#define MIPI_CAL_SEL(x) (((x) & 0x1) << 21) -#define MIPI_CAL_HSPDOS(x) (((x) & 0x1f) << 16) -#define MIPI_CAL_HSPUOS(x) (((x) & 0x1f) << 8) -#define MIPI_CAL_TERMOS(x) (((x) & 0x1f) << 0) - -#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1) -#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0) - -#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16) -#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8) - -#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16) -#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) -#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) - -#define MIPI_CAL_HSCLKPDOSDSI(x) (((x) & 0x1f) << 8) -#define MIPI_CAL_HSCLKPUOSDSI(x) (((x) & 0x1f) << 0) - -struct tegra_mipi_priv { - struct mipi_ctlr *mipi; - struct clk *mipi_cal; - u32 version; -}; - -enum { - T114, - T124, -}; - -static void tegra114_mipi_pads_cal(struct tegra_mipi_priv *priv, - int calibration_pads) -{ - u32 value; - - value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | - MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) | - MIPI_CAL_TERMOS(0x5); - writel(value, &priv->mipi->mipi_cal_config_dsia); - writel(value, &priv->mipi->mipi_cal_config_dsib); - - /* Deselect PAD C */ - value = readl(&priv->mipi->mipi_cal_config_dsic); - value &= ~(MIPI_CAL_SEL(0x1)); - writel(value, &priv->mipi->mipi_cal_config_dsic); - - /* Deselect PAD D */ - value = readl(&priv->mipi->mipi_cal_config_dsid); - value &= ~(MIPI_CAL_SEL(0x1)); - writel(value, &priv->mipi->mipi_cal_config_dsid); -} - -static void tegra124_mipi_pads_cal(struct tegra_mipi_priv *priv, - int calibration_pads) -{ - u32 value; - - /* Calibrate DSI-A */ - if (calibration_pads == MIPI_DSIA_PADS) { - printf("Calibrating DSI-A pads\n"); - - value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | - MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | - MIPI_CAL_TERMOS(0x0); - writel(value, &priv->mipi->mipi_cal_config_dsia); - writel(value, &priv->mipi->mipi_cal_config_dsib); - - value = MIPI_CAL_SEL(0x1) | - MIPI_CAL_HSCLKPDOSDSI(0x1) | - MIPI_CAL_HSCLKPUOSDSI(0x2); - writel(value, &priv->mipi->mipi_cal_dsia_config_2); - writel(value, &priv->mipi->mipi_cal_dsib_config_2); - - /* Deselect PAD C */ - value = readl(&priv->mipi->mipi_cal_cilc_config_2); - value &= ~(MIPI_CAL_SEL(0x1)); - writel(value, &priv->mipi->mipi_cal_cilc_config_2); - - /* Deselect PAD D */ - value = readl(&priv->mipi->mipi_cal_cild_config_2); - value &= ~(MIPI_CAL_SEL(0x1)); - writel(value, &priv->mipi->mipi_cal_cild_config_2); - } - - /* Calibrate DSI-B */ - if (calibration_pads == MIPI_DSIB_PADS) { - printf("Calibrating DSI-B pads\n"); - - value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | - MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | - MIPI_CAL_TERMOS(0x0); - writel(value, &priv->mipi->mipi_cal_config_csic); - writel(value, &priv->mipi->mipi_cal_config_csid); - - value = MIPI_CAL_SEL(0x1) | - MIPI_CAL_HSCLKPDOSDSI(0x1) | - MIPI_CAL_HSCLKPUOSDSI(0x2); - writel(value, &priv->mipi->mipi_cal_cilc_config_2); - writel(value, &priv->mipi->mipi_cal_cild_config_2); - - /* Deselect PAD A */ - value = readl(&priv->mipi->mipi_cal_dsia_config_2); - value &= ~(MIPI_CAL_SEL(0x1)); - writel(value, &priv->mipi->mipi_cal_dsia_config_2); - - /* Deselect PAD B */ - value = readl(&priv->mipi->mipi_cal_dsib_config_2); - value &= ~(MIPI_CAL_SEL(0x1)); - writel(value, &priv->mipi->mipi_cal_dsib_config_2); - } -} - -static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, - int size) -{ - struct tegra_mipi_priv *priv = dev_get_priv(dev); - u32 value; - - value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | - MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); - - value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); - value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); - value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); - - switch (priv->version) { - case T114: - tegra114_mipi_pads_cal(priv, offset); - break; - - case T124: - tegra124_mipi_pads_cal(priv, offset); - break; - - default: - return -EINVAL; - } - - value = readl(&priv->mipi->mipi_cal_ctrl); - value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); - value &= ~MIPI_CAL_CTRL_PRESCALE(0x3); - value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa) | - MIPI_CAL_CTRL_PRESCALE(0x2) | - MIPI_CAL_CTRL_CLKEN_OVR; - writel(value, &priv->mipi->mipi_cal_ctrl); - - /* clear any pending status bits */ - value = readl(&priv->mipi->mipi_cal_status); - writel(value, &priv->mipi->mipi_cal_status); - - value = readl(&priv->mipi->mipi_cal_ctrl); - value |= MIPI_CAL_CTRL_START; - writel(value, &priv->mipi->mipi_cal_ctrl); - - /* - * Wait for min 72uS to let calibration logic finish calibration - * sequence codes before waiting for pads idle state to apply the - * results. - */ - udelay(80); - - return readl_poll_sleep_timeout(&priv->mipi->mipi_cal_status, value, - !(value & MIPI_CAL_STATUS_ACTIVE) && - (value & MIPI_CAL_STATUS_DONE), 100, - 250000); -} - -static int tegra_mipi_enable(struct udevice *dev, bool val) -{ - struct tegra_mipi_priv *priv = dev_get_priv(dev); - u32 value; - - reset_set_enable(priv->mipi_cal->id, 1); - mdelay(100); - reset_set_enable(priv->mipi_cal->id, 0); - mdelay(1); - - clk_enable(priv->mipi_cal); - - value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0); - value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; - value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg0); - - value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); - value &= ~MIPI_CAL_BIAS_PAD_PDVREG; - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); - - return 0; -} - -static const struct misc_ops tegra_mipi_ops = { - .write = tegra_mipi_calibrate, - .set_enabled = tegra_mipi_enable, -}; - -static int tegra_mipi_probe(struct udevice *dev) -{ - struct tegra_mipi_priv *priv = dev_get_priv(dev); - - priv->version = dev_get_driver_data(dev); - - priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev); - if (!priv->mipi) { - log_debug("%s: no MIPI controller address\n", __func__); - return -EINVAL; - } - - priv->mipi_cal = devm_clk_get(dev, NULL); - if (IS_ERR(priv->mipi_cal)) { - log_debug("%s: Could not get MIPI clock: %ld\n", - __func__, PTR_ERR(priv->mipi_cal)); - return PTR_ERR(priv->mipi_cal); - } - - return 0; -} - -static const struct udevice_id tegra_mipi_ids[] = { - { .compatible = "nvidia,tegra114-mipi", .data = T114 }, - { .compatible = "nvidia,tegra124-mipi", .data = T124 }, - { } -}; - -U_BOOT_DRIVER(tegra_mipi) = { - .name = "tegra_mipi", - .id = UCLASS_MISC, - .ops = &tegra_mipi_ops, - .of_match = tegra_mipi_ids, - .probe = tegra_mipi_probe, - .priv_auto = sizeof(struct tegra_mipi_priv), -}; diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra20/tegra-pwm-backlight.c deleted file mode 100644 index 998f0df1991..00000000000 --- a/drivers/video/tegra20/tegra-pwm-backlight.c +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2022 Svyatoslav Ryhel - */ - -#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "tegra-dc.h" - -#define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10 -#define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF - -#define TEGRA_PWM_BL_PERIOD 0xFF -#define TEGRA_PWM_BL_CLK_DIV 0x14 -#define TEGRA_PWM_BL_CLK_SELECT 0x00 - -#define PM_PERIOD_SHIFT 18 -#define PM_CLK_DIVIDER_SHIFT 4 - -#define TEGRA_PWM_PM0 0 -#define TEGRA_PWM_PM1 1 - -struct tegra_pwm_backlight_priv { - struct dc_ctlr *dc; /* Display controller regmap */ - - u32 pwm_source; - u32 period; - u32 clk_div; - u32 clk_select; - u32 dft_brightness; -}; - -static int tegra_pwm_backlight_set_brightness(struct udevice *dev, int percent) -{ - struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); - struct dc_cmd_reg *cmd = &priv->dc->cmd; - struct dc_com_reg *com = &priv->dc->com; - unsigned int ctrl; - unsigned long out_sel; - unsigned long cmd_state; - - if (percent == BACKLIGHT_DEFAULT) - percent = priv->dft_brightness; - - if (percent < TEGRA_PWM_BL_MIN_BRIGHTNESS) - percent = TEGRA_PWM_BL_MIN_BRIGHTNESS; - - if (percent > TEGRA_PWM_BL_MAX_BRIGHTNESS) - percent = TEGRA_PWM_BL_MAX_BRIGHTNESS; - - ctrl = ((priv->period << PM_PERIOD_SHIFT) | - (priv->clk_div << PM_CLK_DIVIDER_SHIFT) | - priv->clk_select); - - /* The new value should be effected immediately */ - cmd_state = readl(&cmd->state_access); - writel((cmd_state | (1 << 2)), &cmd->state_access); - - switch (priv->pwm_source) { - case TEGRA_PWM_PM0: - /* Select the LM0 on PM0 */ - out_sel = readl(&com->pin_output_sel[5]); - out_sel &= ~(7 << 0); - out_sel |= (3 << 0); - writel(out_sel, &com->pin_output_sel[5]); - writel(ctrl, &com->pm0_ctrl); - writel(percent, &com->pm0_duty_cycle); - break; - case TEGRA_PWM_PM1: - /* Select the LM1 on PM1 */ - out_sel = readl(&com->pin_output_sel[5]); - out_sel &= ~(7 << 4); - out_sel |= (3 << 4); - writel(out_sel, &com->pin_output_sel[5]); - writel(ctrl, &com->pm1_ctrl); - writel(percent, &com->pm1_duty_cycle); - break; - default: - break; - } - - writel(cmd_state, &cmd->state_access); - return 0; -} - -static int tegra_pwm_backlight_enable(struct udevice *dev) -{ - struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); - - return tegra_pwm_backlight_set_brightness(dev, priv->dft_brightness); -} - -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)); - - priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc); - if (!priv->dc) { - log_err("%s: failed to get DC controller\n", __func__); - return -EINVAL; - } - - priv->pwm_source = - dev_read_u32_default(dev, "nvidia,pwm-source", - TEGRA_PWM_PM0); - priv->period = - dev_read_u32_default(dev, "nvidia,period", - TEGRA_PWM_BL_PERIOD); - priv->clk_div = - dev_read_u32_default(dev, "nvidia,clock-div", - TEGRA_PWM_BL_CLK_DIV); - priv->clk_select = - dev_read_u32_default(dev, "nvidia,clock-select", - TEGRA_PWM_BL_CLK_SELECT); - priv->dft_brightness = - dev_read_u32_default(dev, "nvidia,default-brightness", - TEGRA_PWM_BL_MAX_BRIGHTNESS); - - return 0; -} - -static const struct backlight_ops tegra_pwm_backlight_ops = { - .enable = tegra_pwm_backlight_enable, - .set_brightness = tegra_pwm_backlight_set_brightness, -}; - -static const struct udevice_id tegra_pwm_backlight_ids[] = { - { .compatible = "nvidia,tegra-pwm-backlight" }, - { } -}; - -U_BOOT_DRIVER(tegra_pwm_backlight) = { - .name = "tegra_pwm_backlight", - .id = UCLASS_PANEL_BACKLIGHT, - .of_match = tegra_pwm_backlight_ids, - .probe = tegra_pwm_backlight_probe, - .ops = &tegra_pwm_backlight_ops, - .priv_auto = sizeof(struct tegra_pwm_backlight_priv), -}; -- cgit v1.2.3