summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Kconfig15
-rw-r--r--drivers/video/Makefile6
-rw-r--r--drivers/video/dp-uclass.c34
-rw-r--r--drivers/video/tegra124/Makefile10
-rw-r--r--drivers/video/tegra124/display.c472
-rw-r--r--drivers/video/tegra124/displayport.h412
-rw-r--r--drivers/video/tegra124/dp.c1607
-rw-r--r--drivers/video/tegra124/sor.c1024
-rw-r--r--drivers/video/tegra124/sor.h922
-rw-r--r--drivers/video/tegra124/tegra124-lcd.c97
10 files changed, 4599 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 51728b3..2544301 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -88,3 +88,18 @@ config VIDEO_LCD_SPI_MISO
hardware and LCD panel id retrieval (if the panel can report it). The
option takes a string in the format understood by 'name_to_gpio'
function, e.g. PH1 for pin 1 of port H.
+
+config DISPLAY_PORT
+ bool "Enable DisplayPort support"
+ help
+ eDP (Embedded DisplayPort) is a standard widely used in laptops
+ to drive LCD panels. This framework provides support for enabling
+ these displays where supported by the video hardware.
+
+config VIDEO_TEGRA124
+ bool "Enable video support on Tegra124"
+ help
+ Tegra124 supports many video output options including eDP and
+ HDMI. At present only eDP is supported by U-Boot. This option
+ enables this support which can be used on devices which
+ have an eDP display connected.
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index f64918e..2ead7f1 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -5,6 +5,10 @@
# SPDX-License-Identifier: GPL-2.0+
#
+ifdef CONFIG_DM
+obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
+endif
+
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
@@ -48,3 +52,5 @@ obj-$(CONFIG_FORMIKE) += formike.o
obj-$(CONFIG_LG4573) += lg4573.o
obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
obj-$(CONFIG_VIDEO_PARADE) += parade.o
+
+obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
diff --git a/drivers/video/dp-uclass.c b/drivers/video/dp-uclass.c
new file mode 100644
index 0000000..17f5de9
--- /dev/null
+++ b/drivers/video/dp-uclass.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <displayport.h>
+#include <errno.h>
+
+int display_port_read_edid(struct udevice *dev, u8 *buf, int buf_size)
+{
+ struct dm_display_port_ops *ops = display_port_get_ops(dev);
+
+ if (!ops || !ops->read_edid)
+ return -ENOSYS;
+ return ops->read_edid(dev, buf, buf_size);
+}
+
+int display_port_enable(struct udevice *dev, int panel_bpp,
+ const struct display_timing *timing)
+{
+ struct dm_display_port_ops *ops = display_port_get_ops(dev);
+
+ if (!ops || !ops->enable)
+ return -ENOSYS;
+ return ops->enable(dev, panel_bpp, timing);
+}
+
+UCLASS_DRIVER(display_port) = {
+ .id = UCLASS_DISPLAY_PORT,
+ .name = "display_port",
+};
diff --git a/drivers/video/tegra124/Makefile b/drivers/video/tegra124/Makefile
new file mode 100644
index 0000000..52eedb0
--- /dev/null
+++ b/drivers/video/tegra124/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2014 Google, Inc
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-y += display.o
+obj-y += dp.o
+obj-y += sor.o
+obj-y += tegra124-lcd.o
diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra124/display.c
new file mode 100644
index 0000000..7179dbf
--- /dev/null
+++ b/drivers/video/tegra124/display.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Extracted from Chromium coreboot commit 3f59b13d
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <edid.h>
+#include <errno.h>
+#include <displayport.h>
+#include <edid.h>
+#include <fdtdec.h>
+#include <lcd.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/pwm.h>
+#include <asm/arch-tegra/dc.h>
+#include "displayport.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* return in 1000ths of a Hertz */
+static int tegra_dc_calc_refresh(const struct display_timing *timing)
+{
+ int h_total, v_total, refresh;
+ int pclk = timing->pixelclock.typ;
+
+ h_total = timing->hactive.typ + timing->hfront_porch.typ +
+ timing->hback_porch.typ + timing->hsync_len.typ;
+ v_total = timing->vactive.typ + timing->vfront_porch.typ +
+ timing->vback_porch.typ + timing->vsync_len.typ;
+ if (!pclk || !h_total || !v_total)
+ return 0;
+ refresh = pclk / h_total;
+ refresh *= 1000;
+ refresh /= v_total;
+
+ return refresh;
+}
+
+static void print_mode(const struct display_timing *timing)
+{
+ int refresh = tegra_dc_calc_refresh(timing);
+
+ debug("MODE:%dx%d@%d.%03uHz pclk=%d\n",
+ timing->hactive.typ, timing->vactive.typ, refresh / 1000,
+ refresh % 1000, timing->pixelclock.typ);
+}
+
+static int update_display_mode(struct dc_ctlr *disp_ctrl,
+ const struct display_timing *timing,
+ int href_to_sync, int vref_to_sync)
+{
+ print_mode(timing);
+
+ writel(0x1, &disp_ctrl->disp.disp_timing_opt);
+
+ writel(vref_to_sync << 16 | href_to_sync,
+ &disp_ctrl->disp.ref_to_sync);
+
+ writel(timing->vsync_len.typ << 16 | timing->hsync_len.typ,
+ &disp_ctrl->disp.sync_width);
+
+ writel(((timing->vback_porch.typ - vref_to_sync) << 16) |
+ timing->hback_porch.typ, &disp_ctrl->disp.back_porch);
+
+ writel(((timing->vfront_porch.typ + vref_to_sync) << 16) |
+ timing->hfront_porch.typ, &disp_ctrl->disp.front_porch);
+
+ writel(timing->hactive.typ | (timing->vactive.typ << 16),
+ &disp_ctrl->disp.disp_active);
+
+ /**
+ * We want to use PLLD_out0, which is PLLD / 2:
+ * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv.
+ *
+ * Currently most panels work inside clock range 50MHz~100MHz, and PLLD
+ * has some requirements to have VCO in range 500MHz~1000MHz (see
+ * clock.c for more detail). To simplify calculation, we set
+ * PixelClockDiv to 1 and ShiftClockDiv to 1. In future these values
+ * may be calculated by clock_display, to allow wider frequency range.
+ *
+ * Note ShiftClockDiv is a 7.1 format value.
+ */
+ const u32 shift_clock_div = 1;
+ writel((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
+ ((shift_clock_div - 1) * 2) << SHIFT_CLK_DIVIDER_SHIFT,
+ &disp_ctrl->disp.disp_clk_ctrl);
+ debug("%s: PixelClock=%u, ShiftClockDiv=%u\n", __func__,
+ timing->pixelclock.typ, shift_clock_div);
+ return 0;
+}
+
+static u32 tegra_dc_poll_register(void *reg,
+ u32 mask, u32 exp_val, u32 poll_interval_us, u32 timeout_us)
+{
+ u32 temp = timeout_us;
+ u32 reg_val = 0;
+
+ do {
+ udelay(poll_interval_us);
+ reg_val = readl(reg);
+ if (timeout_us > poll_interval_us)
+ timeout_us -= poll_interval_us;
+ else
+ break;
+ } while ((reg_val & mask) != exp_val);
+
+ if ((reg_val & mask) == exp_val)
+ return 0; /* success */
+
+ return temp;
+}
+
+int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl)
+{
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+
+ if (tegra_dc_poll_register(&disp_ctrl->cmd.state_ctrl,
+ GENERAL_ACT_REQ, 0, 100,
+ DC_POLL_TIMEOUT_MS * 1000)) {
+ debug("dc timeout waiting for DC to stop\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static struct display_timing min_mode = {
+ .hsync_len = { .typ = 1 },
+ .vsync_len = { .typ = 1 },
+ .hback_porch = { .typ = 20 },
+ .vback_porch = { .typ = 0 },
+ .hactive = { .typ = 16 },
+ .vactive = { .typ = 16 },
+ .hfront_porch = { .typ = 1 },
+ .vfront_porch = { .typ = 2 },
+};
+
+/* Disable windows and set minimum raster timings */
+void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx)
+{
+ const int href_to_sync = 0, vref_to_sync = 1;
+ int selected_windows, i;
+
+ selected_windows = readl(&disp_ctrl->cmd.disp_win_header);
+
+ /* Store and clear window options */
+ for (i = 0; i < DC_N_WINDOWS; ++i) {
+ writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header);
+ dc_reg_ctx[i] = readl(&disp_ctrl->win.win_opt);
+ writel(0, &disp_ctrl->win.win_opt);
+ writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl);
+ }
+
+ writel(selected_windows, &disp_ctrl->cmd.disp_win_header);
+
+ /* Store current raster timings and set minimum timings */
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.ref_to_sync);
+ writel(href_to_sync | (vref_to_sync << 16),
+ &disp_ctrl->disp.ref_to_sync);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.sync_width);
+ writel(min_mode.hsync_len.typ | (min_mode.vsync_len.typ << 16),
+ &disp_ctrl->disp.sync_width);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.back_porch);
+ writel(min_mode.hback_porch.typ | (min_mode.vback_porch.typ << 16),
+ &disp_ctrl->disp.back_porch);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.front_porch);
+ writel(min_mode.hfront_porch.typ | (min_mode.vfront_porch.typ << 16),
+ &disp_ctrl->disp.front_porch);
+
+ dc_reg_ctx[i++] = readl(&disp_ctrl->disp.disp_active);
+ writel(min_mode.hactive.typ | (min_mode.vactive.typ << 16),
+ &disp_ctrl->disp.disp_active);
+
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+}
+
+/* Restore previous windows status and raster timings */
+void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx)
+{
+ int selected_windows, i;
+
+ selected_windows = readl(&disp_ctrl->cmd.disp_win_header);
+
+ for (i = 0; i < DC_N_WINDOWS; ++i) {
+ writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header);
+ writel(dc_reg_ctx[i], &disp_ctrl->win.win_opt);
+ writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl);
+ }
+
+ writel(selected_windows, &disp_ctrl->cmd.disp_win_header);
+
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.ref_to_sync);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.sync_width);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.back_porch);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.front_porch);
+ writel(dc_reg_ctx[i++], &disp_ctrl->disp.disp_active);
+
+ writel(GENERAL_UPDATE, &disp_ctrl->cmd.state_ctrl);
+}
+
+static int tegra_depth_for_bpp(int bpp)
+{
+ switch (bpp) {
+ case 32:
+ return COLOR_DEPTH_R8G8B8A8;
+ case 16:
+ return COLOR_DEPTH_B5G6R5;
+ default:
+ debug("Unsupported LCD bit depth");
+ return -1;
+ }
+}
+
+static int update_window(struct dc_ctlr *disp_ctrl,
+ u32 frame_buffer, int fb_bits_per_pixel,
+ const struct display_timing *timing)
+{
+ const u32 colour_white = 0xffffff;
+ int colour_depth;
+ u32 val;
+
+ writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
+
+ writel(((timing->vactive.typ << 16) | timing->hactive.typ),
+ &disp_ctrl->win.size);
+ writel(((timing->vactive.typ << 16) |
+ (timing->hactive.typ * fb_bits_per_pixel / 8)),
+ &disp_ctrl->win.prescaled_size);
+ writel(((timing->hactive.typ * fb_bits_per_pixel / 8 + 31) /
+ 32 * 32), &disp_ctrl->win.line_stride);
+
+ colour_depth = tegra_depth_for_bpp(fb_bits_per_pixel);
+ if (colour_depth == -1)
+ return -EINVAL;
+
+ writel(colour_depth, &disp_ctrl->win.color_depth);
+
+ writel(frame_buffer, &disp_ctrl->winbuf.start_addr);
+ writel(0x1000 << V_DDA_INC_SHIFT | 0x1000 << H_DDA_INC_SHIFT,
+ &disp_ctrl->win.dda_increment);
+
+ writel(colour_white, &disp_ctrl->disp.blend_background_color);
+ writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
+ &disp_ctrl->cmd.disp_cmd);
+
+ writel(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
+
+ val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
+ val |= GENERAL_UPDATE | WIN_A_UPDATE;
+ writel(val, &disp_ctrl->cmd.state_ctrl);
+
+ /* Enable win_a */
+ val = readl(&disp_ctrl->win.win_opt);
+ writel(val | WIN_ENABLE, &disp_ctrl->win.win_opt);
+
+ return 0;
+}
+
+static int tegra_dc_init(struct dc_ctlr *disp_ctrl)
+{
+ /* do not accept interrupts during initialization */
+ writel(0x00000000, &disp_ctrl->cmd.int_mask);
+ writel(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
+ &disp_ctrl->cmd.state_access);
+ writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
+ writel(0x00000000, &disp_ctrl->win.win_opt);
+ writel(0x00000000, &disp_ctrl->win.byte_swap);
+ writel(0x00000000, &disp_ctrl->win.buffer_ctrl);
+
+ writel(0x00000000, &disp_ctrl->win.pos);
+ writel(0x00000000, &disp_ctrl->win.h_initial_dda);
+ writel(0x00000000, &disp_ctrl->win.v_initial_dda);
+ writel(0x00000000, &disp_ctrl->win.dda_increment);
+ writel(0x00000000, &disp_ctrl->win.dv_ctrl);
+
+ writel(0x01000000, &disp_ctrl->win.blend_layer_ctrl);
+ writel(0x00000000, &disp_ctrl->win.blend_match_select);
+ writel(0x00000000, &disp_ctrl->win.blend_nomatch_select);
+ writel(0x00000000, &disp_ctrl->win.blend_alpha_1bit);
+
+ writel(0x00000000, &disp_ctrl->winbuf.start_addr_hi);
+ writel(0x00000000, &disp_ctrl->winbuf.addr_h_offset);
+ writel(0x00000000, &disp_ctrl->winbuf.addr_v_offset);
+
+ writel(0x00000000, &disp_ctrl->com.crc_checksum);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[0]);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[1]);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[2]);
+ writel(0x00000000, &disp_ctrl->com.pin_output_enb[3]);
+ writel(0x00000000, &disp_ctrl->disp.disp_signal_opt0);
+
+ return 0;
+}
+
+static void dump_config(int panel_bpp, struct display_timing *timing)
+{
+ printf("timing->hactive.typ = %d\n", timing->hactive.typ);
+ printf("timing->vactive.typ = %d\n", timing->vactive.typ);
+ printf("timing->pixelclock.typ = %d\n", timing->pixelclock.typ);
+
+ printf("timing->hfront_porch.typ = %d\n", timing->hfront_porch.typ);
+ printf("timing->hsync_len.typ = %d\n", timing->hsync_len.typ);
+ printf("timing->hback_porch.typ = %d\n", timing->hback_porch.typ);
+
+ printf("timing->vfront_porch.typ %d\n", timing->vfront_porch.typ);
+ printf("timing->vsync_len.typ = %d\n", timing->vsync_len.typ);
+ printf("timing->vback_porch.typ = %d\n", timing->vback_porch.typ);
+
+ printf("panel_bits_per_pixel = %d\n", panel_bpp);
+}
+
+static int display_update_config_from_edid(struct udevice *dp_dev,
+ int *panel_bppp,
+ struct display_timing *timing)
+{
+ u8 buf[EDID_SIZE];
+ int bpc, ret;
+
+ ret = display_port_read_edid(dp_dev, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ ret = edid_get_timing(buf, ret, timing, &bpc);
+ if (ret)
+ return ret;
+
+ /* Use this information if valid */
+ if (bpc != -1)
+ *panel_bppp = bpc * 3;
+
+ return 0;
+}
+
+/* Somewhat torturous method */
+static int get_backlight_info(const void *blob, struct gpio_desc *vdd,
+ struct gpio_desc *enable, int *pwmp)
+{
+ int sor, panel, backlight, power;
+ const u32 *prop;
+ int len;
+ int ret;
+
+ *pwmp = 0;
+ sor = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR);
+ if (sor < 0)
+ return -ENOENT;
+ panel = fdtdec_lookup_phandle(blob, sor, "nvidia,panel");
+ if (panel < 0)
+ return -ENOENT;
+ backlight = fdtdec_lookup_phandle(blob, panel, "backlight");
+ if (backlight < 0)
+ return -ENOENT;
+ ret = gpio_request_by_name_nodev(blob, backlight, "enable-gpios", 0,
+ enable, GPIOD_IS_OUT);
+ if (ret)
+ return ret;
+ prop = fdt_getprop(blob, backlight, "pwms", &len);
+ if (!prop || len != 3 * sizeof(u32))
+ return -EINVAL;
+ *pwmp = fdt32_to_cpu(prop[1]);
+
+ power = fdtdec_lookup_phandle(blob, backlight, "power-supply");
+ if (power < 0)
+ return -ENOENT;
+ ret = gpio_request_by_name_nodev(blob, power, "gpio", 0, vdd,
+ GPIOD_IS_OUT);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ dm_gpio_free(NULL, enable);
+ return ret;
+}
+
+int display_init(void *lcdbase, int fb_bits_per_pixel,
+ struct display_timing *timing)
+{
+ struct dc_ctlr *dc_ctlr;
+ const void *blob = gd->fdt_blob;
+ struct udevice *dp_dev;
+ const int href_to_sync = 1, vref_to_sync = 1;
+ int panel_bpp = 18; /* default 18 bits per pixel */
+ u32 plld_rate;
+ struct gpio_desc vdd_gpio, enable_gpio;
+ int pwm;
+ int node;
+ int ret;
+
+ ret = uclass_get_device(UCLASS_DISPLAY_PORT, 0, &dp_dev);
+ if (ret)
+ return ret;
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
+ if (node < 0)
+ return -ENOENT;
+ dc_ctlr = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+ if (fdtdec_decode_display_timing(blob, node, 0, timing))
+ return -EINVAL;
+
+ ret = display_update_config_from_edid(dp_dev, &panel_bpp, timing);
+ if (ret) {
+ debug("%s: Failed to decode EDID, using defaults\n", __func__);
+ dump_config(panel_bpp, timing);
+ }
+
+ if (!get_backlight_info(blob, &vdd_gpio, &enable_gpio, &pwm)) {
+ dm_gpio_set_value(&vdd_gpio, 1);
+ debug("%s: backlight vdd setting gpio %08x to %d\n",
+ __func__, gpio_get_number(&vdd_gpio), 1);
+ }
+
+ /*
+ * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
+ * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
+ * update_display_mode() for detail.
+ */
+ plld_rate = clock_set_display_rate(timing->pixelclock.typ * 2);
+ if (plld_rate == 0) {
+ printf("dc: clock init failed\n");
+ return -EIO;
+ } else if (plld_rate != timing->pixelclock.typ * 2) {
+ debug("dc: plld rounded to %u\n", plld_rate);
+ timing->pixelclock.typ = plld_rate / 2;
+ }
+
+ /* Init dc */
+ ret = tegra_dc_init(dc_ctlr);
+ if (ret) {
+ debug("dc: init failed\n");
+ return ret;
+ }
+
+ /* Configure dc mode */
+ ret = update_display_mode(dc_ctlr, timing, href_to_sync, vref_to_sync);
+ if (ret) {
+ debug("dc: failed to configure display mode\n");
+ return ret;
+ }
+
+ /* Enable dp */
+ ret = display_port_enable(dp_dev, panel_bpp, timing);
+ if (ret)
+ return ret;
+
+ ret = update_window(dc_ctlr, (ulong)lcdbase, fb_bits_per_pixel, timing);
+ if (ret)
+ return ret;
+
+ /* Set up Tegra PWM to drive the panel backlight */
+ pwm_enable(pwm, 0, 220, 0x2e);
+ udelay(10 * 1000);
+
+ if (dm_gpio_is_valid(&enable_gpio)) {
+ dm_gpio_set_value(&enable_gpio, 1);
+ debug("%s: backlight enable setting gpio %08x to %d\n",
+ __func__, gpio_get_number(&enable_gpio), 1);
+ }
+
+ return 0;
+}
diff --git a/drivers/video/tegra124/displayport.h b/drivers/video/tegra124/displayport.h
new file mode 100644
index 0000000..ace6ab0
--- /dev/null
+++ b/drivers/video/tegra124/displayport.h
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2014, NVIDIA Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _TEGRA_DISPLAYPORT_H
+#define _TEGRA_DISPLAYPORT_H
+
+#include <linux/drm_dp_helper.h>
+
+struct dpaux_ctlr {
+ u32 reserved0;
+ u32 intr_en_aux;
+ u32 reserved2_4;
+ u32 intr_aux;
+};
+
+#define DPAUX_INTR_EN_AUX 0x1
+#define DPAUX_INTR_AUX 0x5
+#define DPAUX_DP_AUXDATA_WRITE_W(i) (0x9 + 4 * (i))
+#define DPAUX_DP_AUXDATA_READ_W(i) (0x19 + 4 * (i))
+#define DPAUX_DP_AUXADDR 0x29
+#define DPAUX_DP_AUXCTL 0x2d
+#define DPAUX_DP_AUXCTL_CMDLEN_SHIFT 0
+#define DPAUX_DP_AUXCTL_CMDLEN_FIELD 0xff
+#define DPAUX_DP_AUXCTL_CMD_SHIFT 12
+#define DPAUX_DP_AUXCTL_CMD_MASK (0xf << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2CWR (0 << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2CRD (1 << 12)
+#define DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT (2 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOTWR (4 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOTRD (5 << 12)
+#define DPAUX_DP_AUXCTL_CMD_MOTREQWSTAT (6 << 12)
+#define DPAUX_DP_AUXCTL_CMD_AUXWR (8 << 12)
+#define DPAUX_DP_AUXCTL_CMD_AUXRD (9 << 12)
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_SHIFT 16
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_MASK (0x1 << 16)
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_DONE (0 << 16)
+#define DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING (1 << 16)
+#define DPAUX_DP_AUXCTL_RST_SHIFT 31
+#define DPAUX_DP_AUXCTL_RST_DEASSERT (0 << 31)
+#define DPAUX_DP_AUXCTL_RST_ASSERT (1 << 31)
+#define DPAUX_DP_AUXSTAT 0x31
+#define DPAUX_DP_AUXSTAT_HPD_STATUS_SHIFT 28
+#define DPAUX_DP_AUXSTAT_HPD_STATUS_UNPLUG (0 << 28)
+#define DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED (1 << 28)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_SHIFT 20
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_MASK (0xf << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_IDLE (0 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_SYNC (1 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_START1 (2 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_COMMAND (3 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_ADDRESS (4 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_LENGTH (5 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_WRITE1 (6 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_READ1 (7 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_GET_M (8 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_STOP1 (9 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_STOP2 (10 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_REPLY (11 << 20)
+#define DPAUX_DP_AUXSTAT_AUXCTL_STATE_CLEANUP (12 << 20)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_SHIFT 16
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_MASK (0xf << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_ACK (0 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_NACK (1 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER (2 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_I2CNACK (4 << 16)
+#define DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER (8 << 16)
+#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_SHIFT 11
+#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_NOT_PENDING (0 << 11)
+#define DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING (1 << 11)
+#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_SHIFT 10
+#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_NOT_PENDING (0 << 10)
+#define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING (1 << 10)
+#define DPAUX_DP_AUXSTAT_RX_ERROR_SHIFT 9
+#define DPAUX_DP_AUXSTAT_RX_ERROR_NOT_PENDING (0 << 9)
+#define DPAUX_DP_AUXSTAT_RX_ERROR_PENDING (1 << 9)
+#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_SHIFT 8
+#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_NOT_PENDING (0 << 8)
+#define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING (1 << 8)
+#define DPAUX_DP_AUXSTAT_REPLY_M_SHIFT 0
+#define DPAUX_DP_AUXSTAT_REPLY_M_MASK (0xff << 0)
+#define DPAUX_HPD_CONFIG (0x3d)
+#define DPAUX_HPD_IRQ_CONFIG 0x41
+#define DPAUX_DP_AUX_CONFIG 0x45
+#define DPAUX_HYBRID_PADCTL 0x49
+#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_SHIFT 15
+#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_DISABLE (0 << 15)
+#define DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV_ENABLE (1 << 15)
+#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_SHIFT 14
+#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_DISABLE (0 << 14)
+#define DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV_ENABLE (1 << 14)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_SHIFT 12
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_DEFAULT_MASK (0x3 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_60 (0 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_64 (1 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 (2 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_CMH_V0_56 (3 << 12)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_SHIFT 8
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_DEFAULT_MASK (0x7 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_78 (0 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_60 (1 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_54 (2 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_45 (3 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 (4 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_42 (5 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_39 (6 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_34 (7 << 8)
+#define DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT 2
+#define DPAUX_HYBRID_PADCTL_AUX_DRVI_DEFAULT_MASK (0x3f << 2)
+#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_SHIFT 1
+#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_DISABLE (0 << 1)
+#define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE (1 << 1)
+#define DPAUX_HYBRID_PADCTL_MODE_SHIFT 0
+#define DPAUX_HYBRID_PADCTL_MODE_AUX 0
+#define DPAUX_HYBRID_PADCTL_MODE_I2C 1
+#define DPAUX_HYBRID_SPARE 0x4d
+#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP 0
+#define DPAUX_HYBRID_SPARE_PAD_PWR_POWERDOWN 1
+
+#define DP_AUX_DEFER_MAX_TRIES 7
+#define DP_AUX_TIMEOUT_MAX_TRIES 2
+#define DP_POWER_ON_MAX_TRIES 3
+
+#define DP_AUX_MAX_BYTES 16
+
+#define DP_AUX_TIMEOUT_MS 40
+#define DP_DPCP_RETRY_SLEEP_NS 400
+
+static const u32 tegra_dp_vs_regs[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x13, 0x19, 0x1e, 0x28}, /* voltage swing: L0 */
+ {0x1e, 0x25, 0x2d}, /* L1 */
+ {0x28, 0x32}, /* L2 */
+ {0x3c}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x12, 0x17, 0x1b, 0x25},
+ {0x1c, 0x23, 0x2a},
+ {0x25, 0x2f},
+ {0x39},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x12, 0x16, 0x1a, 0x22},
+ {0x1b, 0x20, 0x27},
+ {0x24, 0x2d},
+ {0x36},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x11, 0x14, 0x17, 0x1f},
+ {0x19, 0x1e, 0x24},
+ {0x22, 0x2a},
+ {0x32},
+ },
+};
+
+static const u32 tegra_dp_pe_regs[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x00, 0x09, 0x13, 0x25}, /* voltage swing: L0 */
+ {0x00, 0x0f, 0x1e}, /* L1 */
+ {0x00, 0x14}, /* L2 */
+ {0x00}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x00, 0x0a, 0x14, 0x28},
+ {0x00, 0x0f, 0x1e},
+ {0x00, 0x14},
+ {0x00},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x00, 0x0a, 0x14, 0x28},
+ {0x00, 0x0f, 0x1e},
+ {0x00, 0x14},
+ {0x00},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x00, 0x0a, 0x14, 0x28},
+ {0x00, 0x0f, 0x1e},
+ {0x00, 0x14},
+ {0x00},
+ },
+};
+
+static const u32 tegra_dp_pc_regs[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x00, 0x00, 0x00, 0x00}, /* voltage swing: L0 */
+ {0x00, 0x00, 0x00}, /* L1 */
+ {0x00, 0x00}, /* L2 */
+ {0x00}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x02, 0x02, 0x04, 0x05},
+ {0x02, 0x04, 0x05},
+ {0x04, 0x05},
+ {0x05},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x04, 0x05, 0x08, 0x0b},
+ {0x05, 0x09, 0x0b},
+ {0x08, 0x0a},
+ {0x0b},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x05, 0x09, 0x0b, 0x12},
+ {0x09, 0x0d, 0x12},
+ {0x0b, 0x0f},
+ {0x12},
+ },
+};
+
+static const u32 tegra_dp_tx_pu[][4][4] = {
+ /* postcursor2 L0 */
+ {
+ /* pre-emphasis: L0, L1, L2, L3 */
+ {0x20, 0x30, 0x40, 0x60}, /* voltage swing: L0 */
+ {0x30, 0x40, 0x60}, /* L1 */
+ {0x40, 0x60}, /* L2 */
+ {0x60}, /* L3 */
+ },
+
+ /* postcursor2 L1 */
+ {
+ {0x20, 0x20, 0x30, 0x50},
+ {0x30, 0x40, 0x50},
+ {0x40, 0x50},
+ {0x60},
+ },
+
+ /* postcursor2 L2 */
+ {
+ {0x20, 0x20, 0x30, 0x40},
+ {0x30, 0x30, 0x40},
+ {0x40, 0x50},
+ {0x60},
+ },
+
+ /* postcursor2 L3 */
+ {
+ {0x20, 0x20, 0x20, 0x40},
+ {0x30, 0x30, 0x40},
+ {0x40, 0x40},
+ {0x60},
+ },
+};
+
+enum {
+ DRIVECURRENT_LEVEL0 = 0,
+ DRIVECURRENT_LEVEL1 = 1,
+ DRIVECURRENT_LEVEL2 = 2,
+ DRIVECURRENT_LEVEL3 = 3,
+};
+
+enum {
+ PREEMPHASIS_DISABLED = 0,
+ PREEMPHASIS_LEVEL1 = 1,
+ PREEMPHASIS_LEVEL2 = 2,
+ PREEMPHASIS_LEVEL3 = 3,
+};
+
+enum {
+ POSTCURSOR2_LEVEL0 = 0,
+ POSTCURSOR2_LEVEL1 = 1,
+ POSTCURSOR2_LEVEL2 = 2,
+ POSTCURSOR2_LEVEL3 = 3,
+ POSTCURSOR2_SUPPORTED
+};
+
+static inline int tegra_dp_is_max_vs(u32 pe, u32 vs)
+{
+ return (vs < (DRIVECURRENT_LEVEL3 - pe)) ? 0 : 1;
+}
+
+static inline int tegra_dp_is_max_pe(u32 pe, u32 vs)
+{
+ return (pe < (PREEMPHASIS_LEVEL3 - vs)) ? 0 : 1;
+}
+
+static inline int tegra_dp_is_max_pc(u32 pc)
+{
+ return (pc < POSTCURSOR2_LEVEL3) ? 0 : 1;
+}
+
+/* DPCD definitions which are not defined in drm_dp_helper.h */
+#define DP_DPCD_REV_MAJOR_SHIFT 4
+#define DP_DPCD_REV_MAJOR_MASK (0xf << 4)
+#define DP_DPCD_REV_MINOR_SHIFT 0
+#define DP_DPCD_REV_MINOR_MASK 0xf
+
+#define DP_MAX_LINK_RATE_VAL_1_62_GPBS 0x6
+#define DP_MAX_LINK_RATE_VAL_2_70_GPBS 0xa
+#define DP_MAX_LINK_RATE_VAL_5_40_GPBS 0x4
+
+#define DP_MAX_LANE_COUNT_LANE_1 0x1
+#define DP_MAX_LANE_COUNT_LANE_2 0x2
+#define DP_MAX_LANE_COUNT_LANE_4 0x4
+#define DP_MAX_LANE_COUNT_TPS3_SUPPORTED_YES (1 << 6)
+#define DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES (1 << 7)
+
+#define NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT 0
+#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T (0x00000001 << 2)
+#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F (0x00000000 << 2)
+#define NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT 3
+#define NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T (0x00000001 << 5)
+#define NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F (0x00000000 << 5)
+
+#define DP_MAX_DOWNSPREAD_VAL_NONE 0
+#define DP_MAX_DOWNSPREAD_VAL_0_5_PCT 1
+#define DP_MAX_DOWNSPREAD_NO_AUX_HANDSHAKE_LT_T (1 << 6)
+
+#define DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES 1
+#define DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES (1 << 1)
+
+#define DP_LANE_COUNT_SET_ENHANCEDFRAMING_T (1 << 7)
+
+#define DP_TRAINING_PATTERN_SET_SC_DISABLED_T (1 << 5)
+#define NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F (0x00000000 << 5)
+#define NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T (0x00000001 << 5)
+
+#define DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE 0
+#define DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE 1
+
+#define NV_DPCD_TRAINING_LANE0_1_SET2 0x10f
+#define NV_DPCD_TRAINING_LANE2_3_SET2 0x110
+#define NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T (1 << 2)
+#define NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F (0 << 2)
+#define NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T (1 << 6)
+#define NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F (0 << 6)
+#define NV_DPCD_LANEX_SET2_PC2_SHIFT 0
+#define NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT 4
+
+#define NV_DPCD_STATUS_LANEX_CR_DONE_SHIFT 0
+#define NV_DPCD_STATUS_LANEX_CR_DONE_NO (0x00000000)
+#define NV_DPCD_STATUS_LANEX_CR_DONE_YES (0x00000001)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT 1
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_NO (0x00000000 << 1)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES (0x00000001 << 1)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT 2
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_NO (0x00000000 << 2)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES (0x00000001 << 2)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT 4
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_NO (0x00000000 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES (0x00000001 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT 5
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_NO (0x00000000 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES (0x00000001 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT 6
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_NO (0x00000000 << 6)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES (0x00000001 << 6)
+
+#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED (0x00000204)
+#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_NO (0x00000000)
+#define NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES (0x00000001)
+
+#define NV_DPCD_STATUS_LANEX_CR_DONE_SHIFT 0
+#define NV_DPCD_STATUS_LANEX_CR_DONE_NO (0x00000000)
+#define NV_DPCD_STATUS_LANEX_CR_DONE_YES (0x00000001)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT 1
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_NO (0x00000000 << 1)
+#define NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES (0x00000001 << 1)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT 2
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_NO (0x00000000 << 2)
+#define NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES (0x00000001 << 2)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_SHIFT 4
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_NO (0x00000000 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES (0x00000001 << 4)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT 5
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_NO (0x00000000 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES (0x00000001 << 5)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT 6
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_NO (0x00000000 << 6)
+#define NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES (0x00000001 << 6)
+
+#define NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT 0
+#define NV_DPCD_ADJUST_REQ_LANEX_DC_MASK 0x3
+#define NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT 2
+#define NV_DPCD_ADJUST_REQ_LANEX_PE_MASK (0x3 << 2)
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT 4
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK (0x3 << 4)
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT 6
+#define NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK (0x3 << 6)
+#define NV_DPCD_ADJUST_REQ_POST_CURSOR2 (0x0000020C)
+#define NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK 0x3
+#define NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(i) (i*2)
+
+#define NV_DPCD_TRAINING_AUX_RD_INTERVAL (0x0000000E)
+#define NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F (0x00000000 << 2)
+#endif
diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c
new file mode 100644
index 0000000..3c0b721
--- /dev/null
+++ b/drivers/video/tegra124/dp.c
@@ -0,0 +1,1607 @@
+/*
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <displayport.h>
+#include <dm.h>
+#include <div64.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/arch-tegra/dc.h>
+#include "displayport.h"
+#include "edid.h"
+#include "sor.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DO_FAST_LINK_TRAINING 1
+
+struct tegra_dp_plat {
+ ulong base;
+};
+
+struct tegra_dp_priv {
+ struct dpaux_ctlr *regs;
+ struct tegra_dc_sor_data *sor;
+ u8 revision;
+ int enabled;
+};
+
+struct tegra_dp_priv dp_data;
+
+static inline u32 tegra_dpaux_readl(struct tegra_dp_priv *dp, u32 reg)
+{
+ return readl((u32 *)dp->regs + reg);
+}
+
+static inline void tegra_dpaux_writel(struct tegra_dp_priv *dp, u32 reg,
+ u32 val)
+{
+ writel(val, (u32 *)dp->regs + reg);
+}
+
+static inline u32 tegra_dc_dpaux_poll_register(struct tegra_dp_priv *dp,
+ u32 reg, u32 mask, u32 exp_val,
+ u32 poll_interval_us,
+ u32 timeout_us)
+{
+ u32 reg_val = 0;
+ u32 temp = timeout_us;
+
+ do {
+ udelay(poll_interval_us);
+ reg_val = tegra_dpaux_readl(dp, reg);
+ if (timeout_us > poll_interval_us)
+ timeout_us -= poll_interval_us;
+ else
+ break;
+ } while ((reg_val & mask) != exp_val);
+
+ if ((reg_val & mask) == exp_val)
+ return 0; /* success */
+ debug("dpaux_poll_register 0x%x: timeout: (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
+ reg, reg_val, mask, exp_val);
+ return temp;
+}
+
+static inline int tegra_dpaux_wait_transaction(struct tegra_dp_priv *dp)
+{
+ /* According to DP spec, each aux transaction needs to finish
+ within 40ms. */
+ if (tegra_dc_dpaux_poll_register(dp, DPAUX_DP_AUXCTL,
+ DPAUX_DP_AUXCTL_TRANSACTREQ_MASK,
+ DPAUX_DP_AUXCTL_TRANSACTREQ_DONE,
+ 100, DP_AUX_TIMEOUT_MS * 1000) != 0) {
+ debug("dp: DPAUX transaction timeout\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int tegra_dc_dpaux_write_chunk(struct tegra_dp_priv *dp, u32 cmd,
+ u32 addr, u8 *data, u32 *size,
+ u32 *aux_stat)
+{
+ int i;
+ u32 reg_val;
+ u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES;
+ u32 defer_retries = DP_AUX_DEFER_MAX_TRIES;
+ u32 temp_data;
+
+ if (*size > DP_AUX_MAX_BYTES)
+ return -1; /* only write one chunk of data */
+
+ /* Make sure the command is write command */
+ switch (cmd) {
+ case DPAUX_DP_AUXCTL_CMD_I2CWR:
+ case DPAUX_DP_AUXCTL_CMD_MOTWR:
+ case DPAUX_DP_AUXCTL_CMD_AUXWR:
+ break;
+ default:
+ debug("dp: aux write cmd 0x%x is invalid\n", cmd);
+ return -EINVAL;
+ }
+
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr);
+ for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) {
+ memcpy(&temp_data, data, 4);
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXDATA_WRITE_W(i), temp_data);
+ data += 4;
+ }
+
+ reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL);
+ reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK;
+ reg_val |= cmd;
+ reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD;
+ reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT);
+
+ while ((timeout_retries > 0) && (defer_retries > 0)) {
+ if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) ||
+ (defer_retries != DP_AUX_DEFER_MAX_TRIES))
+ udelay(1);
+
+ reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING;
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val);
+
+ if (tegra_dpaux_wait_transaction(dp))
+ debug("dp: aux write transaction timeout\n");
+
+ *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) {
+ if (timeout_retries-- > 0) {
+ debug("dp: aux write retry (0x%x) -- %d\n",
+ *aux_stat, timeout_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue;
+ } else {
+ debug("dp: aux write got error (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) {
+ if (defer_retries-- > 0) {
+ debug("dp: aux write defer (0x%x) -- %d\n",
+ *aux_stat, defer_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue;
+ } else {
+ debug("dp: aux write defer exceeds max retries (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) ==
+ DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) {
+ *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK);
+ return 0;
+ } else {
+ debug("dp: aux write failed (0x%x)\n", *aux_stat);
+ return -EIO;
+ }
+ }
+ /* Should never come to here */
+ return -EIO;
+}
+
+static int tegra_dc_dpaux_read_chunk(struct tegra_dp_priv *dp, u32 cmd,
+ u32 addr, u8 *data, u32 *size,
+ u32 *aux_stat)
+{
+ u32 reg_val;
+ u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES;
+ u32 defer_retries = DP_AUX_DEFER_MAX_TRIES;
+
+ if (*size > DP_AUX_MAX_BYTES) {
+ debug("only read one chunk\n");
+ return -EIO; /* only read one chunk */
+ }
+
+ /* Check to make sure the command is read command */
+ switch (cmd) {
+ case DPAUX_DP_AUXCTL_CMD_I2CRD:
+ case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT:
+ case DPAUX_DP_AUXCTL_CMD_MOTRD:
+ case DPAUX_DP_AUXCTL_CMD_AUXRD:
+ break;
+ default:
+ debug("dp: aux read cmd 0x%x is invalid\n", cmd);
+ return -EIO;
+ }
+
+ *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+ if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
+ debug("dp: HPD is not detected\n");
+ return -EIO;
+ }
+
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr);
+
+ reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL);
+ reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK;
+ reg_val |= cmd;
+ reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD;
+ reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT);
+ while ((timeout_retries > 0) && (defer_retries > 0)) {
+ if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) ||
+ (defer_retries != DP_AUX_DEFER_MAX_TRIES))
+ udelay(DP_DPCP_RETRY_SLEEP_NS * 2);
+
+ reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING;
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val);
+
+ if (tegra_dpaux_wait_transaction(dp))
+ debug("dp: aux read transaction timeout\n");
+
+ *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) {
+ if (timeout_retries-- > 0) {
+ debug("dp: aux read retry (0x%x) -- %d\n",
+ *aux_stat, timeout_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue; /* retry */
+ } else {
+ debug("dp: aux read got error (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) ||
+ (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) {
+ if (defer_retries-- > 0) {
+ debug("dp: aux read defer (0x%x) -- %d\n",
+ *aux_stat, defer_retries);
+ /* clear the error bits */
+ tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT,
+ *aux_stat);
+ continue;
+ } else {
+ debug("dp: aux read defer exceeds max retries (0x%x)\n",
+ *aux_stat);
+ return -ETIMEDOUT;
+ }
+ }
+
+ if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) ==
+ DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) {
+ int i;
+ u32 temp_data[4];
+
+ for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i)
+ temp_data[i] = tegra_dpaux_readl(dp,
+ DPAUX_DP_AUXDATA_READ_W(i));
+
+ *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK);
+ memcpy(data, temp_data, *size);
+
+ return 0;
+ } else {
+ debug("dp: aux read failed (0x%x\n", *aux_stat);
+ return -EIO;
+ }
+ }
+ /* Should never come to here */
+ debug("%s: can't\n", __func__);
+
+ return -EIO;
+}
+
+static int tegra_dc_dpaux_read(struct tegra_dp_priv *dp, u32 cmd, u32 addr,
+ u8 *data, u32 *size, u32 *aux_stat)
+{
+ u32 finished = 0;
+ u32 cur_size;
+ int ret = 0;
+
+ do {
+ cur_size = *size - finished;
+ if (cur_size > DP_AUX_MAX_BYTES)
+ cur_size = DP_AUX_MAX_BYTES;
+
+ ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr,
+ data, &cur_size, aux_stat);
+ if (ret)
+ break;
+
+ /* cur_size should be the real size returned */
+ addr += cur_size;
+ data += cur_size;
+ finished += cur_size;
+
+ } while (*size > finished);
+ *size = finished;
+
+ return ret;
+}
+
+static int tegra_dc_dp_dpcd_read(struct tegra_dp_priv *dp, u32 cmd,
+ u8 *data_ptr)
+{
+ u32 size = 1;
+ u32 status = 0;
+ int ret;
+
+ ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
+ cmd, data_ptr, &size, &status);
+ if (ret) {
+ debug("dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n",
+ cmd, status);
+ }
+
+ return ret;
+}
+
+static int tegra_dc_dp_dpcd_write(struct tegra_dp_priv *dp, u32 cmd,
+ u8 data)
+{
+ u32 size = 1;
+ u32 status = 0;
+ int ret;
+
+ ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR,
+ cmd, &data, &size, &status);
+ if (ret) {
+ debug("dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n",
+ cmd, status);
+ }
+
+ return ret;
+}
+
+static int tegra_dc_i2c_aux_read(struct tegra_dp_priv *dp, u32 i2c_addr,
+ u8 addr, u8 *data, u32 size, u32 *aux_stat)
+{
+ u32 finished = 0;
+ int ret = 0;
+
+ do {
+ u32 cur_size = min((u32)DP_AUX_MAX_BYTES, size - finished);
+
+ u32 len = 1;
+ ret = tegra_dc_dpaux_write_chunk(
+ dp, DPAUX_DP_AUXCTL_CMD_MOTWR, i2c_addr,
+ &addr, &len, aux_stat);
+ if (ret) {
+ debug("%s: error sending address to read.\n",
+ __func__);
+ return ret;
+ }
+
+ ret = tegra_dc_dpaux_read_chunk(
+ dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr,
+ data, &cur_size, aux_stat);
+ if (ret) {
+ debug("%s: error reading data.\n", __func__);
+ return ret;
+ }
+
+ /* cur_size should be the real size returned */
+ addr += cur_size;
+ data += cur_size;
+ finished += cur_size;
+ } while (size > finished);
+
+ return finished;
+}
+
+static void tegra_dc_dpaux_enable(struct tegra_dp_priv *dp)
+{
+ /* clear interrupt */
+ tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff);
+ /* do not enable interrupt for now. Enable them when Isr in place */
+ tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0);
+
+ tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL,
+ DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 |
+ DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 |
+ 0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT |
+ DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE);
+
+ tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE,
+ DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP);
+}
+
+#ifdef DEBUG
+static void tegra_dc_dp_dump_link_cfg(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ debug("DP config: cfg_name cfg_value\n");
+ debug(" Lane Count %d\n",
+ link_cfg->max_lane_count);
+ debug(" SupportEnhancedFraming %s\n",
+ link_cfg->support_enhanced_framing ? "Y" : "N");
+ debug(" Bandwidth %d\n",
+ link_cfg->max_link_bw);
+ debug(" bpp %d\n",
+ link_cfg->bits_per_pixel);
+ debug(" EnhancedFraming %s\n",
+ link_cfg->enhanced_framing ? "Y" : "N");
+ debug(" Scramble_enabled %s\n",
+ link_cfg->scramble_ena ? "Y" : "N");
+ debug(" LinkBW %d\n",
+ link_cfg->link_bw);
+ debug(" lane_count %d\n",
+ link_cfg->lane_count);
+ debug(" activespolarity %d\n",
+ link_cfg->activepolarity);
+ debug(" active_count %d\n",
+ link_cfg->active_count);
+ debug(" tu_size %d\n",
+ link_cfg->tu_size);
+ debug(" active_frac %d\n",
+ link_cfg->active_frac);
+ debug(" watermark %d\n",
+ link_cfg->watermark);
+ debug(" hblank_sym %d\n",
+ link_cfg->hblank_sym);
+ debug(" vblank_sym %d\n",
+ link_cfg->vblank_sym);
+}
+#endif
+
+static int _tegra_dp_lower_link_config(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *cfg)
+{
+ switch (cfg->link_bw) {
+ case SOR_LINK_SPEED_G1_62:
+ if (cfg->max_link_bw > SOR_LINK_SPEED_G1_62)
+ cfg->link_bw = SOR_LINK_SPEED_G2_7;
+ cfg->lane_count /= 2;
+ break;
+ case SOR_LINK_SPEED_G2_7:
+ cfg->link_bw = SOR_LINK_SPEED_G1_62;
+ break;
+ case SOR_LINK_SPEED_G5_4:
+ if (cfg->lane_count == 1) {
+ cfg->link_bw = SOR_LINK_SPEED_G2_7;
+ cfg->lane_count = cfg->max_lane_count;
+ } else {
+ cfg->lane_count /= 2;
+ }
+ break;
+ default:
+ debug("dp: Error link rate %d\n", cfg->link_bw);
+ return -ENOLINK;
+ }
+
+ return (cfg->lane_count > 0) ? 0 : -ENOLINK;
+}
+
+/*
+ * Calcuate if given cfg can meet the mode request.
+ * Return 0 if mode is possible, -1 otherwise
+ */
+static int tegra_dc_dp_calc_config(struct tegra_dp_priv *dp,
+ const struct display_timing *timing,
+ struct tegra_dp_link_config *link_cfg)
+{
+ const u32 link_rate = 27 * link_cfg->link_bw * 1000 * 1000;
+ const u64 f = 100000; /* precision factor */
+ u32 num_linkclk_line; /* Number of link clocks per line */
+ u64 ratio_f; /* Ratio of incoming to outgoing data rate */
+ u64 frac_f;
+ u64 activesym_f; /* Activesym per TU */
+ u64 activecount_f;
+ u32 activecount;
+ u32 activepolarity;
+ u64 approx_value_f;
+ u32 activefrac = 0;
+ u64 accumulated_error_f = 0;
+ u32 lowest_neg_activecount = 0;
+ u32 lowest_neg_activepolarity = 0;
+ u32 lowest_neg_tusize = 64;
+ u32 num_symbols_per_line;
+ u64 lowest_neg_activefrac = 0;
+ u64 lowest_neg_error_f = 64 * f;
+ u64 watermark_f;
+ int i;
+ int neg;
+
+ if (!link_rate || !link_cfg->lane_count || !timing->pixelclock.typ ||
+ !link_cfg->bits_per_pixel)
+ return -1;
+
+ if ((u64)timing->pixelclock.typ * link_cfg->bits_per_pixel >=
+ (u64)link_rate * 8 * link_cfg->lane_count)
+ return -1;
+
+ num_linkclk_line = (u32)(lldiv(link_rate * timing->hactive.typ,
+ timing->pixelclock.typ));
+
+ ratio_f = (u64)timing->pixelclock.typ * link_cfg->bits_per_pixel * f;
+ ratio_f /= 8;
+ do_div(ratio_f, link_rate * link_cfg->lane_count);
+
+ for (i = 64; i >= 32; --i) {
+ activesym_f = ratio_f * i;
+ activecount_f = lldiv(activesym_f, (u32)f) * f;
+ frac_f = activesym_f - activecount_f;
+ activecount = (u32)(lldiv(activecount_f, (u32)f));
+
+ if (frac_f < (lldiv(f, 2))) /* fraction < 0.5 */
+ activepolarity = 0;
+ else {
+ activepolarity = 1;
+ frac_f = f - frac_f;
+ }
+
+ if (frac_f != 0) {
+ /* warning: frac_f should be 64-bit */
+ frac_f = lldiv(f * f, frac_f); /* 1 / fraction */
+ if (frac_f > (15 * f))
+ activefrac = activepolarity ? 1 : 15;
+ else
+ activefrac = activepolarity ?
+ (u32)lldiv(frac_f, (u32)f) + 1 :
+ (u32)lldiv(frac_f, (u32)f);
+ }
+
+ if (activefrac == 1)
+ activepolarity = 0;
+
+ if (activepolarity == 1)
+ approx_value_f = activefrac ? lldiv(
+ (activecount_f + (activefrac * f - f) * f),
+ (activefrac * f)) :
+ activecount_f + f;
+ else
+ approx_value_f = activefrac ?
+ activecount_f + lldiv(f, activefrac) :
+ activecount_f;
+
+ if (activesym_f < approx_value_f) {
+ accumulated_error_f = num_linkclk_line *
+ lldiv(approx_value_f - activesym_f, i);
+ neg = 1;
+ } else {
+ accumulated_error_f = num_linkclk_line *
+ lldiv(activesym_f - approx_value_f, i);
+ neg = 0;
+ }
+
+ if ((neg && (lowest_neg_error_f > accumulated_error_f)) ||
+ (accumulated_error_f == 0)) {
+ lowest_neg_error_f = accumulated_error_f;
+ lowest_neg_tusize = i;
+ lowest_neg_activecount = activecount;
+ lowest_neg_activepolarity = activepolarity;
+ lowest_neg_activefrac = activefrac;
+
+ if (accumulated_error_f == 0)
+ break;
+ }
+ }
+
+ if (lowest_neg_activefrac == 0) {
+ link_cfg->activepolarity = 0;
+ link_cfg->active_count = lowest_neg_activepolarity ?
+ lowest_neg_activecount : lowest_neg_activecount - 1;
+ link_cfg->tu_size = lowest_neg_tusize;
+ link_cfg->active_frac = 1;
+ } else {
+ link_cfg->activepolarity = lowest_neg_activepolarity;
+ link_cfg->active_count = (u32)lowest_neg_activecount;
+ link_cfg->tu_size = lowest_neg_tusize;
+ link_cfg->active_frac = (u32)lowest_neg_activefrac;
+ }
+
+ watermark_f = lldiv(ratio_f * link_cfg->tu_size * (f - ratio_f), f);
+ link_cfg->watermark = (u32)(lldiv(watermark_f + lowest_neg_error_f,
+ f)) + link_cfg->bits_per_pixel / 4 - 1;
+ num_symbols_per_line = (timing->hactive.typ *
+ link_cfg->bits_per_pixel) /
+ (8 * link_cfg->lane_count);
+
+ if (link_cfg->watermark > 30) {
+ debug("dp: sor setting: unable to get a good tusize, force watermark to 30\n");
+ link_cfg->watermark = 30;
+ return -1;
+ } else if (link_cfg->watermark > num_symbols_per_line) {
+ debug("dp: sor setting: force watermark to the number of symbols in the line\n");
+ link_cfg->watermark = num_symbols_per_line;
+ return -1;
+ }
+
+ /*
+ * Refer to dev_disp.ref for more information.
+ * # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width -
+ * SetRasterBlankStart.X - 7) * link_clk / pclk)
+ * - 3 * enhanced_framing - Y
+ * where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12
+ */
+ link_cfg->hblank_sym = (int)lldiv(((uint64_t)timing->hback_porch.typ +
+ timing->hfront_porch.typ + timing->hsync_len.typ - 7) *
+ link_rate, timing->pixelclock.typ) -
+ 3 * link_cfg->enhanced_framing -
+ (12 / link_cfg->lane_count);
+
+ if (link_cfg->hblank_sym < 0)
+ link_cfg->hblank_sym = 0;
+
+
+ /*
+ * Refer to dev_disp.ref for more information.
+ * # symbols/vblank = ((SetRasterBlankStart.X -
+ * SetRasterBlankEen.X - 25) * link_clk / pclk)
+ * - Y - 1;
+ * where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39
+ */
+ link_cfg->vblank_sym = (int)lldiv(((uint64_t)timing->hactive.typ - 25)
+ * link_rate, timing->pixelclock.typ) - (36 /
+ link_cfg->lane_count) - 4;
+
+ if (link_cfg->vblank_sym < 0)
+ link_cfg->vblank_sym = 0;
+
+ link_cfg->is_valid = 1;
+#ifdef DEBUG
+ tegra_dc_dp_dump_link_cfg(dp, link_cfg);
+#endif
+
+ return 0;
+}
+
+static int tegra_dc_dp_init_max_link_cfg(
+ const struct display_timing *timing,
+ struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg)
+{
+ const int drive_current = 0x40404040;
+ const int preemphasis = 0x0f0f0f0f;
+ const int postcursor = 0;
+ u8 dpcd_data;
+ int ret;
+
+ ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LANE_COUNT, &dpcd_data);
+ if (ret)
+ return ret;
+ link_cfg->max_lane_count = dpcd_data & DP_MAX_LANE_COUNT_MASK;
+ link_cfg->tps3_supported = (dpcd_data &
+ DP_MAX_LANE_COUNT_TPS3_SUPPORTED_YES) ? 1 : 0;
+
+ link_cfg->support_enhanced_framing =
+ (dpcd_data & DP_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ?
+ 1 : 0;
+
+ ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_DOWNSPREAD, &dpcd_data);
+ if (ret)
+ return ret;
+ link_cfg->downspread = (dpcd_data & DP_MAX_DOWNSPREAD_VAL_0_5_PCT) ?
+ 1 : 0;
+
+ ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_TRAINING_AUX_RD_INTERVAL,
+ &link_cfg->aux_rd_interval);
+ if (ret)
+ return ret;
+ ret = tegra_dc_dp_dpcd_read(dp, DP_MAX_LINK_RATE,
+ &link_cfg->max_link_bw);
+ if (ret)
+ return ret;
+
+ /*
+ * Set to a high value for link training and attach.
+ * Will be re-programmed when dp is enabled.
+ */
+ link_cfg->drive_current = drive_current;
+ link_cfg->preemphasis = preemphasis;
+ link_cfg->postcursor = postcursor;
+
+ ret = tegra_dc_dp_dpcd_read(dp, DP_EDP_CONFIGURATION_CAP, &dpcd_data);
+ if (ret)
+ return ret;
+
+ link_cfg->alt_scramber_reset_cap =
+ (dpcd_data & DP_EDP_CONFIGURATION_CAP_ASC_RESET_YES) ?
+ 1 : 0;
+ link_cfg->only_enhanced_framing =
+ (dpcd_data & DP_EDP_CONFIGURATION_CAP_FRAMING_CHANGE_YES) ?
+ 1 : 0;
+
+ link_cfg->lane_count = link_cfg->max_lane_count;
+ link_cfg->link_bw = link_cfg->max_link_bw;
+ link_cfg->enhanced_framing = link_cfg->support_enhanced_framing;
+ link_cfg->frame_in_ms = (1000 / 60) + 1;
+
+ tegra_dc_dp_calc_config(dp, timing, link_cfg);
+ return 0;
+}
+
+static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp,
+ struct tegra_dc_sor_data *sor, int ena)
+{
+ int ret;
+
+ u8 dpcd_data = ena ?
+ DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_ENABLE :
+ DP_MAIN_LINK_CHANNEL_CODING_SET_ASC_RESET_DISABLE;
+
+ ret = tegra_dc_dp_dpcd_write(dp, DP_EDP_CONFIGURATION_SET,
+ dpcd_data);
+ if (ret)
+ return ret;
+
+ /* Also reset the scrambler to 0xfffe */
+ tegra_dc_sor_set_internal_panel(sor, ena);
+ return 0;
+}
+
+static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp,
+ struct tegra_dc_sor_data *sor,
+ u8 link_bw)
+{
+ tegra_dc_sor_set_link_bandwidth(sor, link_bw);
+
+ /* Sink side */
+ return tegra_dc_dp_dpcd_write(dp, DP_LINK_BW_SET, link_bw);
+}
+
+static int tegra_dp_set_lane_count(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg,
+ struct tegra_dc_sor_data *sor)
+{
+ u8 dpcd_data;
+ int ret;
+
+ /* check if panel support enhanched_framing */
+ dpcd_data = link_cfg->lane_count;
+ if (link_cfg->enhanced_framing)
+ dpcd_data |= DP_LANE_COUNT_SET_ENHANCEDFRAMING_T;
+ ret = tegra_dc_dp_dpcd_write(dp, DP_LANE_COUNT_SET, dpcd_data);
+ if (ret)
+ return ret;
+
+ tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
+
+ /* Also power down lanes that will not be used */
+ return 0;
+}
+
+static int tegra_dc_dp_link_trained(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 lane;
+ u8 mask;
+ u8 data;
+ int ret;
+
+ for (lane = 0; lane < cfg->lane_count; ++lane) {
+ ret = tegra_dc_dp_dpcd_read(dp, (lane / 2) ?
+ DP_LANE2_3_STATUS : DP_LANE0_1_STATUS,
+ &data);
+ if (ret)
+ return ret;
+ mask = (lane & 1) ?
+ NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES |
+ NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES |
+ NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES :
+ DP_LANE_CR_DONE |
+ DP_LANE_CHANNEL_EQ_DONE |
+ DP_LANE_SYMBOL_LOCKED;
+ if ((data & mask) != mask)
+ return -1;
+ }
+ return 0;
+}
+
+static int tegra_dp_channel_eq_status(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 cnt;
+ u32 n_lanes = cfg->lane_count;
+ u8 data;
+ u8 ce_done = 1;
+ int ret;
+
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ ret = tegra_dc_dp_dpcd_read(dp, DP_LANE0_1_STATUS + cnt, &data);
+ if (ret)
+ return ret;
+
+ if (n_lanes == 1) {
+ ce_done = (data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) &&
+ (data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT));
+ break;
+ } else if (!(data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) ||
+ !(data & (0x1 <<
+ NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)) ||
+ !(data & (0x1 <<
+ NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT)) ||
+ !(data & (0x1 <<
+ NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT)))
+ return -EIO;
+ }
+
+ if (ce_done) {
+ ret = tegra_dc_dp_dpcd_read(dp,
+ DP_LANE_ALIGN_STATUS_UPDATED,
+ &data);
+ if (ret)
+ return ret;
+ if (!(data & NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES))
+ ce_done = 0;
+ }
+
+ return ce_done ? 0 : -EIO;
+}
+
+static int tegra_dp_clock_recovery_status(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 cnt;
+ u32 n_lanes = cfg->lane_count;
+ u8 data_ptr;
+ int ret;
+
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ ret = tegra_dc_dp_dpcd_read(dp, (DP_LANE0_1_STATUS + cnt),
+ &data_ptr);
+ if (ret)
+ return ret;
+
+ if (n_lanes == 1)
+ return (data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) ?
+ 1 : 0;
+ else if (!(data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) ||
+ !(data_ptr & (NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES)))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int tegra_dp_lt_adjust(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4],
+ u32 pc[4], u8 pc_supported,
+ const struct tegra_dp_link_config *cfg)
+{
+ size_t cnt;
+ u8 data_ptr;
+ u32 n_lanes = cfg->lane_count;
+ int ret;
+
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ ret = tegra_dc_dp_dpcd_read(dp, DP_ADJUST_REQUEST_LANE0_1 + cnt,
+ &data_ptr);
+ if (ret)
+ return ret;
+ pe[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_PE_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT;
+ vs[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_DC_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT;
+ pe[1 + 2 * cnt] =
+ (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT;
+ vs[1 + 2 * cnt] =
+ (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK) >>
+ NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT;
+ }
+ if (pc_supported) {
+ ret = tegra_dc_dp_dpcd_read(dp, NV_DPCD_ADJUST_REQ_POST_CURSOR2,
+ &data_ptr);
+ if (ret)
+ return ret;
+ for (cnt = 0; cnt < n_lanes; cnt++) {
+ pc[cnt] = (data_ptr >>
+ NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(cnt)) &
+ NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK;
+ }
+ }
+
+ return 0;
+}
+
+static void tegra_dp_wait_aux_training(struct tegra_dp_priv *dp,
+ bool is_clk_recovery,
+ const struct tegra_dp_link_config *cfg)
+{
+ if (!cfg->aux_rd_interval)
+ udelay(is_clk_recovery ? 200 : 500);
+ else
+ mdelay(cfg->aux_rd_interval * 4);
+}
+
+static void tegra_dp_tpg(struct tegra_dp_priv *dp, u32 tp, u32 n_lanes,
+ const struct tegra_dp_link_config *cfg)
+{
+ u8 data = (tp == training_pattern_disabled)
+ ? (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F)
+ : (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T);
+
+ tegra_dc_sor_set_dp_linkctl(dp->sor, 1, tp, cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, data);
+}
+
+static int tegra_dp_link_config(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u8 dpcd_data;
+ u32 retry;
+ int ret;
+
+ if (link_cfg->lane_count == 0) {
+ debug("dp: error: lane count is 0. Can not set link config.\n");
+ return -ENOLINK;
+ }
+
+ /* Set power state if it is not in normal level */
+ ret = tegra_dc_dp_dpcd_read(dp, DP_SET_POWER, &dpcd_data);
+ if (ret)
+ return ret;
+
+ if (dpcd_data == DP_SET_POWER_D3) {
+ dpcd_data = DP_SET_POWER_D0;
+
+ /* DP spec requires 3 retries */
+ for (retry = 3; retry > 0; --retry) {
+ ret = tegra_dc_dp_dpcd_write(dp, DP_SET_POWER,
+ dpcd_data);
+ if (!ret)
+ break;
+ if (retry == 1) {
+ debug("dp: Failed to set DP panel power\n");
+ return ret;
+ }
+ }
+ }
+
+ /* Enable ASSR if possible */
+ if (link_cfg->alt_scramber_reset_cap) {
+ ret = tegra_dc_dp_set_assr(dp, dp->sor, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = tegra_dp_set_link_bandwidth(dp, dp->sor, link_cfg->link_bw);
+ if (ret) {
+ debug("dp: Failed to set link bandwidth\n");
+ return ret;
+ }
+ ret = tegra_dp_set_lane_count(dp, link_cfg, dp->sor);
+ if (ret) {
+ debug("dp: Failed to set lane count\n");
+ return ret;
+ }
+ tegra_dc_sor_set_dp_linkctl(dp->sor, 1, training_pattern_none,
+ link_cfg);
+
+ return 0;
+}
+
+static int tegra_dp_lower_link_config(struct tegra_dp_priv *dp,
+ const struct display_timing *timing,
+ struct tegra_dp_link_config *cfg)
+{
+ struct tegra_dp_link_config tmp_cfg;
+ int ret;
+
+ tmp_cfg = *cfg;
+ cfg->is_valid = 0;
+
+ ret = _tegra_dp_lower_link_config(dp, cfg);
+ if (!ret)
+ ret = tegra_dc_dp_calc_config(dp, timing, cfg);
+ if (!ret)
+ ret = tegra_dp_link_config(dp, cfg);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ *cfg = tmp_cfg;
+ tegra_dp_link_config(dp, &tmp_cfg);
+ return ret;
+}
+
+static int tegra_dp_lt_config(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4],
+ u32 pc[4], const struct tegra_dp_link_config *cfg)
+{
+ struct tegra_dc_sor_data *sor = dp->sor;
+ u32 n_lanes = cfg->lane_count;
+ u8 pc_supported = cfg->tps3_supported;
+ u32 cnt;
+ u32 val;
+
+ for (cnt = 0; cnt < n_lanes; cnt++) {
+ u32 mask = 0;
+ u32 pe_reg, vs_reg, pc_reg;
+ u32 shift = 0;
+
+ switch (cnt) {
+ case 0:
+ mask = PR_LANE2_DP_LANE0_MASK;
+ shift = PR_LANE2_DP_LANE0_SHIFT;
+ break;
+ case 1:
+ mask = PR_LANE1_DP_LANE1_MASK;
+ shift = PR_LANE1_DP_LANE1_SHIFT;
+ break;
+ case 2:
+ mask = PR_LANE0_DP_LANE2_MASK;
+ shift = PR_LANE0_DP_LANE2_SHIFT;
+ break;
+ case 3:
+ mask = PR_LANE3_DP_LANE3_MASK;
+ shift = PR_LANE3_DP_LANE3_SHIFT;
+ break;
+ default:
+ debug("dp: incorrect lane cnt\n");
+ return -EINVAL;
+ }
+
+ pe_reg = tegra_dp_pe_regs[pc[cnt]][vs[cnt]][pe[cnt]];
+ vs_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]];
+ pc_reg = tegra_dp_pc_regs[pc[cnt]][vs[cnt]][pe[cnt]];
+
+ tegra_dp_set_pe_vs_pc(sor, mask, pe_reg << shift,
+ vs_reg << shift, pc_reg << shift,
+ pc_supported);
+ }
+
+ tegra_dp_disable_tx_pu(dp->sor);
+ udelay(20);
+
+ for (cnt = 0; cnt < n_lanes; cnt++) {
+ u32 max_vs_flag = tegra_dp_is_max_vs(pe[cnt], vs[cnt]);
+ u32 max_pe_flag = tegra_dp_is_max_pe(pe[cnt], vs[cnt]);
+
+ val = (vs[cnt] << NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT) |
+ (max_vs_flag ?
+ NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T :
+ NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F) |
+ (pe[cnt] << NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT) |
+ (max_pe_flag ?
+ NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T :
+ NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F);
+ tegra_dc_dp_dpcd_write(dp, (DP_TRAINING_LANE0_SET + cnt), val);
+ }
+
+ if (pc_supported) {
+ for (cnt = 0; cnt < n_lanes / 2; cnt++) {
+ u32 max_pc_flag0 = tegra_dp_is_max_pc(pc[cnt]);
+ u32 max_pc_flag1 = tegra_dp_is_max_pc(pc[cnt + 1]);
+ val = (pc[cnt] << NV_DPCD_LANEX_SET2_PC2_SHIFT) |
+ (max_pc_flag0 ?
+ NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T :
+ NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F) |
+ (pc[cnt + 1] <<
+ NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT) |
+ (max_pc_flag1 ?
+ NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T :
+ NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F);
+ tegra_dc_dp_dpcd_write(dp,
+ NV_DPCD_TRAINING_LANE0_1_SET2 +
+ cnt, val);
+ }
+ }
+
+ return 0;
+}
+
+static int _tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4],
+ u32 vs[4], u32 pc[4], u8 pc_supported,
+ u32 n_lanes,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 retry_cnt;
+
+ for (retry_cnt = 0; retry_cnt < 4; retry_cnt++) {
+ int ret;
+
+ if (retry_cnt) {
+ ret = tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported,
+ cfg);
+ if (ret)
+ return ret;
+ tegra_dp_lt_config(dp, pe, vs, pc, cfg);
+ }
+
+ tegra_dp_wait_aux_training(dp, false, cfg);
+
+ if (!tegra_dp_clock_recovery_status(dp, cfg)) {
+ debug("dp: CR failed in channel EQ sequence!\n");
+ break;
+ }
+
+ if (!tegra_dp_channel_eq_status(dp, cfg))
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static int tegra_dp_channel_eq(struct tegra_dp_priv *dp, u32 pe[4], u32 vs[4],
+ u32 pc[4],
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 n_lanes = cfg->lane_count;
+ u8 pc_supported = cfg->tps3_supported;
+ int ret;
+ u32 tp_src = training_pattern_2;
+
+ if (pc_supported)
+ tp_src = training_pattern_3;
+
+ tegra_dp_tpg(dp, tp_src, n_lanes, cfg);
+
+ ret = _tegra_dp_channel_eq(dp, pe, vs, pc, pc_supported, n_lanes, cfg);
+
+ tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg);
+
+ return ret;
+}
+
+static int _tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4],
+ u32 vs[4], u32 pc[4], u8 pc_supported,
+ u32 n_lanes,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 vs_temp[4];
+ u32 retry_cnt = 0;
+
+ do {
+ tegra_dp_lt_config(dp, pe, vs, pc, cfg);
+ tegra_dp_wait_aux_training(dp, true, cfg);
+
+ if (tegra_dp_clock_recovery_status(dp, cfg))
+ return 0;
+
+ memcpy(vs_temp, vs, sizeof(vs_temp));
+ tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported, cfg);
+
+ if (memcmp(vs_temp, vs, sizeof(vs_temp)))
+ retry_cnt = 0;
+ else
+ ++retry_cnt;
+ } while (retry_cnt < 5);
+
+ return -EIO;
+}
+
+static int tegra_dp_clk_recovery(struct tegra_dp_priv *dp, u32 pe[4],
+ u32 vs[4], u32 pc[4],
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 n_lanes = cfg->lane_count;
+ u8 pc_supported = cfg->tps3_supported;
+ int err;
+
+ tegra_dp_tpg(dp, training_pattern_1, n_lanes, cfg);
+
+ err = _tegra_dp_clk_recovery(dp, pe, vs, pc, pc_supported, n_lanes,
+ cfg);
+ if (err < 0)
+ tegra_dp_tpg(dp, training_pattern_disabled, n_lanes, cfg);
+
+ return err;
+}
+
+static int tegra_dc_dp_full_link_training(struct tegra_dp_priv *dp,
+ const struct display_timing *timing,
+ struct tegra_dp_link_config *cfg)
+{
+ struct tegra_dc_sor_data *sor = dp->sor;
+ int err;
+ u32 pe[4], vs[4], pc[4];
+
+ tegra_sor_precharge_lanes(sor, cfg);
+
+retry_cr:
+ memset(pe, PREEMPHASIS_DISABLED, sizeof(pe));
+ memset(vs, DRIVECURRENT_LEVEL0, sizeof(vs));
+ memset(pc, POSTCURSOR2_LEVEL0, sizeof(pc));
+
+ err = tegra_dp_clk_recovery(dp, pe, vs, pc, cfg);
+ if (err) {
+ if (!tegra_dp_lower_link_config(dp, timing, cfg))
+ goto retry_cr;
+
+ debug("dp: clk recovery failed\n");
+ goto fail;
+ }
+
+ err = tegra_dp_channel_eq(dp, pe, vs, pc, cfg);
+ if (err) {
+ if (!tegra_dp_lower_link_config(dp, timing, cfg))
+ goto retry_cr;
+
+ debug("dp: channel equalization failed\n");
+ goto fail;
+ }
+#ifdef DEBUG
+ tegra_dc_dp_dump_link_cfg(dp, cfg);
+#endif
+ return 0;
+
+fail:
+ return err;
+}
+
+/*
+ * All link training functions are ported from kernel dc driver.
+ * See more details at drivers/video/tegra/dc/dp.c
+ */
+static int tegra_dc_dp_fast_link_training(struct tegra_dp_priv *dp,
+ const struct tegra_dp_link_config *link_cfg,
+ struct tegra_dc_sor_data *sor)
+{
+ u8 link_bw;
+ u8 lane_count;
+ u16 data16;
+ u32 data32;
+ u32 size;
+ u32 status;
+ int j;
+ u32 mask = 0xffff >> ((4 - link_cfg->lane_count) * 4);
+
+ tegra_dc_sor_set_lane_parm(sor, link_cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_MAIN_LINK_CHANNEL_CODING_SET,
+ DP_SET_ANSI_8B10B);
+
+ /* Send TP1 */
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_1, link_cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_1);
+
+ for (j = 0; j < link_cfg->lane_count; ++j)
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24);
+ udelay(520);
+
+ size = sizeof(data16);
+ tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD,
+ DP_LANE0_1_STATUS, (u8 *)&data16, &size, &status);
+ status = mask & 0x1111;
+ if ((data16 & status) != status) {
+ debug("dp: Link training error for TP1 (%#x, status %#x)\n",
+ data16, status);
+ return -EFAULT;
+ }
+
+ /* enable ASSR */
+ tegra_dc_dp_set_assr(dp, sor, link_cfg->scramble_ena);
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_3, link_cfg);
+
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET,
+ link_cfg->link_bw == 20 ? 0x23 : 0x22);
+ for (j = 0; j < link_cfg->lane_count; ++j)
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_LANE0_SET + j, 0x24);
+ udelay(520);
+
+ size = sizeof(data32);
+ tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, DP_LANE0_1_STATUS,
+ (u8 *)&data32, &size, &status);
+ if ((data32 & mask) != (0x7777 & mask)) {
+ debug("dp: Link training error for TP2/3 (0x%x)\n", data32);
+ return -EFAULT;
+ }
+
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_disabled,
+ link_cfg);
+ tegra_dc_dp_dpcd_write(dp, DP_TRAINING_PATTERN_SET, 0);
+
+ if (tegra_dc_dp_link_trained(dp, link_cfg)) {
+ tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count);
+ debug("Fast link training failed, link bw %d, lane # %d\n",
+ link_bw, lane_count);
+ return -EFAULT;
+ }
+
+ debug("Fast link training succeeded, link bw %d, lane %d\n",
+ link_cfg->link_bw, link_cfg->lane_count);
+
+ return 0;
+}
+
+static int tegra_dp_do_link_training(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing,
+ struct tegra_dc_sor_data *sor)
+{
+ u8 link_bw;
+ u8 lane_count;
+ int ret;
+
+ if (DO_FAST_LINK_TRAINING) {
+ ret = tegra_dc_dp_fast_link_training(dp, link_cfg, sor);
+ if (ret) {
+ debug("dp: fast link training failed\n");
+ } else {
+ /*
+ * set to a known-good drive setting if fast link
+ * succeeded. Ignore any error.
+ */
+ ret = tegra_dc_sor_set_voltage_swing(dp->sor, link_cfg);
+ if (ret)
+ debug("Failed to set voltage swing\n");
+ }
+ } else {
+ ret = -ENOSYS;
+ }
+ if (ret) {
+ /* Try full link training then */
+ ret = tegra_dc_dp_full_link_training(dp, timing, link_cfg);
+ if (ret) {
+ debug("dp: full link training failed\n");
+ return ret;
+ }
+ }
+
+ /* Everything is good; double check the link config */
+ tegra_dc_sor_read_link_config(sor, &link_bw, &lane_count);
+
+ if ((link_cfg->link_bw == link_bw) &&
+ (link_cfg->lane_count == lane_count))
+ return 0;
+ else
+ return -EFAULT;
+}
+
+static int tegra_dc_dp_explore_link_cfg(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg,
+ struct tegra_dc_sor_data *sor,
+ const struct display_timing *timing)
+{
+ struct tegra_dp_link_config temp_cfg;
+
+ if (!timing->pixelclock.typ || !timing->hactive.typ ||
+ !timing->vactive.typ) {
+ debug("dp: error mode configuration");
+ return -EINVAL;
+ }
+ if (!link_cfg->max_link_bw || !link_cfg->max_lane_count) {
+ debug("dp: error link configuration");
+ return -EINVAL;
+ }
+
+ link_cfg->is_valid = 0;
+
+ memcpy(&temp_cfg, link_cfg, sizeof(temp_cfg));
+
+ temp_cfg.link_bw = temp_cfg.max_link_bw;
+ temp_cfg.lane_count = temp_cfg.max_lane_count;
+
+ /*
+ * set to max link config
+ */
+ if ((!tegra_dc_dp_calc_config(dp, timing, &temp_cfg)) &&
+ (!tegra_dp_link_config(dp, &temp_cfg)) &&
+ (!tegra_dp_do_link_training(dp, &temp_cfg, timing, sor)))
+ /* the max link cfg is doable */
+ memcpy(link_cfg, &temp_cfg, sizeof(temp_cfg));
+
+ return link_cfg->is_valid ? 0 : -EFAULT;
+}
+
+static int tegra_dp_hpd_plug(struct tegra_dp_priv *dp)
+{
+ const int vdd_to_hpd_delay_ms = 200;
+ u32 val;
+ ulong start;
+
+ start = get_timer(0);
+ do {
+ val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT);
+ if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)
+ return 0;
+ udelay(100);
+ } while (get_timer(start) < vdd_to_hpd_delay_ms);
+
+ return -EIO;
+}
+
+static int tegra_dc_dp_sink_out_of_sync(struct tegra_dp_priv *dp, u32 delay_ms)
+{
+ u8 dpcd_data;
+ int out_of_sync;
+ int ret;
+
+ debug("%s: delay=%d\n", __func__, delay_ms);
+ mdelay(delay_ms);
+ ret = tegra_dc_dp_dpcd_read(dp, DP_SINK_STATUS, &dpcd_data);
+ if (ret)
+ return ret;
+
+ out_of_sync = !(dpcd_data & DP_SINK_STATUS_PORT0_IN_SYNC);
+ if (out_of_sync)
+ debug("SINK receive port 0 out of sync, data=%x\n", dpcd_data);
+ else
+ debug("SINK is in synchronization\n");
+
+ return out_of_sync;
+}
+
+static int tegra_dc_dp_check_sink(struct tegra_dp_priv *dp,
+ struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing)
+{
+ const int max_retry = 5;
+ int delay_frame;
+ int retries;
+
+ /*
+ * DP TCON may skip some main stream frames, thus we need to wait
+ * some delay before reading the DPCD SINK STATUS register, starting
+ * from 5
+ */
+ delay_frame = 5;
+
+ retries = max_retry;
+ do {
+ int ret;
+
+ if (!tegra_dc_dp_sink_out_of_sync(dp, link_cfg->frame_in_ms *
+ delay_frame))
+ return 0;
+
+ debug("%s: retries left %d\n", __func__, retries);
+ if (!retries--) {
+ printf("DP: Out of sync after %d retries\n", max_retry);
+ return -EIO;
+ }
+ ret = tegra_dc_sor_detach(dp->sor);
+ if (ret)
+ return ret;
+ if (tegra_dc_dp_explore_link_cfg(dp, link_cfg, dp->sor,
+ timing)) {
+ debug("dp: %s: error to configure link\n", __func__);
+ continue;
+ }
+
+ tegra_dc_sor_set_power_state(dp->sor, 1);
+ tegra_dc_sor_attach(dp->sor, link_cfg, timing);
+
+ /* Increase delay_frame for next try in case the sink is
+ skipping more frames */
+ delay_frame += 10;
+ } while (1);
+}
+
+int tegra_dp_enable(struct udevice *dev, int panel_bpp,
+ const struct display_timing *timing)
+{
+ struct tegra_dp_priv *priv = dev_get_priv(dev);
+ struct tegra_dp_link_config slink_cfg, *link_cfg = &slink_cfg;
+ struct tegra_dc_sor_data *sor;
+ int data;
+ int retry;
+ int ret;
+
+ memset(link_cfg, '\0', sizeof(*link_cfg));
+ link_cfg->is_valid = 0;
+ link_cfg->scramble_ena = 1;
+
+ tegra_dc_dpaux_enable(priv);
+
+ if (tegra_dp_hpd_plug(priv) < 0) {
+ debug("dp: hpd plug failed\n");
+ return -EIO;
+ }
+
+ link_cfg->bits_per_pixel = panel_bpp;
+ if (tegra_dc_dp_init_max_link_cfg(timing, priv, link_cfg)) {
+ debug("dp: failed to init link configuration\n");
+ return -ENOLINK;
+ }
+
+ ret = tegra_dc_sor_init(&sor);
+ if (ret)
+ return ret;
+ priv->sor = sor;
+ ret = tegra_dc_sor_enable_dp(sor, link_cfg);
+ if (ret)
+ return ret;
+
+ tegra_dc_sor_set_panel_power(sor, 1);
+
+ /* Write power on to DPCD */
+ data = DP_SET_POWER_D0;
+ retry = 0;
+ do {
+ ret = tegra_dc_dp_dpcd_write(priv, DP_SET_POWER, data);
+ } while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret);
+
+ if (ret || retry >= DP_POWER_ON_MAX_TRIES) {
+ debug("dp: failed to power on panel (0x%x)\n", ret);
+ return -ENETUNREACH;
+ goto error_enable;
+ }
+
+ /* Confirm DP plugging status */
+ if (!(tegra_dpaux_readl(priv, DPAUX_DP_AUXSTAT) &
+ DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) {
+ debug("dp: could not detect HPD\n");
+ return -ENXIO;
+ }
+
+ /* Check DP version */
+ if (tegra_dc_dp_dpcd_read(priv, DP_DPCD_REV, &priv->revision)) {
+ debug("dp: failed to read the revision number from sink\n");
+ return -EIO;
+ }
+
+ if (tegra_dc_dp_explore_link_cfg(priv, link_cfg, sor, timing)) {
+ debug("dp: error configuring link\n");
+ return -ENOMEDIUM;
+ }
+
+ tegra_dc_sor_set_power_state(sor, 1);
+ ret = tegra_dc_sor_attach(sor, link_cfg, timing);
+ if (ret && ret != -EEXIST)
+ return ret;
+
+ /*
+ * This takes a long time, but can apparently resolve a failure to
+ * bring up the display correctly.
+ */
+ if (0) {
+ ret = tegra_dc_dp_check_sink(priv, link_cfg, timing);
+ if (ret)
+ return ret;
+ }
+
+ /* Power down the unused lanes to save power - a few hundred mW */
+ tegra_dc_sor_power_down_unused_lanes(sor, link_cfg);
+
+ priv->enabled = true;
+error_enable:
+ return 0;
+}
+
+static int tegra_dp_ofdata_to_platdata(struct udevice *dev)
+{
+ struct tegra_dp_plat *plat = dev_get_platdata(dev);
+ const void *blob = gd->fdt_blob;
+
+ plat->base = fdtdec_get_addr(blob, dev->of_offset, "reg");
+
+ return 0;
+}
+
+static int tegra_dp_read_edid(struct udevice *dev, u8 *buf, int buf_size)
+{
+ struct tegra_dp_priv *priv = dev_get_priv(dev);
+ const int tegra_edid_i2c_address = 0x50;
+ u32 aux_stat = 0;
+
+ tegra_dc_dpaux_enable(priv);
+
+ return tegra_dc_i2c_aux_read(priv, tegra_edid_i2c_address, 0, buf,
+ buf_size, &aux_stat);
+}
+
+static const struct dm_display_port_ops dp_tegra_ops = {
+ .read_edid = tegra_dp_read_edid,
+ .enable = tegra_dp_enable,
+};
+
+static int dp_tegra_probe(struct udevice *dev)
+{
+ struct tegra_dp_plat *plat = dev_get_platdata(dev);
+ struct tegra_dp_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct dpaux_ctlr *)plat->base;
+ priv->enabled = false;
+
+ return 0;
+}
+
+static const struct udevice_id tegra_dp_ids[] = {
+ { .compatible = "nvidia,tegra124-dpaux" },
+ { }
+};
+
+U_BOOT_DRIVER(dp_tegra) = {
+ .name = "dpaux_tegra",
+ .id = UCLASS_DISPLAY_PORT,
+ .of_match = tegra_dp_ids,
+ .ofdata_to_platdata = tegra_dp_ofdata_to_platdata,
+ .probe = dp_tegra_probe,
+ .ops = &dp_tegra_ops,
+ .priv_auto_alloc_size = sizeof(struct tegra_dp_priv),
+ .platdata_auto_alloc_size = sizeof(struct tegra_dp_plat),
+};
diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra124/sor.c
new file mode 100644
index 0000000..aa3d80c
--- /dev/null
+++ b/drivers/video/tegra124/sor.c
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch-tegra/dc.h>
+#include "displayport.h"
+#include "sor.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DEBUG_SOR 0
+
+#define APBDEV_PMC_DPD_SAMPLE 0x20
+#define APBDEV_PMC_DPD_SAMPLE_ON_DISABLE 0
+#define APBDEV_PMC_DPD_SAMPLE_ON_ENABLE 1
+#define APBDEV_PMC_SEL_DPD_TIM 0x1c8
+#define APBDEV_PMC_SEL_DPD_TIM_SEL_DPD_TIM_DEFAULT 0x7f
+#define APBDEV_PMC_IO_DPD2_REQ 0x1c0
+#define APBDEV_PMC_IO_DPD2_REQ_LVDS_SHIFT 25
+#define APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF (0 << 25)
+#define APBDEV_PMC_IO_DPD2_REQ_LVDS_ON (1 << 25)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_SHIFT 30
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK (0x3 << 30)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_IDLE (0 << 30)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF (1 << 30)
+#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON (2 << 30)
+#define APBDEV_PMC_IO_DPD2_STATUS 0x1c4
+#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_SHIFT 25
+#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF (0 << 25)
+#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON (1 << 25)
+
+static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
+{
+ return readl((u32 *)sor->base + reg);
+}
+
+static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor, u32 reg,
+ u32 val)
+{
+ writel(val, (u32 *)sor->base + reg);
+}
+
+static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor,
+ u32 reg, u32 mask, u32 val)
+{
+ u32 reg_val = tegra_sor_readl(sor, reg);
+ reg_val &= ~mask;
+ reg_val |= val;
+ tegra_sor_writel(sor, reg, reg_val);
+}
+
+void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor)
+{
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE);
+}
+
+void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, u32 pe_reg,
+ u32 vs_reg, u32 pc_reg, u8 pc_supported)
+{
+ tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg);
+ tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg);
+ if (pc_supported) {
+ tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask,
+ pc_reg);
+ }
+}
+
+static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
+ u32 mask, u32 exp_val,
+ int poll_interval_us, int timeout_ms)
+{
+ u32 reg_val = 0;
+ ulong start;
+
+ start = get_timer(0);
+ do {
+ reg_val = tegra_sor_readl(sor, reg);
+ if (((reg_val & mask) == exp_val))
+ return 0;
+ udelay(poll_interval_us);
+ } while (get_timer(start) < timeout_ms);
+
+ debug("sor_poll_register 0x%x: timeout, (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n",
+ reg, reg_val, mask, exp_val);
+
+ return -ETIMEDOUT;
+}
+
+int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
+{
+ u32 reg_val;
+ u32 orig_val;
+
+ orig_val = tegra_sor_readl(sor, PWR);
+
+ reg_val = pu_pd ? PWR_NORMAL_STATE_PU :
+ PWR_NORMAL_STATE_PD; /* normal state only */
+
+ if (reg_val == orig_val)
+ return 0; /* No update needed */
+
+ reg_val |= PWR_SETTING_NEW_TRIGGER;
+ tegra_sor_writel(sor, PWR, reg_val);
+
+ /* Poll to confirm it is done */
+ if (tegra_dc_sor_poll_register(sor, PWR,
+ PWR_SETTING_NEW_DEFAULT_MASK,
+ PWR_SETTING_NEW_DONE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ debug("dc timeout waiting for SOR_PWR = NEW_DONE\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+ u8 training_pattern,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
+
+ if (ena)
+ reg_val |= DP_LINKCTL_ENABLE_YES;
+ else
+ reg_val &= DP_LINKCTL_ENABLE_NO;
+
+ reg_val &= ~DP_LINKCTL_TUSIZE_MASK;
+ reg_val |= (link_cfg->tu_size << DP_LINKCTL_TUSIZE_SHIFT);
+
+ if (link_cfg->enhanced_framing)
+ reg_val |= DP_LINKCTL_ENHANCEDFRAME_ENABLE;
+
+ tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
+
+ switch (training_pattern) {
+ case training_pattern_1:
+ tegra_sor_writel(sor, DP_TPG, 0x41414141);
+ break;
+ case training_pattern_2:
+ case training_pattern_3:
+ reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ?
+ 0x43434343 : 0x42424242;
+ tegra_sor_writel(sor, DP_TPG, reg_val);
+ break;
+ default:
+ tegra_sor_writel(sor, DP_TPG, 0x50505050);
+ break;
+ }
+}
+
+static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor,
+ int pu, int is_lvds)
+{
+ u32 reg_val;
+
+ /* SOR lane sequencer */
+ if (pu) {
+ reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
+ LANE_SEQ_CTL_SEQUENCE_DOWN |
+ LANE_SEQ_CTL_NEW_POWER_STATE_PU;
+ } else {
+ reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER |
+ LANE_SEQ_CTL_SEQUENCE_UP |
+ LANE_SEQ_CTL_NEW_POWER_STATE_PD;
+ }
+
+ if (is_lvds)
+ reg_val |= 15 << LANE_SEQ_CTL_DELAY_SHIFT;
+ else
+ reg_val |= 1 << LANE_SEQ_CTL_DELAY_SHIFT;
+
+ tegra_sor_writel(sor, LANE_SEQ_CTL, reg_val);
+
+ if (tegra_dc_sor_poll_register(sor, LANE_SEQ_CTL,
+ LANE_SEQ_CTL_SETTING_MASK,
+ LANE_SEQ_CTL_SETTING_NEW_DONE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ debug("dp: timeout while waiting for SOR lane sequencer to power down lanes\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
+ u32 lane_count, int pu)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
+
+ if (pu) {
+ switch (lane_count) {
+ case 4:
+ reg_val |= (DP_PADCTL_PD_TXD_3_NO |
+ DP_PADCTL_PD_TXD_2_NO);
+ /* fall through */
+ case 2:
+ reg_val |= DP_PADCTL_PD_TXD_1_NO;
+ case 1:
+ reg_val |= DP_PADCTL_PD_TXD_0_NO;
+ break;
+ default:
+ debug("dp: invalid lane number %d\n", lane_count);
+ return -1;
+ }
+
+ tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
+ tegra_dc_sor_set_lane_count(sor, lane_count);
+ }
+
+ return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0);
+}
+
+void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
+ int power_up)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
+
+ if (power_up)
+ reg_val |= DP_PADCTL_PAD_CAL_PD_POWERUP;
+ else
+ reg_val &= ~DP_PADCTL_PAD_CAL_PD_POWERUP;
+
+ tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
+}
+
+static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div,
+ u32 pwm_dutycycle)
+{
+ tegra_sor_writel(sor, PWM_DIV, pwm_div);
+ tegra_sor_writel(sor, PWM_CTL,
+ (pwm_dutycycle & PWM_CTL_DUTY_CYCLE_MASK) |
+ PWM_CTL_SETTING_NEW_TRIGGER);
+
+ if (tegra_dc_sor_poll_register(sor, PWM_CTL,
+ PWM_CTL_SETTING_NEW_SHIFT,
+ PWM_CTL_SETTING_NEW_DONE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ debug("dp: timeout while waiting for SOR PWM setting\n");
+ }
+}
+
+static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 reg_val;
+
+ tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
+
+ tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg);
+ reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum));
+ reg_val &= ~DP_CONFIG_WATERMARK_MASK;
+ reg_val |= link_cfg->watermark;
+ reg_val &= ~DP_CONFIG_ACTIVESYM_COUNT_MASK;
+ reg_val |= (link_cfg->active_count <<
+ DP_CONFIG_ACTIVESYM_COUNT_SHIFT);
+ reg_val &= ~DP_CONFIG_ACTIVESYM_FRAC_MASK;
+ reg_val |= (link_cfg->active_frac <<
+ DP_CONFIG_ACTIVESYM_FRAC_SHIFT);
+ if (link_cfg->activepolarity)
+ reg_val |= DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
+ else
+ reg_val &= ~DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE;
+ reg_val |= (DP_CONFIG_ACTIVESYM_CNTL_ENABLE |
+ DP_CONFIG_RD_RESET_VAL_NEGATIVE);
+
+ tegra_sor_writel(sor, DP_CONFIG(sor->portnum), reg_val);
+
+ /* program h/vblank sym */
+ tegra_sor_write_field(sor, DP_AUDIO_HBLANK_SYMBOLS,
+ DP_AUDIO_HBLANK_SYMBOLS_MASK,
+ link_cfg->hblank_sym);
+
+ tegra_sor_write_field(sor, DP_AUDIO_VBLANK_SYMBOLS,
+ DP_AUDIO_VBLANK_SYMBOLS_MASK,
+ link_cfg->vblank_sym);
+}
+
+static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor)
+{
+ tegra_sor_writel(sor, SUPER_STATE0, 0);
+ tegra_sor_writel(sor, SUPER_STATE0, 1);
+ tegra_sor_writel(sor, SUPER_STATE0, 0);
+}
+
+static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor)
+{
+ tegra_sor_writel(sor, STATE0, 0);
+ tegra_sor_writel(sor, STATE0, 1);
+ tegra_sor_writel(sor, STATE0, 0);
+}
+
+static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up)
+{
+ u32 reg_val;
+ void *pmc_base = sor->pmc_base;
+
+ if (up) {
+ writel(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE,
+ pmc_base + APBDEV_PMC_DPD_SAMPLE);
+ writel(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM);
+ }
+
+ reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_REQ);
+ reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON ||
+ APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK);
+
+ reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON |
+ APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF :
+ APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF |
+ APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON;
+
+ writel(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ);
+
+ /* Polling */
+ u32 temp = 10 * 1000;
+ do {
+ udelay(20);
+ reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_STATUS);
+ if (temp > 20)
+ temp -= 20;
+ else
+ break;
+ } while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0);
+
+ if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0) {
+ debug("PMC_IO_DPD2 polling failed (0x%x)\n", reg_val);
+ return -EIO;
+ }
+
+ if (up) {
+ writel(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE,
+ pmc_base + APBDEV_PMC_DPD_SAMPLE);
+ }
+
+ return 0;
+}
+
+void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum));
+ if (is_int)
+ reg_val |= DP_SPARE_PANEL_INTERNAL;
+ else
+ reg_val &= ~DP_SPARE_PANEL_INTERNAL;
+
+ reg_val |= DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK |
+ DP_SPARE_SEQ_ENABLE_YES;
+ tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val);
+}
+
+void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
+ u8 *lane_count)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, CLK_CNTRL);
+ *link_bw = (reg_val & CLK_CNTRL_DP_LINK_SPEED_MASK)
+ >> CLK_CNTRL_DP_LINK_SPEED_SHIFT;
+ reg_val = tegra_sor_readl(sor,
+ DP_LINKCTL(sor->portnum));
+
+ switch (reg_val & DP_LINKCTL_LANECOUNT_MASK) {
+ case DP_LINKCTL_LANECOUNT_ZERO:
+ *lane_count = 0;
+ break;
+ case DP_LINKCTL_LANECOUNT_ONE:
+ *lane_count = 1;
+ break;
+ case DP_LINKCTL_LANECOUNT_TWO:
+ *lane_count = 2;
+ break;
+ case DP_LINKCTL_LANECOUNT_FOUR:
+ *lane_count = 4;
+ break;
+ default:
+ printf("Unknown lane count\n");
+ }
+}
+
+void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw)
+{
+ tegra_sor_write_field(sor, CLK_CNTRL,
+ CLK_CNTRL_DP_LINK_SPEED_MASK,
+ link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT);
+}
+
+void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count)
+{
+ u32 reg_val;
+
+ reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
+ reg_val &= ~DP_LINKCTL_LANECOUNT_MASK;
+ switch (lane_count) {
+ case 0:
+ break;
+ case 1:
+ reg_val |= DP_LINKCTL_LANECOUNT_ONE;
+ break;
+ case 2:
+ reg_val |= DP_LINKCTL_LANECOUNT_TWO;
+ break;
+ case 4:
+ reg_val |= DP_LINKCTL_LANECOUNT_FOUR;
+ break;
+ default:
+ /* 0 should be handled earlier. */
+ printf("dp: Invalid lane count %d\n", lane_count);
+ return;
+ }
+ tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val);
+}
+
+/*
+ * The SOR power sequencer does not work for t124 so SW has to
+ * go through the power sequence manually
+ * Power up steps from spec:
+ * STEP PDPORT PDPLL PDBG PLLVCOD PLLCAPD E_DPD PDCAL
+ * 1 1 1 1 1 1 1 1
+ * 2 1 1 1 1 1 0 1
+ * 3 1 1 0 1 1 0 1
+ * 4 1 0 0 0 0 0 1
+ * 5 0 0 0 0 0 0 1
+ */
+static int tegra_dc_sor_power_up(struct tegra_dc_sor_data *sor, int is_lvds)
+{
+ int ret;
+
+ if (sor->power_is_up)
+ return 0;
+
+ /* Set link bw */
+ tegra_dc_sor_set_link_bandwidth(sor, is_lvds ?
+ CLK_CNTRL_DP_LINK_SPEED_LVDS :
+ CLK_CNTRL_DP_LINK_SPEED_G1_62);
+
+ /* step 1 */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */
+ PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */
+ PLL2_AUX7_PORT_POWERDOWN_ENABLE |
+ PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE |
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE);
+ tegra_sor_write_field(sor, PLL0, PLL0_PWR_MASK | /* PDPLL */
+ PLL0_VCOPD_MASK, /* PLLVCOPD */
+ PLL0_PWR_OFF | PLL0_VCOPD_ASSERT);
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */
+ DP_PADCTL_PAD_CAL_PD_POWERDOWN);
+
+ /* step 2 */
+ ret = tegra_dc_sor_io_set_dpd(sor, 1);
+ if (ret)
+ return ret;
+ udelay(15);
+
+ /* step 3 */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
+ PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
+ udelay(25);
+
+ /* step 4 */
+ tegra_sor_write_field(sor, PLL0,
+ PLL0_PWR_MASK | /* PDPLL */
+ PLL0_VCOPD_MASK, /* PLLVCOPD */
+ PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
+ /* PLLCAPD */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
+ udelay(225);
+
+ /* step 5 PDPORT */
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX7_PORT_POWERDOWN_MASK,
+ PLL2_AUX7_PORT_POWERDOWN_DISABLE);
+
+ sor->power_is_up = 1;
+
+ return 0;
+}
+
+#if DEBUG_SOR
+static void dump_sor_reg(struct tegra_dc_sor_data *sor)
+{
+#define DUMP_REG(a) printk(BIOS_INFO, "%-32s %03x %08x\n", \
+ #a, a, tegra_sor_readl(sor, a));
+
+ DUMP_REG(SUPER_STATE0);
+ DUMP_REG(SUPER_STATE1);
+ DUMP_REG(STATE0);
+ DUMP_REG(STATE1);
+ DUMP_REG(NV_HEAD_STATE0(0));
+ DUMP_REG(NV_HEAD_STATE0(1));
+ DUMP_REG(NV_HEAD_STATE1(0));
+ DUMP_REG(NV_HEAD_STATE1(1));
+ DUMP_REG(NV_HEAD_STATE2(0));
+ DUMP_REG(NV_HEAD_STATE2(1));
+ DUMP_REG(NV_HEAD_STATE3(0));
+ DUMP_REG(NV_HEAD_STATE3(1));
+ DUMP_REG(NV_HEAD_STATE4(0));
+ DUMP_REG(NV_HEAD_STATE4(1));
+ DUMP_REG(NV_HEAD_STATE5(0));
+ DUMP_REG(NV_HEAD_STATE5(1));
+ DUMP_REG(CRC_CNTRL);
+ DUMP_REG(CLK_CNTRL);
+ DUMP_REG(CAP);
+ DUMP_REG(PWR);
+ DUMP_REG(TEST);
+ DUMP_REG(PLL0);
+ DUMP_REG(PLL1);
+ DUMP_REG(PLL2);
+ DUMP_REG(PLL3);
+ DUMP_REG(CSTM);
+ DUMP_REG(LVDS);
+ DUMP_REG(CRCA);
+ DUMP_REG(CRCB);
+ DUMP_REG(SEQ_CTL);
+ DUMP_REG(LANE_SEQ_CTL);
+ DUMP_REG(SEQ_INST(0));
+ DUMP_REG(SEQ_INST(1));
+ DUMP_REG(SEQ_INST(2));
+ DUMP_REG(SEQ_INST(3));
+ DUMP_REG(SEQ_INST(4));
+ DUMP_REG(SEQ_INST(5));
+ DUMP_REG(SEQ_INST(6));
+ DUMP_REG(SEQ_INST(7));
+ DUMP_REG(SEQ_INST(8));
+ DUMP_REG(PWM_DIV);
+ DUMP_REG(PWM_CTL);
+ DUMP_REG(MSCHECK);
+ DUMP_REG(XBAR_CTRL);
+ DUMP_REG(DP_LINKCTL(0));
+ DUMP_REG(DP_LINKCTL(1));
+ DUMP_REG(DC(0));
+ DUMP_REG(DC(1));
+ DUMP_REG(LANE_DRIVE_CURRENT(0));
+ DUMP_REG(PR(0));
+ DUMP_REG(LANE4_PREEMPHASIS(0));
+ DUMP_REG(POSTCURSOR(0));
+ DUMP_REG(DP_CONFIG(0));
+ DUMP_REG(DP_CONFIG(1));
+ DUMP_REG(DP_MN(0));
+ DUMP_REG(DP_MN(1));
+ DUMP_REG(DP_PADCTL(0));
+ DUMP_REG(DP_PADCTL(1));
+ DUMP_REG(DP_DEBUG(0));
+ DUMP_REG(DP_DEBUG(1));
+ DUMP_REG(DP_SPARE(0));
+ DUMP_REG(DP_SPARE(1));
+ DUMP_REG(DP_TPG);
+
+ return;
+}
+#endif
+
+static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor,
+ int is_lvds,
+ const struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing)
+{
+ const int head_num = 0;
+ u32 reg_val = STATE1_ASY_OWNER_HEAD0 << head_num;
+ u32 vtotal, htotal;
+ u32 vsync_end, hsync_end;
+ u32 vblank_end, hblank_end;
+ u32 vblank_start, hblank_start;
+
+ reg_val |= is_lvds ? STATE1_ASY_PROTOCOL_LVDS_CUSTOM :
+ STATE1_ASY_PROTOCOL_DP_A;
+ reg_val |= STATE1_ASY_SUBOWNER_NONE |
+ STATE1_ASY_CRCMODE_COMPLETE_RASTER;
+
+ reg_val |= STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE;
+ reg_val |= STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE;
+ reg_val |= (link_cfg->bits_per_pixel > 18) ?
+ STATE1_ASY_PIXELDEPTH_BPP_24_444 :
+ STATE1_ASY_PIXELDEPTH_BPP_18_444;
+
+ tegra_sor_writel(sor, STATE1, reg_val);
+
+ /*
+ * Skipping programming NV_HEAD_STATE0, assuming:
+ * interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB
+ */
+ vtotal = timing->vsync_len.typ + timing->vback_porch.typ +
+ timing->vactive.typ + timing->vfront_porch.typ;
+ htotal = timing->hsync_len.typ + timing->hback_porch.typ +
+ timing->hactive.typ + timing->hfront_porch.typ;
+
+ tegra_sor_writel(sor, NV_HEAD_STATE1(head_num),
+ vtotal << NV_HEAD_STATE1_VTOTAL_SHIFT |
+ htotal << NV_HEAD_STATE1_HTOTAL_SHIFT);
+
+ vsync_end = timing->vsync_len.typ - 1;
+ hsync_end = timing->hsync_len.typ - 1;
+ tegra_sor_writel(sor, NV_HEAD_STATE2(head_num),
+ vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT |
+ hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT);
+
+ vblank_end = vsync_end + timing->vback_porch.typ;
+ hblank_end = hsync_end + timing->hback_porch.typ;
+ tegra_sor_writel(sor, NV_HEAD_STATE3(head_num),
+ vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT |
+ hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT);
+
+ vblank_start = vblank_end + timing->vactive.typ;
+ hblank_start = hblank_end + timing->hactive.typ;
+ tegra_sor_writel(sor, NV_HEAD_STATE4(head_num),
+ vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT |
+ hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT);
+
+ /* TODO: adding interlace mode support */
+ tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1);
+
+ tegra_sor_write_field(sor, CSTM,
+ CSTM_ROTCLK_DEFAULT_MASK |
+ CSTM_LVDS_EN_ENABLE,
+ 2 << CSTM_ROTCLK_SHIFT |
+ is_lvds ? CSTM_LVDS_EN_ENABLE :
+ CSTM_LVDS_EN_DISABLE);
+
+ tegra_dc_sor_config_pwm(sor, 1024, 1024);
+}
+
+static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl)
+{
+ u32 reg_val = readl(&disp_ctrl->cmd.state_access);
+
+ writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
+ writel(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt);
+
+ /* Enable DC now - otherwise pure text console may not show. */
+ writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
+ &disp_ctrl->cmd.disp_cmd);
+ writel(reg_val, &disp_ctrl->cmd.state_access);
+}
+
+int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ int ret;
+
+ tegra_sor_write_field(sor, CLK_CNTRL,
+ CLK_CNTRL_DP_CLK_SEL_MASK,
+ CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK);
+
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX6_BANDGAP_POWERDOWN_MASK,
+ PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE);
+ udelay(25);
+
+ tegra_sor_write_field(sor, PLL3,
+ PLL3_PLLVDD_MODE_MASK,
+ PLL3_PLLVDD_MODE_V3_3);
+ tegra_sor_writel(sor, PLL0,
+ 0xf << PLL0_ICHPMP_SHFIT |
+ 0x3 << PLL0_VCOCAP_SHIFT |
+ PLL0_PLLREG_LEVEL_V45 |
+ PLL0_RESISTORSEL_EXT |
+ PLL0_PWR_ON | PLL0_VCOPD_RESCIND);
+ tegra_sor_write_field(sor, PLL2,
+ PLL2_AUX1_SEQ_MASK |
+ PLL2_AUX9_LVDSEN_OVERRIDE |
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
+ PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE |
+ PLL2_AUX9_LVDSEN_OVERRIDE |
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE);
+ tegra_sor_writel(sor, PLL1, PLL1_TERM_COMPOUT_HIGH |
+ PLL1_TMDS_TERM_ENABLE);
+
+ if (tegra_dc_sor_poll_register(sor, PLL2,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK,
+ PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE,
+ 100, TEGRA_SOR_TIMEOUT_MS)) {
+ printf("DP failed to lock PLL\n");
+ return -EIO;
+ }
+
+ tegra_sor_write_field(sor, PLL2, PLL2_AUX2_MASK |
+ PLL2_AUX7_PORT_POWERDOWN_MASK,
+ PLL2_AUX2_OVERRIDE_POWERDOWN |
+ PLL2_AUX7_PORT_POWERDOWN_DISABLE);
+
+ ret = tegra_dc_sor_power_up(sor, 0);
+ if (ret) {
+ debug("DP failed to power up\n");
+ return ret;
+ }
+
+ /* re-enable SOR clock */
+ clock_sor_enable_edp_clock();
+
+ /* Power up lanes */
+ tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1);
+
+ tegra_dc_sor_set_dp_mode(sor, link_cfg);
+ debug("%s ret\n", __func__);
+
+ return 0;
+}
+
+int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing)
+{
+ const void *blob = gd->fdt_blob;
+ struct dc_ctlr *disp_ctrl;
+ u32 reg_val;
+ int node;
+
+ /* Use the first display controller */
+ debug("%s\n", __func__);
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
+ if (node < 0)
+ return -ENOENT;
+ disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+
+ tegra_dc_sor_enable_dc(disp_ctrl);
+ tegra_dc_sor_config_panel(sor, 0, link_cfg, timing);
+
+ writel(0x9f00, &disp_ctrl->cmd.state_ctrl);
+ writel(0x9f, &disp_ctrl->cmd.state_ctrl);
+
+ writel(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+ PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
+ &disp_ctrl->cmd.disp_pow_ctrl);
+
+ reg_val = tegra_sor_readl(sor, TEST);
+ if (reg_val & TEST_ATTACHED_TRUE)
+ return -EEXIST;
+
+ tegra_sor_writel(sor, SUPER_STATE1,
+ SUPER_STATE1_ATTACHED_NO);
+
+ /*
+ * Enable display2sor clock at least 2 cycles before DC start,
+ * to clear sor internal valid signal.
+ */
+ writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+ writel(0, &disp_ctrl->disp.disp_win_opt);
+ writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
+
+ /* Attach head */
+ tegra_dc_sor_update(sor);
+ tegra_sor_writel(sor, SUPER_STATE1,
+ SUPER_STATE1_ATTACHED_YES);
+ tegra_sor_writel(sor, SUPER_STATE1,
+ SUPER_STATE1_ATTACHED_YES |
+ SUPER_STATE1_ASY_HEAD_OP_AWAKE |
+ SUPER_STATE1_ASY_ORMODE_NORMAL);
+ tegra_dc_sor_super_update(sor);
+
+ /* Enable dc */
+ reg_val = readl(&disp_ctrl->cmd.state_access);
+ writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
+ writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
+ &disp_ctrl->cmd.disp_cmd);
+ writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt);
+ writel(reg_val, &disp_ctrl->cmd.state_access);
+
+ if (tegra_dc_sor_poll_register(sor, TEST,
+ TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
+ TEST_ACT_HEAD_OPMODE_AWAKE,
+ 100,
+ TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
+ printf("dc timeout waiting for OPMOD = AWAKE\n");
+ return -ETIMEDOUT;
+ } else {
+ debug("%s: sor is attached\n", __func__);
+ }
+
+#if DEBUG_SOR
+ dump_sor_reg(sor);
+#endif
+ debug("%s: ret=%d\n", __func__, 0);
+
+ return 0;
+}
+
+void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
+ link_cfg->drive_current);
+ tegra_sor_writel(sor, PR(sor->portnum),
+ link_cfg->preemphasis);
+ tegra_sor_writel(sor, POSTCURSOR(sor->portnum),
+ link_cfg->postcursor);
+ tegra_sor_writel(sor, LVDS, 0);
+
+ tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
+ tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count);
+
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ DP_PADCTL_TX_PU_ENABLE |
+ DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK,
+ DP_PADCTL_TX_PU_ENABLE |
+ 2 << DP_PADCTL_TX_PU_VALUE_SHIFT);
+
+ /* Precharge */
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0xf0);
+ udelay(20);
+
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
+}
+
+int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 drive_current = 0;
+ u32 pre_emphasis = 0;
+
+ /* Set to a known-good pre-calibrated setting */
+ switch (link_cfg->link_bw) {
+ case SOR_LINK_SPEED_G1_62:
+ case SOR_LINK_SPEED_G2_7:
+ drive_current = 0x13131313;
+ pre_emphasis = 0;
+ break;
+ case SOR_LINK_SPEED_G5_4:
+ debug("T124 does not support 5.4G link clock.\n");
+ default:
+ debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
+ return -ENOLINK;
+ }
+
+ tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current);
+ tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
+
+ return 0;
+}
+
+void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg)
+{
+ u32 pad_ctrl = 0;
+ int err = 0;
+
+ switch (link_cfg->lane_count) {
+ case 4:
+ pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
+ DP_PADCTL_PD_TXD_1_NO |
+ DP_PADCTL_PD_TXD_2_NO |
+ DP_PADCTL_PD_TXD_3_NO;
+ break;
+ case 2:
+ pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
+ DP_PADCTL_PD_TXD_1_NO |
+ DP_PADCTL_PD_TXD_2_YES |
+ DP_PADCTL_PD_TXD_3_YES;
+ break;
+ case 1:
+ pad_ctrl = DP_PADCTL_PD_TXD_0_NO |
+ DP_PADCTL_PD_TXD_1_YES |
+ DP_PADCTL_PD_TXD_2_YES |
+ DP_PADCTL_PD_TXD_3_YES;
+ break;
+ default:
+ printf("Invalid sor lane count: %u\n", link_cfg->lane_count);
+ return;
+ }
+
+ pad_ctrl |= DP_PADCTL_PAD_CAL_PD_POWERDOWN;
+ tegra_sor_writel(sor, DP_PADCTL(sor->portnum), pad_ctrl);
+
+ err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0);
+ if (err) {
+ debug("Wait for lane power down failed: %d\n", err);
+ return;
+ }
+}
+
+int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *cfg)
+{
+ u32 val = 0;
+
+ switch (cfg->lane_count) {
+ case 4:
+ val |= (DP_PADCTL_PD_TXD_3_NO |
+ DP_PADCTL_PD_TXD_2_NO);
+ /* fall through */
+ case 2:
+ val |= DP_PADCTL_PD_TXD_1_NO;
+ /* fall through */
+ case 1:
+ val |= DP_PADCTL_PD_TXD_0_NO;
+ break;
+ default:
+ debug("dp: invalid lane number %d\n", cfg->lane_count);
+ return -EINVAL;
+ }
+
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+ (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT));
+ udelay(100);
+ tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+ (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+ 0);
+
+ return 0;
+}
+
+static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable)
+{
+ u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt);
+
+ reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE;
+ writel(reg_val, &disp_ctrl->disp.disp_win_opt);
+}
+
+int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor)
+{
+ int dc_reg_ctx[DC_REG_SAVE_SPACE];
+ const void *blob = gd->fdt_blob;
+ struct dc_ctlr *disp_ctrl;
+ unsigned long dc_int_mask;
+ int node;
+ int ret;
+
+ debug("%s\n", __func__);
+ /* Use the first display controller */
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
+ if (node < 0) {
+ ret = -ENOENT;
+ goto err;
+ }
+ disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+
+ /* Sleep mode */
+ tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+ SUPER_STATE1_ASY_ORMODE_SAFE |
+ SUPER_STATE1_ATTACHED_YES);
+ tegra_dc_sor_super_update(sor);
+
+ tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx);
+
+ if (tegra_dc_sor_poll_register(sor, TEST,
+ TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
+ TEST_ACT_HEAD_OPMODE_SLEEP, 100,
+ TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
+ debug("dc timeout waiting for OPMOD = SLEEP\n");
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+ SUPER_STATE1_ASY_ORMODE_SAFE |
+ SUPER_STATE1_ATTACHED_NO);
+
+ /* Mask DC interrupts during the 2 dummy frames required for detach */
+ dc_int_mask = readl(&disp_ctrl->cmd.int_mask);
+ writel(0, &disp_ctrl->cmd.int_mask);
+
+ /* Stop DC->SOR path */
+ tegra_dc_sor_enable_sor(disp_ctrl, false);
+ ret = tegra_dc_sor_general_act(disp_ctrl);
+ if (ret)
+ goto err;
+
+ /* Stop DC */
+ writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd);
+ ret = tegra_dc_sor_general_act(disp_ctrl);
+ if (ret)
+ goto err;
+
+ tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx);
+
+ writel(dc_int_mask, &disp_ctrl->cmd.int_mask);
+
+ return 0;
+err:
+ debug("%s: ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp)
+{
+ const void *blob = gd->fdt_blob;
+ struct tegra_dc_sor_data *sor;
+ int node;
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_SOR);
+ if (node < 0)
+ return -ENOENT;
+ sor = calloc(1, sizeof(*sor));
+ if (!sor)
+ return -ENOMEM;
+ sor->base = (void *)fdtdec_get_addr(blob, node, "reg");
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_PMC);
+ if (node < 0)
+ return -ENOENT;
+ sor->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg");
+
+ sor->power_is_up = 0;
+ sor->portnum = 0;
+ *sorp = sor;
+
+ return 0;
+}
diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra124/sor.h
new file mode 100644
index 0000000..dc8fd03
--- /dev/null
+++ b/drivers/video/tegra124/sor.h
@@ -0,0 +1,922 @@
+/*
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _VIDEO_TEGRA124_SOR_H
+#define _VIDEO_TEGRA124_SOR_H
+
+#define SUPER_STATE0 0x1
+#define SUPER_STATE0_UPDATE_SHIFT 0
+#define SUPER_STATE0_UPDATE_DEFAULT_MASK 0x1
+#define SUPER_STATE1 0x2
+#define SUPER_STATE1_ATTACHED_SHIFT 3
+#define SUPER_STATE1_ATTACHED_NO (0 << 3)
+#define SUPER_STATE1_ATTACHED_YES (1 << 3)
+#define SUPER_STATE1_ASY_ORMODE_SHIFT 2
+#define SUPER_STATE1_ASY_ORMODE_SAFE (0 << 2)
+#define SUPER_STATE1_ASY_ORMODE_NORMAL (1 << 2)
+#define SUPER_STATE1_ASY_HEAD_OP_SHIFT 0
+#define SUPER_STATE1_ASY_HEAD_OP_DEFAULT_MASK 0x3
+#define SUPER_STATE1_ASY_HEAD_OP_SLEEP 0
+#define SUPER_STATE1_ASY_HEAD_OP_SNOOZE 1
+#define SUPER_STATE1_ASY_HEAD_OP_AWAKE 2
+#define STATE0 0x3
+#define STATE0_UPDATE_SHIFT 0
+#define STATE0_UPDATE_DEFAULT_MASK 0x1
+#define STATE1 0x4
+#define STATE1_ASY_PIXELDEPTH_SHIFT 17
+#define STATE1_ASY_PIXELDEPTH_DEFAULT_MASK (0xf << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_16_422 (1 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_18_444 (2 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_20_422 (3 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_24_422 (4 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_24_444 (5 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_30_444 (6 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_32_422 (7 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_36_444 (8 << 17)
+#define STATE1_ASY_PIXELDEPTH_BPP_48_444 (9 << 17)
+#define STATE1_ASY_REPLICATE_SHIFT 15
+#define STATE1_ASY_REPLICATE_DEFAULT_MASK (3 << 15)
+#define STATE1_ASY_REPLICATE_OFF (0 << 15)
+#define STATE1_ASY_REPLICATE_X2 (1 << 15)
+#define STATE1_ASY_REPLICATE_X4 (2 << 15)
+#define STATE1_ASY_DEPOL_SHIFT 14
+#define STATE1_ASY_DEPOL_DEFAULT_MASK (1 << 14)
+#define STATE1_ASY_DEPOL_POSITIVE_TRUE (0 << 14)
+#define STATE1_ASY_DEPOL_NEGATIVE_TRUE (1 << 14)
+#define STATE1_ASY_VSYNCPOL_SHIFT 13
+#define STATE1_ASY_VSYNCPOL_DEFAULT_MASK (1 << 13)
+#define STATE1_ASY_VSYNCPOL_POSITIVE_TRUE (0 << 13)
+#define STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE (1 << 13)
+#define STATE1_ASY_HSYNCPOL_SHIFT 12
+#define STATE1_ASY_HSYNCPOL_DEFAULT_MASK (1 << 12)
+#define STATE1_ASY_HSYNCPOL_POSITIVE_TRUE (0 << 12)
+#define STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE (1 << 12)
+#define STATE1_ASY_PROTOCOL_SHIFT 8
+#define STATE1_ASY_PROTOCOL_DEFAULT_MASK (0xf << 8)
+#define STATE1_ASY_PROTOCOL_LVDS_CUSTOM (0 << 8)
+#define STATE1_ASY_PROTOCOL_DP_A (8 << 8)
+#define STATE1_ASY_PROTOCOL_DP_B (9 << 8)
+#define STATE1_ASY_PROTOCOL_CUSTOM (15 << 8)
+#define STATE1_ASY_CRCMODE_SHIFT 6
+#define STATE1_ASY_CRCMODE_DEFAULT_MASK (3 << 6)
+#define STATE1_ASY_CRCMODE_ACTIVE_RASTER (0 << 6)
+#define STATE1_ASY_CRCMODE_COMPLETE_RASTER (1 << 6)
+#define STATE1_ASY_CRCMODE_NON_ACTIVE_RASTER (2 << 6)
+#define STATE1_ASY_SUBOWNER_SHIFT 4
+#define STATE1_ASY_SUBOWNER_DEFAULT_MASK (3 << 4)
+#define STATE1_ASY_SUBOWNER_NONE (0 << 4)
+#define STATE1_ASY_SUBOWNER_SUBHEAD0 (1 << 4)
+#define STATE1_ASY_SUBOWNER_SUBHEAD1 (2 << 4)
+#define STATE1_ASY_SUBOWNER_BOTH (3 << 4)
+#define STATE1_ASY_OWNER_SHIFT 0
+#define STATE1_ASY_OWNER_DEFAULT_MASK 0xf
+#define STATE1_ASY_OWNER_NONE 0
+#define STATE1_ASY_OWNER_HEAD0 1
+#define STATE1_ASY_OWNER_HEAD1 2
+#define NV_HEAD_STATE0(i) 0x5
+#define NV_HEAD_STATE0_INTERLACED_SHIFT 4
+#define NV_HEAD_STATE0_INTERLACED_DEFAULT_MASK (3 << 4)
+#define NV_HEAD_STATE0_INTERLACED_PROGRESSIVE (0 << 4)
+#define NV_HEAD_STATE0_INTERLACED_INTERLACED (1 << 4)
+#define NV_HEAD_STATE0_RANGECOMPRESS_SHIFT 3
+#define NV_HEAD_STATE0_RANGECOMPRESS_DEFAULT_MASK (1 << 3)
+#define NV_HEAD_STATE0_RANGECOMPRESS_DISABLE (0 << 3)
+#define NV_HEAD_STATE0_RANGECOMPRESS_ENABLE (1 << 3)
+#define NV_HEAD_STATE0_DYNRANGE_SHIFT 2
+#define NV_HEAD_STATE0_DYNRANGE_DEFAULT_MASK (1 << 2)
+#define NV_HEAD_STATE0_DYNRANGE_VESA (0 << 2)
+#define NV_HEAD_STATE0_DYNRANGE_CEA (1 << 2)
+#define NV_HEAD_STATE0_COLORSPACE_SHIFT 0
+#define NV_HEAD_STATE0_COLORSPACE_DEFAULT_MASK 0x3
+#define NV_HEAD_STATE0_COLORSPACE_RGB 0
+#define NV_HEAD_STATE0_COLORSPACE_YUV_601 1
+#define NV_HEAD_STATE0_COLORSPACE_YUV_709 2
+#define NV_HEAD_STATE1(i) (7 + i)
+#define NV_HEAD_STATE1_VTOTAL_SHIFT 16
+#define NV_HEAD_STATE1_VTOTAL_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE1_HTOTAL_SHIFT 0
+#define NV_HEAD_STATE1_HTOTAL_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE2(i) (9 + i)
+#define NV_HEAD_STATE2_VSYNC_END_SHIFT 16
+#define NV_HEAD_STATE2_VSYNC_END_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE2_HSYNC_END_SHIFT 0
+#define NV_HEAD_STATE2_HSYNC_END_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE3(i) (0xb + i)
+#define NV_HEAD_STATE3_VBLANK_END_SHIFT 16
+#define NV_HEAD_STATE3_VBLANK_END_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE3_HBLANK_END_SHIFT 0
+#define NV_HEAD_STATE3_HBLANK_END_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE4(i) (0xd + i)
+#define NV_HEAD_STATE4_VBLANK_START_SHIFT 16
+#define NV_HEAD_STATE4_VBLANK_START_DEFAULT_MASK (0x7fff << 16)
+#define NV_HEAD_STATE4_HBLANK_START_SHIFT 0
+#define NV_HEAD_STATE4_HBLANK_START_DEFAULT_MASK 0x7fff
+#define NV_HEAD_STATE5(i) (0xf + i)
+#define CRC_CNTRL 0x11
+#define CRC_CNTRL_ARM_CRC_ENABLE_SHIFT 0
+#define CRC_CNTRL_ARM_CRC_ENABLE_NO 0
+#define CRC_CNTRL_ARM_CRC_ENABLE_YES 1
+#define CRC_CNTRL_ARM_CRC_ENABLE_DIS 0
+#define CRC_CNTRL_ARM_CRC_ENABLE_EN 1
+#define CLK_CNTRL 0x13
+#define CLK_CNTRL_DP_CLK_SEL_SHIFT 0
+#define CLK_CNTRL_DP_CLK_SEL_MASK 0x3
+#define CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK 0
+#define CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK 1
+#define CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK 2
+#define CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK 3
+#define CLK_CNTRL_DP_LINK_SPEED_SHIFT 2
+#define CLK_CNTRL_DP_LINK_SPEED_MASK (0x1f << 2)
+#define CLK_CNTRL_DP_LINK_SPEED_G1_62 (6 << 2)
+#define CLK_CNTRL_DP_LINK_SPEED_G2_7 (10 << 2)
+#define CLK_CNTRL_DP_LINK_SPEED_LVDS (7 << 2)
+#define CAP 0x14
+#define CAP_DP_A_SHIFT 24
+#define CAP_DP_A_DEFAULT_MASK (1 << 24)
+#define CAP_DP_A_FALSE (0 << 24)
+#define CAP_DP_A_TRUE (1 << 24)
+#define CAP_DP_B_SHIFT 25
+#define CAP_DP_B_DEFAULT_MASK (1 << 24)
+#define CAP_DP_B_FALSE (0 << 24)
+#define CAP_DP_B_TRUE (1 << 24)
+#define PWR 0x15
+#define PWR_SETTING_NEW_SHIFT 31
+#define PWR_SETTING_NEW_DEFAULT_MASK (1 << 31)
+#define PWR_SETTING_NEW_DONE (0 << 31)
+#define PWR_SETTING_NEW_PENDING (1 << 31)
+#define PWR_SETTING_NEW_TRIGGER (1 << 31)
+#define PWR_MODE_SHIFT 28
+#define PWR_MODE_DEFAULT_MASK (1 << 28)
+#define PWR_MODE_NORMAL (0 << 28)
+#define PWR_MODE_SAFE (1 << 28)
+#define PWR_HALT_DELAY_SHIFT 24
+#define PWR_HALT_DELAY_DEFAULT_MASK (1 << 24)
+#define PWR_HALT_DELAY_DONE (0 << 24)
+#define PWR_HALT_DELAY_ACTIVE (1 << 24)
+#define PWR_SAFE_START_SHIFT 17
+#define PWR_SAFE_START_DEFAULT_MASK (1 << 17)
+#define PWR_SAFE_START_NORMAL (0 << 17)
+#define PWR_SAFE_START_ALT (1 << 17)
+#define PWR_SAFE_STATE_SHIFT 16
+#define PWR_SAFE_STATE_DEFAULT_MASK (1 << 16)
+#define PWR_SAFE_STATE_PD (0 << 16)
+#define PWR_SAFE_STATE_PU (1 << 16)
+#define PWR_NORMAL_START_SHIFT 1
+#define PWR_NORMAL_START_DEFAULT_MASK (1 << 1)
+#define PWR_NORMAL_START_NORMAL (0 << 16)
+#define PWR_NORMAL_START_ALT (1 << 16)
+#define PWR_NORMAL_STATE_SHIFT 0
+#define PWR_NORMAL_STATE_DEFAULT_MASK 0x1
+#define PWR_NORMAL_STATE_PD 0
+#define PWR_NORMAL_STATE_PU 1
+#define TEST 0x16
+#define TEST_TESTMUX_SHIFT 24
+#define TEST_TESTMUX_DEFAULT_MASK (0xff << 24)
+#define TEST_TESTMUX_AVSS (0 << 24)
+#define TEST_TESTMUX_CLOCKIN (2 << 24)
+#define TEST_TESTMUX_PLL_VOL (4 << 24)
+#define TEST_TESTMUX_SLOWCLKINT (8 << 24)
+#define TEST_TESTMUX_AVDD (16 << 24)
+#define TEST_TESTMUX_VDDREG (32 << 24)
+#define TEST_TESTMUX_REGREF_VDDREG (64 << 24)
+#define TEST_TESTMUX_REGREF_AVDD (128 << 24)
+#define TEST_CRC_SHIFT 23
+#define TEST_CRC_PRE_SERIALIZE (0 << 23)
+#define TEST_CRC_POST_DESERIALIZE (1 << 23)
+#define TEST_TPAT_SHIFT 20
+#define TEST_TPAT_DEFAULT_MASK (7 << 20)
+#define TEST_TPAT_LO (0 << 20)
+#define TEST_TPAT_TDAT (1 << 20)
+#define TEST_TPAT_RAMP (2 << 20)
+#define TEST_TPAT_WALK (3 << 20)
+#define TEST_TPAT_MAXSTEP (4 << 20)
+#define TEST_TPAT_MINSTEP (5 << 20)
+#define TEST_DSRC_SHIFT 16
+#define TEST_DSRC_DEFAULT_MASK (3 << 16)
+#define TEST_DSRC_NORMAL (0 << 16)
+#define TEST_DSRC_DEBUG (1 << 16)
+#define TEST_DSRC_TGEN (2 << 16)
+#define TEST_HEAD_NUMBER_SHIFT 12
+#define TEST_HEAD_NUMBER_DEFAULT_MASK (3 << 12)
+#define TEST_HEAD_NUMBER_NONE (0 << 12)
+#define TEST_HEAD_NUMBER_HEAD0 (1 << 12)
+#define TEST_HEAD_NUMBER_HEAD1 (2 << 12)
+#define TEST_ATTACHED_SHIFT 10
+#define TEST_ATTACHED_DEFAULT_MASK (1 << 10)
+#define TEST_ATTACHED_FALSE (0 << 10)
+#define TEST_ATTACHED_TRUE (1 << 10)
+#define TEST_ACT_HEAD_OPMODE_SHIFT 8
+#define TEST_ACT_HEAD_OPMODE_DEFAULT_MASK (3 << 8)
+#define TEST_ACT_HEAD_OPMODE_SLEEP (0 << 8)
+#define TEST_ACT_HEAD_OPMODE_SNOOZE (1 << 8)
+#define TEST_ACT_HEAD_OPMODE_AWAKE (2 << 8)
+#define TEST_INVD_SHIFT 6
+#define TEST_INVD_DISABLE (0 << 6)
+#define TEST_INVD_ENABLE (1 << 6)
+#define TEST_TEST_ENABLE_SHIFT 1
+#define TEST_TEST_ENABLE_DISABLE (0 << 1)
+#define TEST_TEST_ENABLE_ENABLE (1 << 1)
+#define PLL0 0x17
+#define PLL0_ICHPMP_SHFIT 24
+#define PLL0_ICHPMP_DEFAULT_MASK (0xf << 24)
+#define PLL0_VCOCAP_SHIFT 8
+#define PLL0_VCOCAP_DEFAULT_MASK (0xf << 8)
+#define PLL0_PLLREG_LEVEL_SHIFT 6
+#define PLL0_PLLREG_LEVEL_DEFAULT_MASK (3 << 6)
+#define PLL0_PLLREG_LEVEL_V25 (0 << 6)
+#define PLL0_PLLREG_LEVEL_V15 (1 << 6)
+#define PLL0_PLLREG_LEVEL_V35 (2 << 6)
+#define PLL0_PLLREG_LEVEL_V45 (3 << 6)
+#define PLL0_PULLDOWN_SHIFT 5
+#define PLL0_PULLDOWN_DEFAULT_MASK (1 << 5)
+#define PLL0_PULLDOWN_DISABLE (0 << 5)
+#define PLL0_PULLDOWN_ENABLE (1 << 5)
+#define PLL0_RESISTORSEL_SHIFT 4
+#define PLL0_RESISTORSEL_DEFAULT_MASK (1 << 4)
+#define PLL0_RESISTORSEL_INT (0 << 4)
+#define PLL0_RESISTORSEL_EXT (1 << 4)
+#define PLL0_VCOPD_SHIFT 2
+#define PLL0_VCOPD_MASK (1 << 2)
+#define PLL0_VCOPD_RESCIND (0 << 2)
+#define PLL0_VCOPD_ASSERT (1 << 2)
+#define PLL0_PWR_SHIFT 0
+#define PLL0_PWR_MASK 1
+#define PLL0_PWR_ON 0
+#define PLL0_PWR_OFF 1
+#define PLL1_TMDS_TERM_SHIFT 8
+#define PLL1_TMDS_TERM_DISABLE (0 << 8)
+#define PLL1_TMDS_TERM_ENABLE (1 << 8)
+#define PLL1 0x18
+#define PLL1_TERM_COMPOUT_SHIFT 15
+#define PLL1_TERM_COMPOUT_LOW (0 << 15)
+#define PLL1_TERM_COMPOUT_HIGH (1 << 15)
+#define PLL2 0x19
+#define PLL2_DCIR_PLL_RESET_SHIFT 0
+#define PLL2_DCIR_PLL_RESET_OVERRIDE (0 << 0)
+#define PLL2_DCIR_PLL_RESET_ALLOW (1 << 0)
+#define PLL2_AUX1_SHIFT 17
+#define PLL2_AUX1_SEQ_MASK (1 << 17)
+#define PLL2_AUX1_SEQ_PLLCAPPD_ALLOW (0 << 17)
+#define PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE (1 << 17)
+#define PLL2_AUX2_SHIFT 18
+#define PLL2_AUX2_MASK (1 << 18)
+#define PLL2_AUX2_OVERRIDE_POWERDOWN (0 << 18)
+#define PLL2_AUX2_ALLOW_POWERDOWN (1 << 18)
+#define PLL2_AUX6_SHIFT 22
+#define PLL2_AUX6_BANDGAP_POWERDOWN_MASK (1 << 22)
+#define PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE (0 << 22)
+#define PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE (1 << 22)
+#define PLL2_AUX7_SHIFT 23
+#define PLL2_AUX7_PORT_POWERDOWN_MASK (1 << 23)
+#define PLL2_AUX7_PORT_POWERDOWN_DISABLE (0 << 23)
+#define PLL2_AUX7_PORT_POWERDOWN_ENABLE (1 << 23)
+#define PLL2_AUX8_SHIFT 24
+#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK (1 << 24)
+#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE (0 << 24)
+#define PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE (1 << 24)
+#define PLL2_AUX9_SHIFT 25
+#define PLL2_AUX9_LVDSEN_ALLOW (0 << 25)
+#define PLL2_AUX9_LVDSEN_OVERRIDE (1 << 25)
+#define PLL3 0x1a
+#define PLL3_PLLVDD_MODE_SHIFT 13
+#define PLL3_PLLVDD_MODE_MASK (1 << 13)
+#define PLL3_PLLVDD_MODE_V1_8 (0 << 13)
+#define PLL3_PLLVDD_MODE_V3_3 (1 << 13)
+#define CSTM 0x1b
+#define CSTM_ROTDAT_SHIFT 28
+#define CSTM_ROTDAT_DEFAULT_MASK (7 << 28)
+#define CSTM_ROTCLK_SHIFT 24
+#define CSTM_ROTCLK_DEFAULT_MASK (0xf << 24)
+#define CSTM_LVDS_EN_SHIFT 16
+#define CSTM_LVDS_EN_DISABLE (0 << 16)
+#define CSTM_LVDS_EN_ENABLE (1 << 16)
+#define CSTM_LINKACTB_SHIFT 15
+#define CSTM_LINKACTB_DISABLE (0 << 15)
+#define CSTM_LINKACTB_ENABLE (1 << 15)
+#define CSTM_LINKACTA_SHIFT 14
+#define CSTM_LINKACTA_DISABLE (0 << 14)
+#define CSTM_LINKACTA_ENABLE (1 << 14)
+#define LVDS 0x1c
+#define LVDS_ROTDAT_SHIFT 28
+#define LVDS_ROTDAT_DEFAULT_MASK (7 << 28)
+#define LVDS_ROTDAT_RST (0 << 28)
+#define LVDS_ROTCLK_SHIFT 24
+#define LVDS_ROTCLK_DEFAULT_MASK (0xf << 24)
+#define LVDS_ROTCLK_RST (0 << 24)
+#define LVDS_PLLDIV_SHIFT 21
+#define LVDS_PLLDIV_DEFAULT_MASK (1 << 21)
+#define LVDS_PLLDIV_BY_7 (0 << 21)
+#define LVDS_BALANCED_SHIFT 19
+#define LVDS_BALANCED_DEFAULT_MASK (1 << 19)
+#define LVDS_BALANCED_DISABLE (0 << 19)
+#define LVDS_BALANCED_ENABLE (1 << 19)
+#define LVDS_NEW_MODE_SHIFT 18
+#define LVDS_NEW_MODE_DEFAULT_MASK (1 << 18)
+#define LVDS_NEW_MODE_DISABLE (0 << 18)
+#define LVDS_NEW_MODE_ENABLE (1 << 18)
+#define LVDS_DUP_SYNC_SHIFT 17
+#define LVDS_DUP_SYNC_DEFAULT_MASK (1 << 17)
+#define LVDS_DUP_SYNC_DISABLE (0 << 17)
+#define LVDS_DUP_SYNC_ENABLE (1 << 17)
+#define LVDS_LVDS_EN_SHIFT 16
+#define LVDS_LVDS_EN_DEFAULT_MASK (1 << 16)
+#define LVDS_LVDS_EN_ENABLE (1 << 16)
+#define LVDS_LINKACTB_SHIFT 15
+#define LVDS_LINKACTB_DEFAULT_MASK (1 << 15)
+#define LVDS_LINKACTB_DISABLE (0 << 15)
+#define LVDS_LINKACTB_ENABLE (1 << 15)
+#define LVDS_LINKACTA_SHIFT 14
+#define LVDS_LINKACTA_DEFAULT_MASK (1 << 14)
+#define LVDS_LINKACTA_ENABLE (1 << 14)
+#define LVDS_MODE_SHIFT 12
+#define LVDS_MODE_DEFAULT_MASK (3 << 12)
+#define LVDS_MODE_LVDS (0 << 12)
+#define LVDS_UPPER_SHIFT 11
+#define LVDS_UPPER_DEFAULT_MASK (1 << 11)
+#define LVDS_UPPER_FALSE (0 << 11)
+#define LVDS_UPPER_TRUE (1 << 11)
+#define LVDS_PD_TXCB_SHIFT 9
+#define LVDS_PD_TXCB_DEFAULT_MASK (1 << 9)
+#define LVDS_PD_TXCB_ENABLE (0 << 9)
+#define LVDS_PD_TXCB_DISABLE (1 << 9)
+#define LVDS_PD_TXCA_SHIFT 8
+#define LVDS_PD_TXCA_DEFAULT_MASK (1 << 8)
+#define LVDS_PD_TXCA_ENABLE (0 << 8)
+#define LVDS_PD_TXDB_3_SHIFT 7
+#define LVDS_PD_TXDB_3_DEFAULT_MASK (1 << 7)
+#define LVDS_PD_TXDB_3_ENABLE (0 << 7)
+#define LVDS_PD_TXDB_3_DISABLE (1 << 7)
+#define LVDS_PD_TXDB_2_SHIFT 6
+#define LVDS_PD_TXDB_2_DEFAULT_MASK (1 << 6)
+#define LVDS_PD_TXDB_2_ENABLE (0 << 6)
+#define LVDS_PD_TXDB_2_DISABLE (1 << 6)
+#define LVDS_PD_TXDB_1_SHIFT 5
+#define LVDS_PD_TXDB_1_DEFAULT_MASK (1 << 5)
+#define LVDS_PD_TXDB_1_ENABLE (0 << 5)
+#define LVDS_PD_TXDB_1_DISABLE (1 << 5)
+#define LVDS_PD_TXDB_0_SHIFT 4
+#define LVDS_PD_TXDB_0_DEFAULT_MASK (1 << 4)
+#define LVDS_PD_TXDB_0_ENABLE (0 << 4)
+#define LVDS_PD_TXDB_0_DISABLE (1 << 4)
+#define LVDS_PD_TXDA_3_SHIFT 3
+#define LVDS_PD_TXDA_3_DEFAULT_MASK (1 << 3)
+#define LVDS_PD_TXDA_3_ENABLE (0 << 3)
+#define LVDS_PD_TXDA_3_DISABLE (1 << 3)
+#define LVDS_PD_TXDA_2_SHIFT 2
+#define LVDS_PD_TXDA_2_DEFAULT_MASK (1 << 2)
+#define LVDS_PD_TXDA_2_ENABLE (0 << 2)
+#define LVDS_PD_TXDA_1_SHIFT 1
+#define LVDS_PD_TXDA_1_DEFAULT_MASK (1 << 1)
+#define LVDS_PD_TXDA_1_ENABLE (0 << 1)
+#define LVDS_PD_TXDA_0_SHIFT 0
+#define LVDS_PD_TXDA_0_DEFAULT_MASK 0x1
+#define LVDS_PD_TXDA_0_ENABLE 0
+#define CRCA 0x1d
+#define CRCA_VALID_FALSE 0
+#define CRCA_VALID_TRUE 1
+#define CRCA_VALID_RST 1
+#define CRCB 0x1e
+#define CRCB_CRC_DEFAULT_MASK 0xffffffff
+#define SEQ_CTL 0x20
+#define SEQ_CTL_SWITCH_SHIFT 30
+#define SEQ_CTL_SWITCH_MASK (1 << 30)
+#define SEQ_CTL_SWITCH_WAIT (0 << 30)
+#define SEQ_CTL_SWITCH_FORCE (1 << 30)
+#define SEQ_CTL_STATUS_SHIFT 28
+#define SEQ_CTL_STATUS_MASK (1 << 28)
+#define SEQ_CTL_STATUS_STOPPED (0 << 28)
+#define SEQ_CTL_STATUS_RUNNING (1 << 28)
+#define SEQ_CTL_PC_SHIFT 16
+#define SEQ_CTL_PC_MASK (0xf << 16)
+#define SEQ_CTL_PD_PC_ALT_SHIFT 12
+#define SEQ_CTL_PD_PC_ALT_MASK (0xf << 12)
+#define SEQ_CTL_PD_PC_SHIFT 8
+#define SEQ_CTL_PD_PC_MASK (0xf << 8)
+#define SEQ_CTL_PU_PC_ALT_SHIFT 4
+#define SEQ_CTL_PU_PC_ALT_MASK (0xf << 4)
+#define SEQ_CTL_PU_PC_SHIFT 0
+#define SEQ_CTL_PU_PC_MASK 0xf
+#define LANE_SEQ_CTL 0x21
+#define LANE_SEQ_CTL_SETTING_NEW_SHIFT 31
+#define LANE_SEQ_CTL_SETTING_MASK (1 << 31)
+#define LANE_SEQ_CTL_SETTING_NEW_DONE (0 << 31)
+#define LANE_SEQ_CTL_SETTING_NEW_PENDING (1 << 31)
+#define LANE_SEQ_CTL_SETTING_NEW_TRIGGER (1 << 31)
+#define LANE_SEQ_CTL_SEQ_STATE_SHIFT 28
+#define LANE_SEQ_CTL_SEQ_STATE_IDLE (0 << 28)
+#define LANE_SEQ_CTL_SEQ_STATE_BUSY (1 << 28)
+#define LANE_SEQ_CTL_SEQUENCE_SHIFT 20
+#define LANE_SEQ_CTL_SEQUENCE_UP (0 << 20)
+#define LANE_SEQ_CTL_SEQUENCE_DOWN (1 << 20)
+#define LANE_SEQ_CTL_NEW_POWER_STATE_SHIFT 16
+#define LANE_SEQ_CTL_NEW_POWER_STATE_PU (0 << 16)
+#define LANE_SEQ_CTL_NEW_POWER_STATE_PD (1 << 16)
+#define LANE_SEQ_CTL_DELAY_SHIFT 12
+#define LANE_SEQ_CTL_DELAY_DEFAULT_MASK (0xf << 12)
+#define LANE_SEQ_CTL_LANE9_STATE_SHIFT 9
+#define LANE_SEQ_CTL_LANE9_STATE_POWERUP (0 << 9)
+#define LANE_SEQ_CTL_LANE9_STATE_POWERDOWN (1 << 9)
+#define LANE_SEQ_CTL_LANE8_STATE_SHIFT 8
+#define LANE_SEQ_CTL_LANE8_STATE_POWERUP (0 << 8)
+#define LANE_SEQ_CTL_LANE8_STATE_POWERDOWN (1 << 8)
+#define LANE_SEQ_CTL_LANE7_STATE_SHIFT 7
+#define LANE_SEQ_CTL_LANE7_STATE_POWERUP (0 << 7)
+#define LANE_SEQ_CTL_LANE7_STATE_POWERDOWN (1 << 7)
+#define LANE_SEQ_CTL_LANE6_STATE_SHIFT 6
+#define LANE_SEQ_CTL_LANE6_STATE_POWERUP (0 << 6)
+#define LANE_SEQ_CTL_LANE6_STATE_POWERDOWN (1 << 6)
+#define LANE_SEQ_CTL_LANE5_STATE_SHIFT 5
+#define LANE_SEQ_CTL_LANE5_STATE_POWERUP (0 << 5)
+#define LANE_SEQ_CTL_LANE5_STATE_POWERDOWN (1 << 5)
+#define LANE_SEQ_CTL_LANE4_STATE_SHIFT 4
+#define LANE_SEQ_CTL_LANE4_STATE_POWERUP (0 << 4)
+#define LANE_SEQ_CTL_LANE4_STATE_POWERDOWN (1 << 4)
+#define LANE_SEQ_CTL_LANE3_STATE_SHIFT 3
+#define LANE_SEQ_CTL_LANE3_STATE_POWERUP (0 << 3)
+#define LANE_SEQ_CTL_LANE3_STATE_POWERDOWN (1 << 3)
+#define LANE_SEQ_CTL_LANE2_STATE_SHIFT 2
+#define LANE_SEQ_CTL_LANE2_STATE_POWERUP (0 << 2)
+#define LANE_SEQ_CTL_LANE2_STATE_POWERDOWN (1 << 2)
+#define LANE_SEQ_CTL_LANE1_STATE_SHIFT 1
+#define LANE_SEQ_CTL_LANE1_STATE_POWERUP (0 << 1)
+#define LANE_SEQ_CTL_LANE1_STATE_POWERDOWN (1 << 1)
+#define LANE_SEQ_CTL_LANE0_STATE_SHIFT 0
+#define LANE_SEQ_CTL_LANE0_STATE_POWERUP 0
+#define LANE_SEQ_CTL_LANE0_STATE_POWERDOWN 1
+#define SEQ_INST(i) (0x22 + i)
+#define SEQ_INST_PLL_PULLDOWN_SHIFT 31
+#define SEQ_INST_PLL_PULLDOWN_DISABLE (0 << 31)
+#define SEQ_INST_PLL_PULLDOWN_ENABLE (1 << 31)
+#define SEQ_INST_POWERDOWN_MACRO_SHIFT 30
+#define SEQ_INST_POWERDOWN_MACRO_NORMAL (0 << 30)
+#define SEQ_INST_POWERDOWN_MACRO_POWERDOWN (1 << 30)
+#define SEQ_INST_ASSERT_PLL_RESET_SHIFT 29
+#define SEQ_INST_ASSERT_PLL_RESET_NORMAL (0 << 29)
+#define SEQ_INST_ASSERT_PLL_RESET_RST (1 << 29)
+#define SEQ_INST_BLANK_V_SHIFT 28
+#define SEQ_INST_BLANK_V_NORMAL (0 << 28)
+#define SEQ_INST_BLANK_V_INACTIVE (1 << 28)
+#define SEQ_INST_BLANK_H_SHIFT 27
+#define SEQ_INST_BLANK_H_NORMAL (0 << 27)
+#define SEQ_INST_BLANK_H_INACTIVE (1 << 27)
+#define SEQ_INST_BLANK_DE_SHIFT 26
+#define SEQ_INST_BLANK_DE_NORMAL (0 << 26)
+#define SEQ_INST_BLANK_DE_INACTIVE (1 << 26)
+#define SEQ_INST_BLACK_DATA_SHIFT 25
+#define SEQ_INST_BLACK_DATA_NORMAL (0 << 25)
+#define SEQ_INST_BLACK_DATA_BLACK (1 << 25)
+#define SEQ_INST_TRISTATE_IOS_SHIFT 24
+#define SEQ_INST_TRISTATE_IOS_ENABLE_PINS (0 << 24)
+#define SEQ_INST_TRISTATE_IOS_TRISTATE (1 << 24)
+#define SEQ_INST_DRIVE_PWM_OUT_LO_SHIFT 23
+#define SEQ_INST_DRIVE_PWM_OUT_LO_FALSE (0 << 23)
+#define SEQ_INST_DRIVE_PWM_OUT_LO_TRUE (1 << 23)
+#define SEQ_INST_PIN_B_SHIFT 22
+#define SEQ_INST_PIN_B_LOW (0 << 22)
+#define SEQ_INST_PIN_B_HIGH (1 << 22)
+#define SEQ_INST_PIN_A_SHIFT 21
+#define SEQ_INST_PIN_A_LOW (0 << 21)
+#define SEQ_INST_PIN_A_HIGH (1 << 21)
+#define SEQ_INST_SEQUENCE_SHIFT 19
+#define SEQ_INST_SEQUENCE_UP (0 << 19)
+#define SEQ_INST_SEQUENCE_DOWN (1 << 19)
+#define SEQ_INST_LANE_SEQ_SHIFT 18
+#define SEQ_INST_LANE_SEQ_STOP (0 << 18)
+#define SEQ_INST_LANE_SEQ_RUN (1 << 18)
+#define SEQ_INST_PDPORT_SHIFT 17
+#define SEQ_INST_PDPORT_NO (0 << 17)
+#define SEQ_INST_PDPORT_YES (1 << 17)
+#define SEQ_INST_PDPLL_SHIFT 16
+#define SEQ_INST_PDPLL_NO (0 << 16)
+#define SEQ_INST_PDPLL_YES (1 << 16)
+#define SEQ_INST_HALT_SHIFT 15
+#define SEQ_INST_HALT_FALSE (0 << 15)
+#define SEQ_INST_HALT_TRUE (1 << 15)
+#define SEQ_INST_WAIT_UNITS_SHIFT 12
+#define SEQ_INST_WAIT_UNITS_DEFAULT_MASK (3 << 12)
+#define SEQ_INST_WAIT_UNITS_US (0 << 12)
+#define SEQ_INST_WAIT_UNITS_MS (1 << 12)
+#define SEQ_INST_WAIT_UNITS_VSYNC (2 << 12)
+#define SEQ_INST_WAIT_TIME_SHIFT 0
+#define SEQ_INST_WAIT_TIME_DEFAULT_MASK 0x3ff
+#define PWM_DIV 0x32
+#define PWM_DIV_DIVIDE_DEFAULT_MASK 0xffffff
+#define PWM_CTL 0x33
+#define PWM_CTL_SETTING_NEW_SHIFT 31
+#define PWM_CTL_SETTING_NEW_DONE (0 << 31)
+#define PWM_CTL_SETTING_NEW_PENDING (1 << 31)
+#define PWM_CTL_SETTING_NEW_TRIGGER (1 << 31)
+#define PWM_CTL_CLKSEL_SHIFT 30
+#define PWM_CTL_CLKSEL_PCLK (0 << 30)
+#define PWM_CTL_CLKSEL_XTAL (1 << 30)
+#define PWM_CTL_DUTY_CYCLE_SHIFT 0
+#define PWM_CTL_DUTY_CYCLE_MASK 0xffffff
+#define MSCHECK 0x49
+#define MSCHECK_CTL_SHIFT 31
+#define MSCHECK_CTL_CLEAR (0 << 31)
+#define MSCHECK_CTL_RUN (1 << 31)
+#define XBAR_CTRL 0x4a
+#define DP_LINKCTL(i) (0x4c + (i))
+#define DP_LINKCTL_FORCE_IDLEPTTRN_SHIFT 31
+#define DP_LINKCTL_FORCE_IDLEPTTRN_NO (0 << 31)
+#define DP_LINKCTL_FORCE_IDLEPTTRN_YES (1 << 31)
+#define DP_LINKCTL_COMPLIANCEPTTRN_SHIFT 28
+#define DP_LINKCTL_COMPLIANCEPTTRN_NOPATTERN (0 << 28)
+#define DP_LINKCTL_COMPLIANCEPTTRN_COLORSQARE (1 << 28)
+#define DP_LINKCTL_LANECOUNT_SHIFT 16
+#define DP_LINKCTL_LANECOUNT_MASK (0x1f << 16)
+#define DP_LINKCTL_LANECOUNT_ZERO (0 << 16)
+#define DP_LINKCTL_LANECOUNT_ONE (1 << 16)
+#define DP_LINKCTL_LANECOUNT_TWO (3 << 16)
+#define DP_LINKCTL_LANECOUNT_FOUR (15 << 16)
+#define DP_LINKCTL_ENHANCEDFRAME_SHIFT 14
+#define DP_LINKCTL_ENHANCEDFRAME_DISABLE (0 << 14)
+#define DP_LINKCTL_ENHANCEDFRAME_ENABLE (1 << 14)
+#define DP_LINKCTL_SYNCMODE_SHIFT 10
+#define DP_LINKCTL_SYNCMODE_DISABLE (0 << 10)
+#define DP_LINKCTL_SYNCMODE_ENABLE (1 << 10)
+#define DP_LINKCTL_TUSIZE_SHIFT 2
+#define DP_LINKCTL_TUSIZE_MASK (0x7f << 2)
+#define DP_LINKCTL_ENABLE_SHIFT 0
+#define DP_LINKCTL_ENABLE_NO 0
+#define DP_LINKCTL_ENABLE_YES 1
+#define DC(i) (0x4e + (i))
+#define DC_LANE3_DP_LANE3_SHIFT 24
+#define DC_LANE3_DP_LANE3_MASK (0xff << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL0 (17 << 24)
+#define DC_LANE3_DP_LANE3_P1_LEVEL0 (21 << 24)
+#define DC_LANE3_DP_LANE3_P2_LEVEL0 (26 << 24)
+#define DC_LANE3_DP_LANE3_P3_LEVEL0 (34 << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL1 (26 << 24)
+#define DC_LANE3_DP_LANE3_P1_LEVEL1 (32 << 24)
+#define DC_LANE3_DP_LANE3_P2_LEVEL1 (39 << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL2 (34 << 24)
+#define DC_LANE3_DP_LANE3_P1_LEVEL2 (43 << 24)
+#define DC_LANE3_DP_LANE3_P0_LEVEL3 (51 << 24)
+#define DC_LANE2_DP_LANE0_SHIFT 16
+#define DC_LANE2_DP_LANE0_MASK (0xff << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL0 (17 << 16)
+#define DC_LANE2_DP_LANE0_P1_LEVEL0 (21 << 16)
+#define DC_LANE2_DP_LANE0_P2_LEVEL0 (26 << 16)
+#define DC_LANE2_DP_LANE0_P3_LEVEL0 (34 << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL1 (26 << 16)
+#define DC_LANE2_DP_LANE0_P1_LEVEL1 (32 << 16)
+#define DC_LANE2_DP_LANE0_P2_LEVEL1 (39 << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL2 (34 << 16)
+#define DC_LANE2_DP_LANE0_P1_LEVEL2 (43 << 16)
+#define DC_LANE2_DP_LANE0_P0_LEVEL3 (51 << 16)
+#define DC_LANE1_DP_LANE1_SHIFT 8
+#define DC_LANE1_DP_LANE1_MASK (0xff << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL0 (17 << 8)
+#define DC_LANE1_DP_LANE1_P1_LEVEL0 (21 << 8)
+#define DC_LANE1_DP_LANE1_P2_LEVEL0 (26 << 8)
+#define DC_LANE1_DP_LANE1_P3_LEVEL0 (34 << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL1 (26 << 8)
+#define DC_LANE1_DP_LANE1_P1_LEVEL1 (32 << 8)
+#define DC_LANE1_DP_LANE1_P2_LEVEL1 (39 << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL2 (34 << 8)
+#define DC_LANE1_DP_LANE1_P1_LEVEL2 (43 << 8)
+#define DC_LANE1_DP_LANE1_P0_LEVEL3 (51 << 8)
+#define DC_LANE0_DP_LANE2_SHIFT 0
+#define DC_LANE0_DP_LANE2_MASK 0xff
+#define DC_LANE0_DP_LANE2_P0_LEVEL0 17
+#define DC_LANE0_DP_LANE2_P1_LEVEL0 21
+#define DC_LANE0_DP_LANE2_P2_LEVEL0 26
+#define DC_LANE0_DP_LANE2_P3_LEVEL0 34
+#define DC_LANE0_DP_LANE2_P0_LEVEL1 26
+#define DC_LANE0_DP_LANE2_P1_LEVEL1 32
+#define DC_LANE0_DP_LANE2_P2_LEVEL1 39
+#define DC_LANE0_DP_LANE2_P0_LEVEL2 34
+#define DC_LANE0_DP_LANE2_P1_LEVEL2 43
+#define DC_LANE0_DP_LANE2_P0_LEVEL3 51
+#define LANE_DRIVE_CURRENT(i) (0x4e + (i))
+#define PR(i) (0x52 + (i))
+#define PR_LANE3_DP_LANE3_SHIFT 24
+#define PR_LANE3_DP_LANE3_MASK (0xff << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D1_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D2_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D3_LEVEL0 (0 << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL1 (4 << 24)
+#define PR_LANE3_DP_LANE3_D1_LEVEL1 (6 << 24)
+#define PR_LANE3_DP_LANE3_D2_LEVEL1 (17 << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL2 (8 << 24)
+#define PR_LANE3_DP_LANE3_D1_LEVEL2 (13 << 24)
+#define PR_LANE3_DP_LANE3_D0_LEVEL3 (17 << 24)
+#define PR_LANE2_DP_LANE0_SHIFT 16
+#define PR_LANE2_DP_LANE0_MASK (0xff << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D1_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D2_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D3_LEVEL0 (0 << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL1 (4 << 16)
+#define PR_LANE2_DP_LANE0_D1_LEVEL1 (6 << 16)
+#define PR_LANE2_DP_LANE0_D2_LEVEL1 (17 << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL2 (8 << 16)
+#define PR_LANE2_DP_LANE0_D1_LEVEL2 (13 << 16)
+#define PR_LANE2_DP_LANE0_D0_LEVEL3 (17 << 16)
+#define PR_LANE1_DP_LANE1_SHIFT 8
+#define PR_LANE1_DP_LANE1_MASK (0xff >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D1_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D2_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D3_LEVEL0 (0 >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL1 (4 >> 8)
+#define PR_LANE1_DP_LANE1_D1_LEVEL1 (6 >> 8)
+#define PR_LANE1_DP_LANE1_D2_LEVEL1 (17 >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL2 (8 >> 8)
+#define PR_LANE1_DP_LANE1_D1_LEVEL2 (13 >> 8)
+#define PR_LANE1_DP_LANE1_D0_LEVEL3 (17 >> 8)
+#define PR_LANE0_DP_LANE2_SHIFT 0
+#define PR_LANE0_DP_LANE2_MASK 0xff
+#define PR_LANE0_DP_LANE2_D0_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D1_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D2_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D3_LEVEL0 0
+#define PR_LANE0_DP_LANE2_D0_LEVEL1 4
+#define PR_LANE0_DP_LANE2_D1_LEVEL1 6
+#define PR_LANE0_DP_LANE2_D2_LEVEL1 17
+#define PR_LANE0_DP_LANE2_D0_LEVEL2 8
+#define PR_LANE0_DP_LANE2_D1_LEVEL2 13
+#define PR_LANE0_DP_LANE2_D0_LEVEL3 17
+#define LANE4_PREEMPHASIS(i) (0x54 + (i))
+#define POSTCURSOR(i) (0x56 + (i))
+#define DP_CONFIG(i) (0x58 + (i))
+#define DP_CONFIG_RD_RESET_VAL_SHIFT 31
+#define DP_CONFIG_RD_RESET_VAL_POSITIVE (0 << 31)
+#define DP_CONFIG_RD_RESET_VAL_NEGATIVE (1 << 31)
+#define DP_CONFIG_IDLE_BEFORE_ATTACH_SHIFT 28
+#define DP_CONFIG_IDLE_BEFORE_ATTACH_DISABLE (0 << 28)
+#define DP_CONFIG_IDLE_BEFORE_ATTACH_ENABLE (1 << 28)
+#define DP_CONFIG_ACTIVESYM_CNTL_SHIFT 26
+#define DP_CONFIG_ACTIVESYM_CNTL_DISABLE (0 << 26)
+#define DP_CONFIG_ACTIVESYM_CNTL_ENABLE (1 << 26)
+#define DP_CONFIG_ACTIVESYM_POLARITY_SHIFT 24
+#define DP_CONFIG_ACTIVESYM_POLARITY_NEGATIVE (0 << 24)
+#define DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE (1 << 24)
+#define DP_CONFIG_ACTIVESYM_FRAC_SHIFT 16
+#define DP_CONFIG_ACTIVESYM_FRAC_MASK (0xf << 16)
+#define DP_CONFIG_ACTIVESYM_COUNT_SHIFT 8
+#define DP_CONFIG_ACTIVESYM_COUNT_MASK (0x7f << 8)
+#define DP_CONFIG_WATERMARK_SHIFT 0
+#define DP_CONFIG_WATERMARK_MASK 0x3f
+#define DP_MN(i) (0x5a + i)
+#define DP_MN_M_MOD_SHIFT 30
+#define DP_MN_M_MOD_DEFAULT_MASK (3 << 30)
+#define DP_MN_M_MOD_NONE (0 << 30)
+#define DP_MN_M_MOD_INC (1 << 30)
+#define DP_MN_M_MOD_DEC (2 << 30)
+#define DP_MN_M_DELTA_SHIFT 24
+#define DP_MN_M_DELTA_DEFAULT_MASK (0xf << 24)
+#define DP_MN_N_VAL_SHIFT 0
+#define DP_MN_N_VAL_DEFAULT_MASK 0xffffff
+#define DP_PADCTL(i) (0x5c + (i))
+#define DP_PADCTL_SPARE_SHIFT 25
+#define DP_PADCTL_SPARE_DEFAULT_MASK (0x7f << 25)
+#define DP_PADCTL_VCO_2X_SHIFT 24
+#define DP_PADCTL_VCO_2X_DISABLE (0 << 24)
+#define DP_PADCTL_VCO_2X_ENABLE (1 << 24)
+#define DP_PADCTL_PAD_CAL_PD_SHIFT 23
+#define DP_PADCTL_PAD_CAL_PD_POWERUP (0 << 23)
+#define DP_PADCTL_PAD_CAL_PD_POWERDOWN (1 << 23)
+#define DP_PADCTL_TX_PU_SHIFT 22
+#define DP_PADCTL_TX_PU_DISABLE (0 << 22)
+#define DP_PADCTL_TX_PU_ENABLE (1 << 22)
+#define DP_PADCTL_TX_PU_MASK (1 << 22)
+#define DP_PADCTL_REG_CTRL_SHIFT 20
+#define DP_PADCTL_REG_CTRL_DEFAULT_MASK (3 << 20)
+#define DP_PADCTL_VCMMODE_SHIFT 16
+#define DP_PADCTL_VCMMODE_DEFAULT_MASK (0xf << 16)
+#define DP_PADCTL_VCMMODE_TRISTATE (0 << 16)
+#define DP_PADCTL_VCMMODE_TEST_MUX (1 << 16)
+#define DP_PADCTL_VCMMODE_WEAK_PULLDOWN (2 << 16)
+#define DP_PADCTL_VCMMODE_STRONG_PULLDOWN (4 << 16)
+#define DP_PADCTL_TX_PU_VALUE_SHIFT 8
+#define DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK (0xff << 8)
+#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_SHIFT 7
+#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_DISABLE (0 << 7)
+#define DP_PADCTL_COMODE_TXD_3_DP_TXD_3_ENABLE (1 << 7)
+#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_SHIFT 6
+#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_DISABLE (0 << 6)
+#define DP_PADCTL_COMODE_TXD_2_DP_TXD_0_ENABLE (1 << 6)
+#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_SHIFT 5
+#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_DISABLE (0 << 5)
+#define DP_PADCTL_COMODE_TXD_1_DP_TXD_1_ENABLE (1 << 5)
+#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT 4
+#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_DISABLE (0 << 4)
+#define DP_PADCTL_COMODE_TXD_0_DP_TXD_2_ENABLE (1 << 4)
+#define DP_PADCTL_PD_TXD_3_SHIFT 3
+#define DP_PADCTL_PD_TXD_3_YES (0 << 3)
+#define DP_PADCTL_PD_TXD_3_NO (1 << 3)
+#define DP_PADCTL_PD_TXD_0_SHIFT 2
+#define DP_PADCTL_PD_TXD_0_YES (0 << 2)
+#define DP_PADCTL_PD_TXD_0_NO (1 << 2)
+#define DP_PADCTL_PD_TXD_1_SHIFT 1
+#define DP_PADCTL_PD_TXD_1_YES (0 << 1)
+#define DP_PADCTL_PD_TXD_1_NO (1 << 1)
+#define DP_PADCTL_PD_TXD_2_SHIFT 0
+#define DP_PADCTL_PD_TXD_2_YES 0
+#define DP_PADCTL_PD_TXD_2_NO 1
+#define DP_DEBUG(i) (0x5e + i)
+#define DP_SPARE(i) (0x60 + (i))
+#define DP_SPARE_REG_SHIFT 3
+#define DP_SPARE_REG_DEFAULT_MASK (0x1fffffff << 3)
+#define DP_SPARE_SOR_CLK_SEL_SHIFT 2
+#define DP_SPARE_SOR_CLK_SEL_DEFAULT_MASK (1 << 2)
+#define DP_SPARE_SOR_CLK_SEL_SAFE_SORCLK (0 << 2)
+#define DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK (1 << 2)
+#define DP_SPARE_PANEL_SHIFT 1
+#define DP_SPARE_PANEL_EXTERNAL (0 << 1)
+#define DP_SPARE_PANEL_INTERNAL (1 << 1)
+#define DP_SPARE_SEQ_ENABLE_SHIFT 0
+#define DP_SPARE_SEQ_ENABLE_NO 0
+#define DP_SPARE_SEQ_ENABLE_YES 1
+#define DP_AUDIO_CTRL 0x62
+#define DP_AUDIO_HBLANK_SYMBOLS 0x63
+#define DP_AUDIO_HBLANK_SYMBOLS_MASK 0x1ffff
+#define DP_AUDIO_HBLANK_SYMBOLS_VALUE_SHIFT 0
+#define DP_AUDIO_VBLANK_SYMBOLS 0x64
+#define DP_AUDIO_VBLANK_SYMBOLS_MASK 0x1ffff
+#define DP_AUDIO_VBLANK_SYMBOLS_SHIFT 0
+#define DP_GENERIC_INFOFRAME_HEADER 0x65
+#define DP_GENERIC_INFOFRAME_SUBPACK(i) (0x66 + (i))
+#define DP_TPG 0x6d
+#define DP_TPG_LANE3_CHANNELCODING_SHIFT 30
+#define DP_TPG_LANE3_CHANNELCODING_DISABLE (0 << 30)
+#define DP_TPG_LANE3_CHANNELCODING_ENABLE (1 << 30)
+#define DP_TPG_LANE3_SCRAMBLEREN_SHIFT 28
+#define DP_TPG_LANE3_SCRAMBLEREN_ENABLE_GALIOS (1 << 28)
+#define DP_TPG_LANE3_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 28)
+#define DP_TPG_LANE3_PATTERN_SHIFT 24
+#define DP_TPG_LANE3_PATTERN_DEFAULT_MASK (0xf << 24)
+#define DP_TPG_LANE3_PATTERN_NOPATTERN (0 << 24)
+#define DP_TPG_LANE3_PATTERN_TRAINING1 (1 << 24)
+#define DP_TPG_LANE3_PATTERN_TRAINING2 (2 << 24)
+#define DP_TPG_LANE3_PATTERN_TRAINING3 (3 << 24)
+#define DP_TPG_LANE3_PATTERN_D102 (4 << 24)
+#define DP_TPG_LANE3_PATTERN_SBLERRRATE (5 << 24)
+#define DP_TPG_LANE3_PATTERN_PRBS7 (6 << 24)
+#define DP_TPG_LANE3_PATTERN_CSTM (7 << 24)
+#define DP_TPG_LANE3_PATTERN_HBR2_COMPLIANCE (8 << 24)
+#define DP_TPG_LANE2_CHANNELCODING_SHIFT 22
+#define DP_TPG_LANE2_CHANNELCODING_DISABLE (0 << 22)
+#define DP_TPG_LANE2_CHANNELCODING_ENABLE (1 << 22)
+#define DP_TPG_LANE2_SCRAMBLEREN_SHIFT 20
+#define DP_TPG_LANE2_SCRAMBLEREN_DEFAULT_MASK (3 << 20)
+#define DP_TPG_LANE2_SCRAMBLEREN_DISABLE (0 << 20)
+#define DP_TPG_LANE2_SCRAMBLEREN_ENABLE_GALIOS (1 << 20)
+#define DP_TPG_LANE2_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 20)
+#define DP_TPG_LANE2_PATTERN_SHIFT 16
+#define DP_TPG_LANE2_PATTERN_DEFAULT_MASK (0xf << 16)
+#define DP_TPG_LANE2_PATTERN_NOPATTERN (0 << 16)
+#define DP_TPG_LANE2_PATTERN_TRAINING1 (1 << 16)
+#define DP_TPG_LANE2_PATTERN_TRAINING2 (2 << 16)
+#define DP_TPG_LANE2_PATTERN_TRAINING3 (3 << 16)
+#define DP_TPG_LANE2_PATTERN_D102 (4 << 16)
+#define DP_TPG_LANE2_PATTERN_SBLERRRATE (5 << 16)
+#define DP_TPG_LANE2_PATTERN_PRBS7 (6 << 16)
+#define DP_TPG_LANE2_PATTERN_CSTM (7 << 16)
+#define DP_TPG_LANE2_PATTERN_HBR2_COMPLIANCE (8 << 16)
+#define DP_TPG_LANE1_CHANNELCODING_SHIFT 14
+#define DP_TPG_LANE1_CHANNELCODING_DISABLE (0 << 14)
+#define DP_TPG_LANE1_CHANNELCODING_ENABLE (1 << 14)
+#define DP_TPG_LANE1_SCRAMBLEREN_SHIFT 12
+#define DP_TPG_LANE1_SCRAMBLEREN_DEFAULT_MASK (3 << 12)
+#define DP_TPG_LANE1_SCRAMBLEREN_DISABLE (0 << 12)
+#define DP_TPG_LANE1_SCRAMBLEREN_ENABLE_GALIOS (1 << 12)
+#define DP_TPG_LANE1_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 12)
+#define DP_TPG_LANE1_PATTERN_SHIFT 8
+#define DP_TPG_LANE1_PATTERN_DEFAULT_MASK (0xf << 8)
+#define DP_TPG_LANE1_PATTERN_NOPATTERN (0 << 8)
+#define DP_TPG_LANE1_PATTERN_TRAINING1 (1 << 8)
+#define DP_TPG_LANE1_PATTERN_TRAINING2 (2 << 8)
+#define DP_TPG_LANE1_PATTERN_TRAINING3 (3 << 8)
+#define DP_TPG_LANE1_PATTERN_D102 (4 << 8)
+#define DP_TPG_LANE1_PATTERN_SBLERRRATE (5 << 8)
+#define DP_TPG_LANE1_PATTERN_PRBS7 (6 << 8)
+#define DP_TPG_LANE1_PATTERN_CSTM (7 << 8)
+#define DP_TPG_LANE1_PATTERN_HBR2_COMPLIANCE (8 << 8)
+#define DP_TPG_LANE0_CHANNELCODING_SHIFT 6
+#define DP_TPG_LANE0_CHANNELCODING_DISABLE (0 << 6)
+#define DP_TPG_LANE0_CHANNELCODING_ENABLE (1 << 6)
+#define DP_TPG_LANE0_SCRAMBLEREN_SHIFT 4
+#define DP_TPG_LANE0_SCRAMBLEREN_DEFAULT_MASK (3 << 4)
+#define DP_TPG_LANE0_SCRAMBLEREN_DISABLE (0 << 4)
+#define DP_TPG_LANE0_SCRAMBLEREN_ENABLE_GALIOS (1 << 4)
+#define DP_TPG_LANE0_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 4)
+#define DP_TPG_LANE0_PATTERN_SHIFT 0
+#define DP_TPG_LANE0_PATTERN_DEFAULT_MASK 0xf
+#define DP_TPG_LANE0_PATTERN_NOPATTERN 0
+#define DP_TPG_LANE0_PATTERN_TRAINING1 1
+#define DP_TPG_LANE0_PATTERN_TRAINING2 2
+#define DP_TPG_LANE0_PATTERN_TRAINING3 3
+#define DP_TPG_LANE0_PATTERN_D102 4
+#define DP_TPG_LANE0_PATTERN_SBLERRRATE 5
+#define DP_TPG_LANE0_PATTERN_PRBS7 6
+#define DP_TPG_LANE0_PATTERN_CSTM 7
+#define DP_TPG_LANE0_PATTERN_HBR2_COMPLIANCE 8
+
+enum {
+ training_pattern_disabled = 0,
+ training_pattern_1 = 1,
+ training_pattern_2 = 2,
+ training_pattern_3 = 3,
+ training_pattern_none = 0xff
+};
+
+enum tegra_dc_sor_protocol {
+ SOR_DP,
+ SOR_LVDS,
+};
+
+#define SOR_LINK_SPEED_G1_62 6
+#define SOR_LINK_SPEED_G2_7 10
+#define SOR_LINK_SPEED_G5_4 20
+#define SOR_LINK_SPEED_LVDS 7
+
+struct tegra_dp_link_config {
+ int is_valid;
+
+ /* Supported configuration */
+ u8 max_link_bw;
+ u8 max_lane_count;
+ int downspread;
+ int support_enhanced_framing;
+ u32 bits_per_pixel;
+ int alt_scramber_reset_cap; /* true for eDP */
+ int only_enhanced_framing; /* enhanced_frame_en ignored */
+ int frame_in_ms;
+
+ /* Actual configuration */
+ u8 link_bw;
+ u8 lane_count;
+ int enhanced_framing;
+ int scramble_ena;
+
+ u32 activepolarity;
+ u32 active_count;
+ u32 tu_size;
+ u32 active_frac;
+ u32 watermark;
+
+ s32 hblank_sym;
+ s32 vblank_sym;
+
+ /* Training data */
+ u32 drive_current;
+ u32 preemphasis;
+ u32 postcursor;
+ u8 aux_rd_interval;
+ u8 tps3_supported;
+};
+
+struct tegra_dc_sor_data {
+ void *base;
+ void *pmc_base;
+ u8 portnum; /* 0 or 1 */
+ int power_is_up;
+};
+
+#define TEGRA_SOR_TIMEOUT_MS 1000
+#define TEGRA_SOR_ATTACH_TIMEOUT_MS 1000
+
+int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd);
+void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+ u8 training_pattern, const struct tegra_dp_link_config *link_cfg);
+void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw);
+void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count);
+void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor,
+ int power_up);
+void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int);
+void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw,
+ u8 *lane_count);
+void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg);
+int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *cfg);
+void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor);
+void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask,
+ u32 pe_reg, u32 vs_reg, u32 pc_reg, u8 pc_supported);
+
+int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+ const struct tegra_dp_link_config *link_cfg,
+ const struct display_timing *timing);
+int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor);
+
+void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx);
+int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl);
+void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl,
+ int *dc_reg_ctx);
+
+int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp);
+#endif
diff --git a/drivers/video/tegra124/tegra124-lcd.c b/drivers/video/tegra124/tegra124-lcd.c
new file mode 100644
index 0000000..2733590
--- /dev/null
+++ b/drivers/video/tegra124/tegra124-lcd.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <lcd.h>
+#include <asm/gpio.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch/clock.h>
+#include <asm/arch-tegra/dc.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum {
+ /* Maximum LCD size we support */
+ LCD_MAX_WIDTH = 1920,
+ LCD_MAX_HEIGHT = 1200,
+ LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
+};
+
+vidinfo_t panel_info = {
+ /* Insert a value here so that we don't end up in the BSS */
+ .vl_col = -1,
+};
+
+int tegra_lcd_check_next_stage(const void *blob, int wait)
+{
+ return 0;
+}
+
+void tegra_lcd_early_init(const void *blob)
+{
+ /*
+ * Go with the maximum size for now. We will fix this up after
+ * relocation. These values are only used for memory alocation.
+ */
+ panel_info.vl_col = LCD_MAX_WIDTH;
+ panel_info.vl_row = LCD_MAX_HEIGHT;
+ panel_info.vl_bpix = LCD_MAX_LOG2_BPP;
+}
+
+static int tegra124_lcd_init(void *lcdbase)
+{
+ struct display_timing timing;
+ int ret;
+
+ clock_set_up_plldp();
+ clock_adjust_periph_pll_div(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
+ 408000000, NULL);
+
+ clock_enable(PERIPH_ID_HOST1X);
+ clock_enable(PERIPH_ID_DISP1);
+ clock_enable(PERIPH_ID_PWM);
+ clock_enable(PERIPH_ID_DPAUX);
+ clock_enable(PERIPH_ID_SOR0);
+
+ udelay(2);
+
+ reset_set_enable(PERIPH_ID_HOST1X, 0);
+ reset_set_enable(PERIPH_ID_DISP1, 0);
+ reset_set_enable(PERIPH_ID_PWM, 0);
+ reset_set_enable(PERIPH_ID_DPAUX, 0);
+ reset_set_enable(PERIPH_ID_SOR0, 0);
+
+ ret = display_init(lcdbase, 1 << LCD_BPP, &timing);
+ if (ret)
+ return ret;
+
+ panel_info.vl_col = roundup(timing.hactive.typ, 16);
+ panel_info.vl_row = timing.vactive.typ;
+
+ lcd_set_flush_dcache(1);
+
+ return 0;
+}
+
+void lcd_ctrl_init(void *lcdbase)
+{
+ ulong start;
+ int ret;
+
+ start = get_timer(0);
+ ret = tegra124_lcd_init(lcdbase);
+ debug("LCD init took %lu ms\n", get_timer(start));
+ if (ret)
+ printf("%s: Error %d\n", __func__, ret);
+}
+
+void lcd_enable(void)
+{
+}