tegra: nyan-big: Move the LCD driver to driver model

Adjust the driver to use driver model. The SOR becomes a bridge device. We
use the normal simple_panel driver to handle the display itself. We also
need to enable some options such as regulators, PWMs and DM_VIDEO itself.

Signed-off-by: Simon Glass <sjg@chromium.org>
Acked-by: Anatolij Gustschin <agust@denx.de>
Signed-off-by: Tom Warren <twarren@nvidia.com>
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 19f9429..72afae0 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -383,6 +383,7 @@
 
 config VIDEO_TEGRA124
 	bool "Enable video support on Tegra124"
+	depends on DM_VIDEO
 	help
 	   Tegra124 supports many video output options including eDP and
 	   HDMI. At present only eDP is supported by U-Boot. This option
diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index b161517..c73f242 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -85,6 +85,7 @@
 
 static const struct udevice_id simple_panel_ids[] = {
 	{ .compatible = "simple-panel" },
+	{ .compatible = "auo,b133xtn01" },
 	{ }
 };
 
diff --git a/drivers/video/tegra124/display.c b/drivers/video/tegra124/display.c
index b4c4093..2f1f0df 100644
--- a/drivers/video/tegra124/display.c
+++ b/drivers/video/tegra124/display.c
@@ -14,11 +14,13 @@
 #include <edid.h>
 #include <fdtdec.h>
 #include <lcd.h>
+#include <video.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 <dm/uclass-internal.h>
 #include "displayport.h"
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -333,73 +335,46 @@
 	return 0;
 }
 
-/* Somewhat torturous method */
-static int get_backlight_info(const void *blob, struct gpio_desc *vdd,
-			      struct gpio_desc *enable, int *pwmp)
+static int display_init(struct udevice *dev, void *lcdbase,
+			int fb_bits_per_pixel, struct display_timing *timing)
 {
-	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;
-}
-
-static int display_init(void *lcdbase, int fb_bits_per_pixel,
-			struct display_timing *timing)
-{
+	struct display_plat *disp_uc_plat;
 	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;
 
+	/*
+	 * Before we probe the display device (eDP), tell it that this device
+	 * is are the source of the display data.
+	 */
+	ret = uclass_find_first_device(UCLASS_DISPLAY, &dp_dev);
+	if (ret) {
+		debug("%s: device '%s' display not found (ret=%d)\n", __func__,
+		      dev->name, ret);
+		return ret;
+	}
+
+	disp_uc_plat = dev_get_uclass_platdata(dp_dev);
+	debug("Found device '%s', disp_uc_priv=%p\n", dp_dev->name,
+	      disp_uc_plat);
+	disp_uc_plat->src_dev = dev;
+
 	ret = uclass_get_device(UCLASS_DISPLAY, 0, &dp_dev);
-	if (ret)
+	if (ret) {
+		debug("%s: Failed to probe eDP, ret=%d\n", __func__, 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))
+	dc_ctlr = (struct dc_ctlr *)fdtdec_get_addr(blob, dev->of_offset,
+						    "reg");
+	if (fdtdec_decode_display_timing(blob, dev->of_offset, 0, timing)) {
+		debug("%s: Failed to decode display timing\n", __func__);
 		return -EINVAL;
+	}
 
 	ret = display_update_config_from_edid(dp_dev, &panel_bpp, timing);
 	if (ret) {
@@ -407,12 +382,6 @@
 		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
@@ -443,21 +412,15 @@
 
 	/* Enable dp */
 	ret = display_enable(dp_dev, panel_bpp, timing);
-	if (ret)
+	if (ret) {
+		debug("dc: failed to enable display: ret=%d\n", ret);
 		return ret;
+	}
 
 	ret = update_window(dc_ctlr, (ulong)lcdbase, fb_bits_per_pixel, timing);
-	if (ret)
+	if (ret) {
+		debug("dc: failed to update window\n");
 		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;
@@ -470,29 +433,10 @@
 	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)
+static int tegra124_lcd_init(struct udevice *dev, void *lcdbase,
+			     enum video_log2_bpp l2bpp)
 {
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 	struct display_timing timing;
 	int ret;
 
@@ -512,30 +456,55 @@
 	reset_set_enable(PERIPH_ID_DPAUX, 0);
 	reset_set_enable(PERIPH_ID_SOR0, 0);
 
-	ret = display_init(lcdbase, 1 << LCD_BPP, &timing);
+	ret = display_init(dev, lcdbase, 1 << l2bpp, &timing);
 	if (ret)
 		return ret;
 
-	panel_info.vl_col = roundup(timing.hactive.typ, 16);
-	panel_info.vl_row = timing.vactive.typ;
+	uc_priv->xsize = roundup(timing.hactive.typ, 16);
+	uc_priv->ysize = timing.vactive.typ;
+	uc_priv->bpix = l2bpp;
 
-	lcd_set_flush_dcache(1);
+	video_set_flush_dcache(dev, 1);
+	debug("%s: done\n", __func__);
 
 	return 0;
 }
 
-void lcd_ctrl_init(void *lcdbase)
+static int tegra124_lcd_probe(struct udevice *dev)
 {
+	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 	ulong start;
 	int ret;
 
 	start = get_timer(0);
-	ret = tegra124_lcd_init(lcdbase);
+	ret = tegra124_lcd_init(dev, (void *)plat->base, VIDEO_BPP16);
 	debug("LCD init took %lu ms\n", get_timer(start));
 	if (ret)
 		printf("%s: Error %d\n", __func__, ret);
+
+	return 0;
 }
 
-void lcd_enable(void)
+static int tegra124_lcd_bind(struct udevice *dev)
 {
+	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+
+	uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
+			(1 << VIDEO_BPP16) / 8;
+	debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
+
+	return 0;
 }
+
+static const struct udevice_id tegra124_lcd_ids[] = {
+	{ .compatible = "nvidia,tegra124-dc" },
+	{ }
+};
+
+U_BOOT_DRIVER(tegra124_dc) = {
+	.name	= "tegra124-dc",
+	.id	= UCLASS_VIDEO,
+	.of_match = tegra124_lcd_ids,
+	.bind	= tegra124_lcd_bind,
+	.probe	= tegra124_lcd_probe,
+};
diff --git a/drivers/video/tegra124/dp.c b/drivers/video/tegra124/dp.c
index bb1805a..5bf8524 100644
--- a/drivers/video/tegra124/dp.c
+++ b/drivers/video/tegra124/dp.c
@@ -11,6 +11,7 @@
 #include <div64.h>
 #include <errno.h>
 #include <fdtdec.h>
+#include <video_bridge.h>
 #include <asm/io.h>
 #include <asm/arch-tegra/dc.h>
 #include "display.h"
@@ -26,9 +27,15 @@
 	ulong base;
 };
 
+/**
+ * struct tegra_dp_priv - private displayport driver info
+ *
+ * @dc_dev:	Display controller device that is sending the video feed
+ */
 struct tegra_dp_priv {
+	struct udevice *sor;
+	struct udevice *dc_dev;
 	struct dpaux_ctlr *regs;
-	struct tegra_dc_sor_data *sor;
 	u8 revision;
 	int enabled;
 };
@@ -710,8 +717,8 @@
 	return 0;
 }
 
-static int tegra_dc_dp_set_assr(struct tegra_dp_priv *dp,
-				struct tegra_dc_sor_data *sor, int ena)
+static int tegra_dc_dp_set_assr(struct tegra_dp_priv *priv,
+				struct udevice *sor, int ena)
 {
 	int ret;
 
@@ -719,7 +726,7 @@
 		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,
+	ret = tegra_dc_dp_dpcd_write(priv, DP_EDP_CONFIGURATION_SET,
 				     dpcd_data);
 	if (ret)
 		return ret;
@@ -730,7 +737,7 @@
 }
 
 static int tegra_dp_set_link_bandwidth(struct tegra_dp_priv *dp,
-				       struct tegra_dc_sor_data *sor,
+				       struct udevice *sor,
 				       u8 link_bw)
 {
 	tegra_dc_sor_set_link_bandwidth(sor, link_bw);
@@ -741,7 +748,7 @@
 
 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)
+		struct udevice *sor)
 {
 	u8	dpcd_data;
 	int	ret;
@@ -1002,7 +1009,7 @@
 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;
+	struct udevice *sor = dp->sor;
 	u32 n_lanes = cfg->lane_count;
 	u8 pc_supported = cfg->tps3_supported;
 	u32 cnt;
@@ -1186,7 +1193,7 @@
 					  const struct display_timing *timing,
 					  struct tegra_dp_link_config *cfg)
 {
-	struct tegra_dc_sor_data *sor = dp->sor;
+	struct udevice *sor = dp->sor;
 	int err;
 	u32 pe[4], vs[4], pc[4];
 
@@ -1229,7 +1236,7 @@
  */
 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)
+		struct udevice *sor)
 {
 	u8	link_bw;
 	u8	lane_count;
@@ -1301,7 +1308,7 @@
 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)
+		struct udevice *sor)
 {
 	u8	link_bw;
 	u8	lane_count;
@@ -1344,7 +1351,7 @@
 
 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,
+			struct udevice *sor,
 			const struct display_timing *timing)
 {
 	struct tegra_dp_link_config temp_cfg;
@@ -1444,7 +1451,7 @@
 			printf("DP: Out of sync after %d retries\n", max_retry);
 			return -EIO;
 		}
-		ret = tegra_dc_sor_detach(dp->sor);
+		ret = tegra_dc_sor_detach(dp->dc_dev, dp->sor);
 		if (ret)
 			return ret;
 		if (tegra_dc_dp_explore_link_cfg(dp, link_cfg, dp->sor,
@@ -1454,7 +1461,7 @@
 		}
 
 		tegra_dc_sor_set_power_state(dp->sor, 1);
-		tegra_dc_sor_attach(dp->sor, link_cfg, timing);
+		tegra_dc_sor_attach(dp->dc_dev, dp->sor, link_cfg, timing);
 
 		/* Increase delay_frame for next try in case the sink is
 		   skipping more frames */
@@ -1467,7 +1474,7 @@
 {
 	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;
+	struct udevice *sor;
 	int data;
 	int retry;
 	int ret;
@@ -1489,9 +1496,11 @@
 		return -ENOLINK;
 	}
 
-	ret = tegra_dc_sor_init(&sor);
-	if (ret)
+	ret = uclass_first_device(UCLASS_VIDEO_BRIDGE, &sor);
+	if (ret || !sor) {
+		debug("dp: failed to find SOR device: ret=%d\n", ret);
 		return ret;
+	}
 	priv->sor = sor;
 	ret = tegra_dc_sor_enable_dp(sor, link_cfg);
 	if (ret)
@@ -1531,7 +1540,7 @@
 	}
 
 	tegra_dc_sor_set_power_state(sor, 1);
-	ret = tegra_dc_sor_attach(sor, link_cfg, timing);
+	ret = tegra_dc_sor_attach(priv->dc_dev, sor, link_cfg, timing);
 	if (ret && ret != -EEXIST)
 		return ret;
 
@@ -1548,6 +1557,12 @@
 	/* Power down the unused lanes to save power - a few hundred mW */
 	tegra_dc_sor_power_down_unused_lanes(sor, link_cfg);
 
+	ret = video_bridge_set_backlight(sor, 80);
+	if (ret) {
+		debug("dp: failed to set backlight\n");
+		return ret;
+	}
+
 	priv->enabled = true;
 error_enable:
 	return 0;
@@ -1583,10 +1598,14 @@
 {
 	struct tegra_dp_plat *plat = dev_get_platdata(dev);
 	struct tegra_dp_priv *priv = dev_get_priv(dev);
+	struct display_plat *disp_uc_plat = dev_get_uclass_platdata(dev);
 
 	priv->regs = (struct dpaux_ctlr *)plat->base;
 	priv->enabled = false;
 
+	/* Remember the display controller that is sending us video */
+	priv->dc_dev = disp_uc_plat->src_dev;
+
 	return 0;
 }
 
diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra124/sor.c
index aa3d80c..e5cea51 100644
--- a/drivers/video/tegra124/sor.c
+++ b/drivers/video/tegra124/sor.c
@@ -5,9 +5,12 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <errno.h>
 #include <fdtdec.h>
 #include <malloc.h>
+#include <panel.h>
+#include <video_bridge.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch-tegra/dc.h>
@@ -37,6 +40,14 @@
 #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF		(0 << 25)
 #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON		(1 << 25)
 
+struct tegra_dc_sor_data {
+	void *base;
+	void *pmc_base;
+	u8 portnum;	/* 0 or 1 */
+	int power_is_up;
+	struct udevice *panel;
+};
+
 static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg)
 {
 	return readl((u32 *)sor->base + reg);
@@ -57,15 +68,19 @@
 	tegra_sor_writel(sor, reg, reg_val);
 }
 
-void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor)
+void tegra_dp_disable_tx_pu(struct udevice *dev)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
 	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,
+void tegra_dp_set_pe_vs_pc(struct udevice *dev, u32 mask, u32 pe_reg,
 			   u32 vs_reg, u32 pc_reg, u8 pc_supported)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
 	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) {
@@ -95,8 +110,9 @@
 	return -ETIMEDOUT;
 }
 
-int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd)
+int tegra_dc_sor_set_power_state(struct udevice *dev, int pu_pd)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 	u32 orig_val;
 
@@ -123,10 +139,11 @@
 	return 0;
 }
 
-void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena,
+void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, int ena,
 				 u8 training_pattern,
 				 const struct tegra_dp_link_config *link_cfg)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 
 	reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
@@ -194,9 +211,10 @@
 	return 0;
 }
 
-static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor,
+static int tegra_dc_sor_power_dplanes(struct udevice *dev,
 				      u32 lane_count, int pu)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 
 	reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
@@ -218,15 +236,15 @@
 		}
 
 		tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val);
-		tegra_dc_sor_set_lane_count(sor, lane_count);
+		tegra_dc_sor_set_lane_count(dev, 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)
+void tegra_dc_sor_set_panel_power(struct udevice *dev, int power_up)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 
 	reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum));
@@ -255,14 +273,15 @@
 	}
 }
 
-static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor,
+static void tegra_dc_sor_set_dp_mode(struct udevice *dev,
 				const struct tegra_dp_link_config *link_cfg)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 
-	tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw);
+	tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
 
-	tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg);
+	tegra_dc_sor_set_dp_linkctl(dev, 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;
@@ -351,8 +370,9 @@
 	return 0;
 }
 
-void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int)
+void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 
 	reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum));
@@ -366,9 +386,10 @@
 	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,
+void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw,
 				   u8 *lane_count)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 
 	reg_val = tegra_sor_readl(sor, CLK_CNTRL);
@@ -395,15 +416,18 @@
 	}
 }
 
-void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw)
+void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
 	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)
+void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 reg_val;
 
 	reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum));
@@ -439,15 +463,16 @@
  * 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)
+static int tegra_dc_sor_power_up(struct udevice *dev, int is_lvds)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	int ret;
 
 	if (sor->power_is_up)
 		return 0;
 
 	/* Set link bw */
-	tegra_dc_sor_set_link_bandwidth(sor, is_lvds ?
+	tegra_dc_sor_set_link_bandwidth(dev, is_lvds ?
 					CLK_CNTRL_DP_LINK_SPEED_LVDS :
 					CLK_CNTRL_DP_LINK_SPEED_G1_62);
 
@@ -655,9 +680,10 @@
 	writel(reg_val, &disp_ctrl->cmd.state_access);
 }
 
-int tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_enable_dp(struct udevice *dev,
 			   const struct tegra_dp_link_config *link_cfg)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	int ret;
 
 	tegra_sor_write_field(sor, CLK_CNTRL,
@@ -701,7 +727,7 @@
 			      PLL2_AUX2_OVERRIDE_POWERDOWN |
 			      PLL2_AUX7_PORT_POWERDOWN_DISABLE);
 
-	ret = tegra_dc_sor_power_up(sor, 0);
+	ret = tegra_dc_sor_power_up(dev, 0);
 	if (ret) {
 		debug("DP failed to power up\n");
 		return ret;
@@ -711,18 +737,19 @@
 	clock_sor_enable_edp_clock();
 
 	/* Power up lanes */
-	tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1);
+	tegra_dc_sor_power_dplanes(dev, link_cfg->lane_count, 1);
 
-	tegra_dc_sor_set_dp_mode(sor, link_cfg);
+	tegra_dc_sor_set_dp_mode(dev, link_cfg);
 	debug("%s ret\n", __func__);
 
 	return 0;
 }
 
-int tegra_dc_sor_attach(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *dev,
 			const struct tegra_dp_link_config *link_cfg,
 			const struct display_timing *timing)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	const void *blob = gd->fdt_blob;
 	struct dc_ctlr *disp_ctrl;
 	u32 reg_val;
@@ -730,9 +757,7 @@
 
 	/* Use the first display controller */
 	debug("%s\n", __func__);
-	node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
-	if (node < 0)
-		return -ENOENT;
+	node = dc_dev->of_offset;
 	disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
 
 	tegra_dc_sor_enable_dc(disp_ctrl);
@@ -798,9 +823,11 @@
 	return 0;
 }
 
-void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_set_lane_parm(struct udevice *dev,
 		const struct tegra_dp_link_config *link_cfg)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
+
 	tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
 			 link_cfg->drive_current);
 	tegra_sor_writel(sor, PR(sor->portnum),
@@ -809,8 +836,8 @@
 			 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_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw);
+	tegra_dc_sor_set_lane_count(dev, link_cfg->lane_count);
 
 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
 			      DP_PADCTL_TX_PU_ENABLE |
@@ -825,9 +852,10 @@
 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
 }
 
-int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+int tegra_dc_sor_set_voltage_swing(struct udevice *dev,
 				    const struct tegra_dp_link_config *link_cfg)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 drive_current = 0;
 	u32 pre_emphasis = 0;
 
@@ -851,9 +879,10 @@
 	return 0;
 }
 
-void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_power_down_unused_lanes(struct udevice *dev,
 			const struct tegra_dp_link_config *link_cfg)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 pad_ctrl = 0;
 	int err = 0;
 
@@ -891,9 +920,10 @@
 	}
 }
 
-int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+int tegra_sor_precharge_lanes(struct udevice *dev,
 			      const struct tegra_dp_link_config *cfg)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	u32 val = 0;
 
 	switch (cfg->lane_count) {
@@ -931,8 +961,9 @@
 	writel(reg_val, &disp_ctrl->disp.disp_win_opt);
 }
 
-int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor)
+int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *dev)
 {
+	struct tegra_dc_sor_data *sor = dev_get_priv(dev);
 	int dc_reg_ctx[DC_REG_SAVE_SPACE];
 	const void *blob = gd->fdt_blob;
 	struct dc_ctlr *disp_ctrl;
@@ -942,11 +973,7 @@
 
 	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;
-	}
+	node = dc_dev->of_offset;
 	disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
 
 	/* Sleep mode */
@@ -997,28 +1024,61 @@
 	return ret;
 }
 
-int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp)
+static int tegra_sor_set_backlight(struct udevice *dev, int percent)
 {
+	struct tegra_dc_sor_data *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = panel_enable_backlight(priv->panel);
+	if (ret) {
+		debug("sor: Cannot enable panel backlight\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tegra_sor_ofdata_to_platdata(struct udevice *dev)
+{
+	struct tegra_dc_sor_data *priv = dev_get_priv(dev);
 	const void *blob = gd->fdt_blob;
-	struct tegra_dc_sor_data *sor;
 	int node;
+	int ret;
 
-	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");
+	priv->base = (void *)fdtdec_get_addr(blob, dev->of_offset, "reg");
 
 	node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_PMC);
-	if (node < 0)
+	if (node < 0) {
+		debug("%s: Cannot find PMC\n", __func__);
 		return -ENOENT;
-	sor->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg");
+	}
+	priv->pmc_base = (void *)fdtdec_get_addr(blob, node, "reg");
 
-	sor->power_is_up = 0;
-	sor->portnum = 0;
-	*sorp = sor;
+	ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "nvidia,panel",
+					   &priv->panel);
+	if (ret) {
+		debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__,
+		      dev->name, ret);
+		return ret;
+	}
 
 	return 0;
 }
+
+static const struct video_bridge_ops tegra_sor_ops = {
+	.set_backlight	= tegra_sor_set_backlight,
+};
+
+static const struct udevice_id tegra_sor_ids[] = {
+	{ .compatible = "nvidia,tegra124-sor" },
+	{ }
+};
+
+U_BOOT_DRIVER(sor_tegra) = {
+	.name	= "sor_tegra",
+	.id	= UCLASS_VIDEO_BRIDGE,
+	.of_match = tegra_sor_ids,
+	.ofdata_to_platdata = tegra_sor_ofdata_to_platdata,
+	.ops	= &tegra_sor_ops,
+	.priv_auto_alloc_size = sizeof(struct tegra_dc_sor_data),
+};
diff --git a/drivers/video/tegra124/sor.h b/drivers/video/tegra124/sor.h
index dc8fd03..e854bef 100644
--- a/drivers/video/tegra124/sor.h
+++ b/drivers/video/tegra124/sor.h
@@ -873,44 +873,37 @@
 	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,
+int tegra_dc_sor_enable_dp(struct udevice *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,
+int tegra_dc_sor_set_power_state(struct udevice *sor, int pu_pd);
+void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, 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,
+void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw);
+void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count);
+void tegra_dc_sor_set_panel_power(struct udevice *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,
+void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int);
+void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw,
 				   u8 *lane_count);
-void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor,
+void tegra_dc_sor_set_lane_parm(struct udevice *dev,
+		const struct tegra_dp_link_config *link_cfg);
+void tegra_dc_sor_power_down_unused_lanes(struct udevice *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,
+int tegra_dc_sor_set_voltage_swing(struct udevice *sor,
 				const struct tegra_dp_link_config *link_cfg);
-int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+int tegra_sor_precharge_lanes(struct udevice *dev,
 			      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);
+void tegra_dp_disable_tx_pu(struct udevice *sor);
+void tegra_dp_set_pe_vs_pc(struct udevice *dev, 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,
+int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *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);
+int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *sor);
 
 void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl,
 					   int *dc_reg_ctx);
@@ -918,5 +911,5 @@
 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);
+int tegra_dc_sor_init(struct udevice **sorp);
 #endif