From: Erik Gilling <konkers@android.com>
Date: Tue, 24 Aug 2010 04:28:12 +0000 (-0700)
Subject: video: tegra: add hotplug display support
X-Git-Tag: firefly_0821_release~9833^2~264
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=809b025947eec41bad5cbe7bd6dd46a3d262889f;p=firefly-linux-kernel-4.4.55.git

video: tegra: add hotplug display support

This is needed for HDMI.

Signed-off-by: Erik Gilling <konkers@android.com>
---

diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index 38ea7b2bf826..fc1911dfba13 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -57,6 +57,10 @@ enum {
 
 struct tegra_dc_out {
 	int			type;
+	unsigned		flags;
+
+	int			dcc_bus;
+	int			hotplug_gpio;
 
 	unsigned		order;
 	unsigned		align;
@@ -64,11 +68,14 @@ struct tegra_dc_out {
 	struct tegra_dc_mode	*modes;
 	int			n_modes;
 
-	int	(*init)(void);
-	int	(*suspend)(pm_message_t state);
-	int	(*resume)(void);
+	int	(*enable)(void);
+	int	(*disable)(void);
 };
 
+#define TEGRA_DC_OUT_HOTPLUG_HIGH	(0 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_LOW	(1 << 1)
+#define TEGRA_DC_OUT_HOTPLUG_MASK	(1 << 1)
+
 #define TEGRA_DC_ALIGN_MSB		0
 #define TEGRA_DC_ALIGN_LSB		1
 
@@ -143,6 +150,9 @@ struct tegra_dc_platform_data {
 struct tegra_dc *tegra_dc_get_dc(unsigned idx);
 struct tegra_dc_win *tegra_dc_get_window(struct tegra_dc *dc, unsigned win);
 
+void tegra_dc_enable(struct tegra_dc *dc);
+void tegra_dc_disable(struct tegra_dc *dc);
+
 /* tegra_dc_update_windows and tegra_dc_sync_windows do not support windows
  * with differenct dcs in one call
  */
@@ -152,6 +162,6 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n);
 /* will probably be replaced with an interface describing the window order */
 void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend);
 
-int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode);
+int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode);
 
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/fb.h b/arch/arm/mach-tegra/include/mach/fb.h
index 58a8f41b6690..f41a04929d9e 100644
--- a/arch/arm/mach-tegra/include/mach/fb.h
+++ b/arch/arm/mach-tegra/include/mach/fb.h
@@ -20,12 +20,17 @@
 #ifndef __MACH_TEGRA_FB_H
 #define __MACH_TEGRA_FB_H
 
+#include <linux/fb.h>
+
 #ifdef CONFIG_FB_TEGRA
 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 					struct tegra_dc *dc,
 					struct tegra_fb_data *fb_data,
 					struct resource *fb_mem);
 void tegra_fb_unregister(struct tegra_fb_info *fb_info);
+void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+			      struct fb_monspecs *specs,
+			      bool (*mode_filter)(struct fb_videomode *mode));
 #else
 static inline struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 						      struct tegra_dc *dc,
@@ -38,6 +43,12 @@ static inline struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev
 static inline void tegra_fb_unregister(struct tegra_fb_info *fb_info)
 {
 }
+
+void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+			      struct fb_monspecs *specs,
+			      bool (*mode_filter)(struct fb_videomode *mode))
+{
+}
 #endif
 
 #endif
diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig
index 2610d137ffb8..ee3bf59d88c1 100644
--- a/drivers/video/tegra/Kconfig
+++ b/drivers/video/tegra/Kconfig
@@ -9,6 +9,7 @@ config TEGRA_DC
 	tristate "Tegra Display Contoller"
 	depends on ARCH_TEGRA
 	select FB_MODE_HELPERS
+	select I2C
 	help
 	  Tegra display controller support.
 
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index a28b7ed0ba46..397ade7f11d9 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -117,19 +117,6 @@ static void _dump_regs(struct tegra_dc *dc, void *data,
 	int i;
 	char buff[256];
 
-	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT);
-	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
-	DUMP_REG(DC_CMD_GENERAL_INCR_SYNCPT_ERROR);
-	DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT);
-	DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL);
-	DUMP_REG(DC_CMD_WIN_A_INCR_SYNCPT_ERROR);
-	DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT);
-	DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL);
-	DUMP_REG(DC_CMD_WIN_B_INCR_SYNCPT_ERROR);
-	DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT);
-	DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL);
-	DUMP_REG(DC_CMD_WIN_C_INCR_SYNCPT_ERROR);
-	DUMP_REG(DC_CMD_CONT_SYNCPT_VSYNC);
 	DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0);
 	DUMP_REG(DC_CMD_DISPLAY_COMMAND);
 	DUMP_REG(DC_CMD_SIGNAL_RAISE);
@@ -368,12 +355,17 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 	struct tegra_dc *dc;
 	unsigned long update_mask = GENERAL_ACT_REQ;
 	unsigned long val;
-	unsigned long flags;
 	int i;
 
 	dc = windows[0]->dc;
 
-	spin_lock_irqsave(&dc->lock, flags);
+	mutex_lock(&dc->lock);
+
+	if (!dc->enabled) {
+		mutex_unlock(&dc->lock);
+		return -EFAULT;
+	}
+
 	for (i = 0; i < n; i++) {
 		struct tegra_dc_win *win = windows[i];
 		unsigned h_dda;
@@ -441,7 +433,8 @@ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n)
 	tegra_dc_writel(dc, val, DC_CMD_INT_MASK);
 
 	tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL);
-	spin_unlock_irqrestore(&dc->lock, flags);
+
+	mutex_unlock(&dc->lock);
 
 	return 0;
 }
@@ -466,6 +459,9 @@ int tegra_dc_sync_windows(struct tegra_dc_win *windows[], int n)
 	if (n < 1 || n > DC_N_WINDOWS)
 		return -EINVAL;
 
+	if (!windows[0]->dc->enabled)
+		return -EFAULT;
+
 	return wait_event_interruptible_timeout(windows[0]->dc->wq,
 					 tegra_dc_windows_are_clean(windows, n),
 					 HZ);
@@ -489,7 +485,12 @@ void tegra_dc_set_blending(struct tegra_dc *dc, struct tegra_dc_blend *blend)
 }
 EXPORT_SYMBOL(tegra_dc_set_blending);
 
-int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
+void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
+{
+	/* clock setup for DSI/HDMI to go here */
+}
+
+static int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
 {
 	unsigned long val;
 	unsigned long rate;
@@ -547,6 +548,14 @@ int tegra_dc_set_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode)
 
 	return 0;
 }
+
+
+int tegra_dc_set_mode(struct tegra_dc *dc, const struct tegra_dc_mode *mode)
+{
+	memcpy(&dc->mode, mode, sizeof(dc->mode));
+
+	return 0;
+}
 EXPORT_SYMBOL(tegra_dc_set_mode);
 
 static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
@@ -554,10 +563,7 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
 	dc->out = out;
 
 	if (out->n_modes > 0)
-		dc->mode = &dc->out->modes[0];
-	else
-		dev_err(&dc->ndev->dev,
-			"No default modes specified.  Leaving output disabled.\n");
+		tegra_dc_set_mode(dc, &dc->out->modes[0]);
 
 	switch (out->type) {
 	case TEGRA_DC_OUT_RGB:
@@ -568,6 +574,10 @@ static void tegra_dc_set_out(struct tegra_dc *dc, struct tegra_dc_out *out)
 		dc->out_ops = NULL;
 		break;
 	}
+
+	if (dc->out_ops && dc->out_ops->init)
+		dc->out_ops->init(dc);
+
 }
 
 
@@ -575,11 +585,9 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
 {
 	struct tegra_dc *dc = ptr;
 	unsigned long status;
-	unsigned long flags;
 	unsigned long val;
 	int i;
 
-
 	status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
 	tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
 
@@ -587,7 +595,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
 		int completed = 0;
 		int dirty = 0;
 
-		spin_lock_irqsave(&dc->lock, flags);
 		val = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
 		for (i = 0; i < DC_N_WINDOWS; i++) {
 			if (!(val & (WIN_A_ACT_REQ << i))) {
@@ -604,8 +611,6 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
 			tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE);
 		}
 
-		spin_unlock_irqrestore(&dc->lock, flags);
-
 		if (completed)
 			wake_up(&dc->wq);
 	}
@@ -618,7 +623,7 @@ static void tegra_dc_init(struct tegra_dc *dc)
 	tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
 	if (dc->ndev->id == 0)
 		tegra_dc_writel(dc, 0x0000011a, DC_CMD_CONT_SYNCPT_VSYNC);
-	else if (dc->ndev->id == 1)
+	else
 		tegra_dc_writel(dc, 0x0000011b, DC_CMD_CONT_SYNCPT_VSYNC);
 	tegra_dc_writel(dc, 0x00004700, DC_CMD_INT_TYPE);
 	tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_POLARITY);
@@ -628,12 +633,67 @@ static void tegra_dc_init(struct tegra_dc *dc)
 	tegra_dc_writel(dc, 0x0001c702, DC_CMD_INT_MASK);
 	tegra_dc_writel(dc, 0x0001c700, DC_CMD_INT_ENABLE);
 
-	if (dc->mode)
-		tegra_dc_set_mode(dc, dc->mode);
+	if (dc->mode.pclk)
+		tegra_dc_program_mode(dc, &dc->mode);
+}
+
+static void _tegra_dc_enable(struct tegra_dc *dc)
+{
+	if (dc->out && dc->out->enable)
+		dc->out->enable();
 
+	tegra_dc_setup_clk(dc, dc->clk);
 
-	if (dc->out_ops && dc->out_ops->init)
-		dc->out_ops->init(dc);
+	clk_enable(dc->host1x_clk);
+	clk_enable(dc->clk);
+	tegra_periph_reset_deassert(dc->clk);
+	enable_irq(dc->irq);
+
+	tegra_dc_init(dc);
+
+	if (dc->out_ops && dc->out_ops->enable)
+		dc->out_ops->enable(dc);
+
+	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
+}
+
+void tegra_dc_enable(struct tegra_dc *dc)
+{
+	mutex_lock(&dc->lock);
+
+	if (!dc->enabled) {
+		_tegra_dc_enable(dc);
+		dc->enabled = true;
+	}
+
+	mutex_unlock(&dc->lock);
+}
+
+static void _tegra_dc_disable(struct tegra_dc *dc)
+{
+	if (dc->out_ops && dc->out_ops->disable)
+		dc->out_ops->disable(dc);
+
+	disable_irq(dc->irq);
+	tegra_periph_reset_assert(dc->clk);
+	clk_disable(dc->clk);
+	clk_disable(dc->host1x_clk);
+
+	if (dc->out && dc->out->disable)
+		dc->out->disable();
+}
+
+
+void tegra_dc_disable(struct tegra_dc *dc)
+{
+	mutex_lock(&dc->lock);
+
+	if (dc->enabled) {
+		dc->enabled = false;
+		_tegra_dc_disable(dc);
+	}
+
+	mutex_unlock(&dc->lock);
 }
 
 static int tegra_dc_probe(struct nvhost_device *ndev)
@@ -688,9 +748,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 		goto err_release_resource_reg;
 	}
 
-	res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem");
-	if (res)
-		fb_mem = request_mem_region(res->start, resource_size(res), ndev->name);
+	fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem");
 
 	host1x_clk = clk_get(&ndev->dev, "host1x");
 	if (IS_ERR_OR_NULL(host1x_clk)) {
@@ -698,7 +756,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 		ret = -ENOENT;
 		goto err_iounmap_reg;
 	}
-	clk_enable(host1x_clk);
 
 	clk = clk_get(&ndev->dev, NULL);
 	if (IS_ERR_OR_NULL(clk)) {
@@ -707,8 +764,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 
 		goto err_put_host1x_clk;
 	}
-	clk_enable(clk);
-	tegra_periph_reset_deassert(clk);
 
 	dc->clk = clk;
 	dc->host1x_clk = host1x_clk;
@@ -717,9 +772,12 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 	dc->irq = irq;
 	dc->ndev = ndev;
 	dc->pdata = ndev->dev.platform_data;
-	spin_lock_init(&dc->lock);
-	init_waitqueue_head(&dc->wq);
 
+	if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
+		dc->enabled = true;
+
+	mutex_init(&dc->lock);
+	init_waitqueue_head(&dc->wq);
 
 	dc->n_windows = DC_N_WINDOWS;
 	for (i = 0; i < dc->n_windows; i++) {
@@ -734,46 +792,45 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
 		goto err_put_clk;
 	}
 
+	/* hack to ballence enable_irq calls in _tegra_dc_enable() */
+	disable_irq(dc->irq);
+
 	ret = tegra_dc_add(dc, ndev->id);
 	if (ret < 0) {
 		dev_err(&ndev->dev, "can't add dc\n");
 		goto err_free_irq;
 	}
 
-	if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) {
-		if (dc->pdata->default_out)
-			tegra_dc_set_out(dc, dc->pdata->default_out);
-		else
-			dev_err(&ndev->dev, "No default output specified.  Leaving output disabled.\n");
-	}
-
-	tegra_dc_init(dc);
-	if (dc->out && dc->out->init)
-		dc->out->init();
+	nvhost_set_drvdata(ndev, dc);
 
-	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
+	if (dc->pdata->default_out)
+		tegra_dc_set_out(dc, dc->pdata->default_out);
+	else
+		dev_err(&ndev->dev, "No default output specified.  Leaving output disabled.\n");
 
-	nvhost_set_drvdata(ndev, dc);
+	if (dc->enabled)
+		_tegra_dc_enable(dc);
 
 	tegra_dc_dbg_add(dc);
 
 	dev_info(&ndev->dev, "probed\n");
 
-	if (fb_mem && dc->pdata->fb) {
+	if (dc->pdata->fb) {
 		dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem);
 		if (IS_ERR_OR_NULL(dc->fb))
 			dc->fb = NULL;
 	}
 
+	if (dc->out_ops && dc->out_ops->detect)
+		dc->out_ops->detect(dc);
+
 	return 0;
 
 err_free_irq:
 	free_irq(irq, dc);
 err_put_clk:
-	clk_disable(clk);
 	clk_put(clk);
 err_put_host1x_clk:
-	clk_disable(host1x_clk);
 	clk_put(host1x_clk);
 err_iounmap_reg:
 	iounmap(base);
@@ -793,14 +850,16 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
 
 	if (dc->fb) {
 		tegra_fb_unregister(dc->fb);
-		release_resource(dc->fb_mem);
+		if (dc->fb_mem)
+			release_resource(dc->fb_mem);
 	}
 
+
+	if (dc->enabled)
+		_tegra_dc_disable(dc);
+
 	free_irq(dc->irq, dc);
-	tegra_periph_reset_assert(dc->clk);
-	clk_disable(dc->clk);
 	clk_put(dc->clk);
-	clk_disable(dc->host1x_clk);
 	clk_put(dc->host1x_clk);
 	iounmap(dc->base);
 	if (dc->fb_mem)
@@ -816,12 +875,8 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
 
 	dev_info(&ndev->dev, "suspend\n");
 
-	if (dc->out && dc->out->suspend)
-		dc->out->suspend(state);
-
-	disable_irq(dc->irq);
-	tegra_periph_reset_assert(dc->clk);
-	clk_disable(dc->clk);
+	if (dc->enabled)
+		_tegra_dc_disable(dc);
 
 	return 0;
 }
@@ -834,19 +889,14 @@ static int tegra_dc_resume(struct nvhost_device *ndev)
 
 	dev_info(&ndev->dev, "resume\n");
 
-	clk_enable(dc->clk);
-	tegra_periph_reset_deassert(dc->clk);
-	enable_irq(dc->irq);
-
-	for (i = 0; i < dc->n_windows; i++)
-		wins[i] = &dc->windows[i];
+	if (dc->enabled) {
+		for (i = 0; i < dc->n_windows; i++)
+			wins[i] = &dc->windows[i];
 
-	tegra_dc_init(dc);
-	if (dc->out && dc->out->resume)
-		dc->out->resume();
+		_tegra_dc_enable(dc);
 
-	tegra_dc_set_blending(dc, tegra_dc_blend_modes[0]);
-	tegra_dc_update_windows(wins, dc->n_windows);
+		tegra_dc_update_windows(wins, dc->n_windows);
+	}
 
 	return 0;
 }
@@ -900,4 +950,4 @@ static void __exit tegra_dc_module_exit(void)
 }
 
 module_exit(tegra_dc_module_exit);
-module_init(tegra_dc_module_init);
+late_initcall(tegra_dc_module_init);
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index a2e961e7e6b7..1bc1c770ec1f 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -18,14 +18,24 @@
 #ifndef __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
 #define __DRIVERS_VIDEO_TEGRA_DC_DC_PRIV_H
 
-#include <linux/wait.h>
-#include <linux/list.h>
 #include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
 
 struct tegra_dc;
 
 struct tegra_dc_out_ops {
-	void (*init)(struct tegra_dc *dc);
+	/* initialize output.  dc clocks are not on at this point */
+	int (*init)(struct tegra_dc *dc);
+	/* destroy output.  dc clocks are not on at this point */
+	void (*destroy)(struct tegra_dc *dc);
+	/* detect connected display.  can sleep.*/
+	bool (*detect)(struct tegra_dc *dc);
+	/* enable output.  dc clocks are on at this point */
+	void (*enable)(struct tegra_dc *dc);
+	/* disable output.  dc clocks are on at this point */
+	void (*disable)(struct tegra_dc *dc);
 };
 
 struct tegra_dc {
@@ -41,17 +51,20 @@ struct tegra_dc {
 	struct clk			*clk;
 	struct clk			*host1x_clk;
 
+	bool				enabled;
+
 	struct tegra_dc_out		*out;
 	struct tegra_dc_out_ops		*out_ops;
+	void				*out_data;
 
-	struct tegra_dc_mode		*mode;
+	struct tegra_dc_mode		mode;
 
 	struct tegra_dc_win		windows[DC_N_WINDOWS];
 	int				n_windows;
 
 	wait_queue_head_t		wq;
 
-	spinlock_t			lock;
+	struct mutex			lock;
 
 	struct resource			*fb_mem;
 	struct tegra_fb_info		*fb;
@@ -81,6 +94,19 @@ static inline void _tegra_dc_write_table(struct tegra_dc *dc, const u32 *table,
 #define tegra_dc_write_table(dc, table)		\
 	_tegra_dc_write_table(dc, table, ARRAY_SIZE(table) / 2)
 
+static inline void tegra_dc_set_outdata(struct tegra_dc *dc, void *data)
+{
+	dc->out_data = data;
+}
+
+static inline void *tegra_dc_get_outdata(struct tegra_dc *dc)
+{
+	return dc->out_data;
+}
+
+void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk);
+
 extern struct tegra_dc_out_ops tegra_dc_rgb_ops;
+extern struct tegra_dc_out_ops tegra_dc_hdmi_ops;
 
 #endif
diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h
index 6d6b3ba006a3..f7342cc6eb0a 100644
--- a/drivers/video/tegra/dc/dc_reg.h
+++ b/drivers/video/tegra/dc/dc_reg.h
@@ -136,11 +136,31 @@
 #define DC_COM_CRC_CHECKSUM_LATCHED		0x329
 
 #define DC_DISP_DISP_SIGNAL_OPTIONS0		0x400
+#define  H_PULSE_0_ENABLE		(1 << 8)
+#define  H_PULSE_1_ENABLE		(1 << 10)
+#define  H_PULSE_2_ENABLE		(1 << 12)
+#define  V_PULSE_0_ENABLE		(1 << 16)
+#define  V_PULSE_1_ENABLE		(1 << 18)
+#define  V_PULSE_2_ENABLE		(1 << 19)
+#define  V_PULSE_3_ENABLE		(1 << 20)
+#define  M0_ENABLE			(1 << 24)
+#define  M1_ENABLE			(1 << 26)
+
 #define DC_DISP_DISP_SIGNAL_OPTIONS1		0x401
+#define  DI_ENABLE			(1 << 16)
+#define  PP_ENABLE			(1 << 18)
+
 #define DC_DISP_DISP_WIN_OPTIONS		0x402
+#define  CURSOR_ENABLE			(1 << 16)
+#define  TVO_ENABLE			(1 << 28)
+#define  DSI_ENABLE			(1 << 29)
+#define  HDMI_ENABLE			(1 << 30)
+
 #define DC_DISP_MEM_HIGH_PRIORITY		0x403
 #define DC_DISP_MEM_HIGH_PRIORITY_TIMER		0x404
 #define DC_DISP_DISP_TIMING_OPTIONS		0x405
+#define  VSYNC_H_POSITION(x)		((x) & 0xfff)
+
 #define DC_DISP_REF_TO_SYNC			0x406
 #define DC_DISP_SYNC_WIDTH			0x407
 #define DC_DISP_BACK_PORCH			0x408
@@ -181,6 +201,26 @@
 #define DC_DISP_PP_SELECT_B			0x42b
 #define DC_DISP_PP_SELECT_C			0x42c
 #define DC_DISP_PP_SELECT_D			0x42d
+
+#define  PULSE_MODE_NORMAL		(0 << 3)
+#define  PULSE_MODE_ONE_CLOCK		(1 << 3)
+#define  PULSE_POLARITY_HIGH		(0 << 4)
+#define  PULSE_POLARITY_LOW		(1 << 4)
+#define  PULSE_QUAL_ALWAYS		(0 << 6)
+#define  PULSE_QUAL_VACTIVE		(2 << 6)
+#define  PULSE_QUAL_VACTIVE1		(3 << 6)
+#define  PULSE_LAST_START_A		(0 << 8)
+#define  PULSE_LAST_END_A		(1 << 8)
+#define  PULSE_LAST_START_B		(2 << 8)
+#define  PULSE_LAST_END_B		(3 << 8)
+#define  PULSE_LAST_START_C		(4 << 8)
+#define  PULSE_LAST_END_C		(5 << 8)
+#define  PULSE_LAST_START_D		(6 << 8)
+#define  PULSE_LAST_END_D		(7 << 8)
+
+#define  PULSE_START(x)			((x) & 0xfff)
+#define  PULSE_END(x)			(((x) & 0xfff) << 16)
+
 #define DC_DISP_DISP_CLOCK_CONTROL		0x42e
 #define  PIXEL_CLK_DIVIDER_PCD1		(0 << 8)
 #define  PIXEL_CLK_DIVIDER_PCD1H	(1 << 8)
@@ -213,6 +253,20 @@
 #define  DISP_DATA_ORDER_BLUE_RED	(1 << 9)
 
 #define DC_DISP_DISP_COLOR_CONTROL		0x430
+#define  BASE_COLOR_SIZE666		(0 << 0)
+#define  BASE_COLOR_SIZE111		(1 << 0)
+#define  BASE_COLOR_SIZE222		(2 << 0)
+#define  BASE_COLOR_SIZE333		(3 << 0)
+#define  BASE_COLOR_SIZE444		(4 << 0)
+#define  BASE_COLOR_SIZE555		(5 << 0)
+#define  BASE_COLOR_SIZE565		(6 << 0)
+#define  BASE_COLOR_SIZE332		(7 << 0)
+#define  BASE_COLOR_SIZE888		(8 << 0)
+
+#define  DITHER_CONTROL_DISABLE		(0 << 8)
+#define  DITHER_CONTROL_ORDERED		(2 << 8)
+#define  DITHER_CONTROL_ERRDIFF		(3 << 8)
+
 #define DC_DISP_SHIFT_CLOCK_OPTIONS		0x431
 #define DC_DISP_DATA_ENABLE_OPTIONS		0x432
 #define   DE_SELECT_ACTIVE_BLANK	0x0
diff --git a/drivers/video/tegra/dc/rgb.c b/drivers/video/tegra/dc/rgb.c
index de1a8fabd25e..c56e1f9e126b 100644
--- a/drivers/video/tegra/dc/rgb.c
+++ b/drivers/video/tegra/dc/rgb.c
@@ -46,7 +46,7 @@ static const u32 tegra_dc_rgb_pintable[] = {
 };
 
 
-void tegra_dc_rgb_init(struct tegra_dc *dc)
+void tegra_dc_rgb_enable(struct tegra_dc *dc)
 {
 	tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
 			PW4_ENABLE | PM0_ENABLE | PM1_ENABLE,
@@ -58,6 +58,6 @@ void tegra_dc_rgb_init(struct tegra_dc *dc)
 }
 
 struct tegra_dc_out_ops tegra_dc_rgb_ops = {
-	.init = tegra_dc_rgb_init,
+	.enable = tegra_dc_rgb_enable,
 };
 
diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c
index c50c0565032e..95c7c489b513 100644
--- a/drivers/video/tegra/fb.c
+++ b/drivers/video/tegra/fb.c
@@ -35,6 +35,7 @@ struct tegra_fb_info {
 	struct tegra_dc_win	*win;
 	struct nvhost_device	*ndev;
 	struct fb_info		*info;
+	bool			valid;
 
 	struct resource		*fb_mem;
 
@@ -69,12 +70,13 @@ static int tegra_fb_release(struct fb_info *info, int user)
 static int tegra_fb_check_var(struct fb_var_screeninfo *var,
 			      struct fb_info *info)
 {
-	if ((var->xres != info->var.xres) ||
-	    (var->yres != info->var.yres) ||
-	    (var->xres_virtual != info->var.xres_virtual) ||
-	    (var->yres_virtual != info->var.yres_virtual) ||
-	    (var->grayscale != info->var.grayscale))
+	if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) >
+	    info->screen_size)
 		return -EINVAL;
+
+	/* double yres_virtual to allow double buffering through pan_display */
+	var->yres_virtual = var->yres * 2;
+
 	return 0;
 }
 
@@ -104,13 +106,39 @@ static int tegra_fb_set_par(struct fb_info *info)
 		var->blue.length = 5;
 		tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5;
 		break;
+
+	case 0:
+		break;
+
 	default:
 		return -EINVAL;
 	}
 	info->fix.line_length = var->xres * var->bits_per_pixel / 8;
 
-	tegra_dc_update_windows(&tegra_fb->win, 1);
+	if (var->pixclock) {
+		struct tegra_dc_mode mode;
 
+		info->mode = (struct fb_videomode *)
+			fb_find_best_mode(var, &info->modelist);
+		if (!info->mode) {
+			dev_warn(&tegra_fb->ndev->dev, "can't match video mode\n");
+			return -EINVAL;
+		}
+
+		mode.pclk = PICOS2KHZ(info->mode->pixclock) * 1000;
+		mode.h_ref_to_sync = 1;
+		mode.v_ref_to_sync = 1;
+		mode.h_sync_width = info->mode->hsync_len;
+		mode.v_sync_width = info->mode->vsync_len;
+		mode.h_back_porch = info->mode->left_margin;
+		mode.v_back_porch = info->mode->upper_margin;
+		mode.h_active = info->mode->xres;
+		mode.v_active = info->mode->yres;
+		mode.h_front_porch = info->mode->right_margin;
+		mode.v_front_porch = info->mode->lower_margin;
+
+		tegra_dc_set_mode(tegra_fb->win->dc, &mode);
+	}
 	return 0;
 }
 
@@ -136,6 +164,26 @@ static int tegra_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
 	return 0;
 }
 
+static int tegra_fb_blank(int blank, struct fb_info *info)
+{
+	struct tegra_fb_info *tegra_fb = info->par;
+
+	switch (blank) {
+	case FB_BLANK_UNBLANK:
+		dev_dbg(&tegra_fb->ndev->dev, "unblank\n");
+		tegra_dc_enable(tegra_fb->win->dc);
+		return 0;
+
+	case FB_BLANK_POWERDOWN:
+		dev_dbg(&tegra_fb->ndev->dev, "blank\n");
+		tegra_dc_disable(tegra_fb->win->dc);
+		return 0;
+
+	default:
+		return -ENOTTY;
+	}
+}
+
 static int tegra_fb_pan_display(struct fb_var_screeninfo *var,
 				struct fb_info *info)
 {
@@ -187,12 +235,47 @@ static struct fb_ops tegra_fb_ops = {
 	.fb_check_var = tegra_fb_check_var,
 	.fb_set_par = tegra_fb_set_par,
 	.fb_setcolreg = tegra_fb_setcolreg,
+	.fb_blank = tegra_fb_blank,
 	.fb_pan_display = tegra_fb_pan_display,
 	.fb_fillrect = tegra_fb_fillrect,
 	.fb_copyarea = tegra_fb_copyarea,
 	.fb_imageblit = tegra_fb_imageblit,
 };
 
+void tegra_fb_update_monspecs(struct tegra_fb_info *fb_info,
+			      struct fb_monspecs *specs,
+			      bool (*mode_filter)(struct fb_videomode *mode))
+{
+	struct fb_event event;
+	int i;
+
+	mutex_lock(&fb_info->info->lock);
+	fb_destroy_modedb(fb_info->info->monspecs.modedb);
+
+	memcpy(&fb_info->info->monspecs, specs,
+	       sizeof(fb_info->info->monspecs));
+
+	fb_destroy_modelist(&fb_info->info->modelist);
+
+	for (i = 0; i < specs->modedb_len; i++) {
+		if (mode_filter) {
+			if (mode_filter(&specs->modedb[i]))
+				fb_add_videomode(&specs->modedb[i],
+						 &fb_info->info->modelist);
+		} else {
+			fb_add_videomode(&specs->modedb[i],
+					 &fb_info->info->modelist);
+		}
+	}
+
+	fb_info->info->mode = (struct fb_videomode *)
+		fb_find_best_display(specs, &fb_info->info->modelist);
+
+	event.info = fb_info->info;
+	fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event);
+	mutex_unlock(&fb_info->info->lock);
+}
+
 struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 					struct tegra_dc *dc,
 					struct tegra_fb_data *fb_data,
@@ -201,9 +284,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 	struct tegra_dc_win *win;
 	struct fb_info *info;
 	struct tegra_fb_info *tegra_fb;
-	void __iomem *fb_base;
-	unsigned long fb_size;
-	unsigned long fb_phys;
+	void __iomem *fb_base = NULL;
+	unsigned long fb_size = 0;	unsigned long fb_phys = 0;
 	int ret = 0;
 
 	win = tegra_dc_get_window(dc, fb_data->win);
@@ -219,15 +301,6 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 		goto err;
 	}
 
-	fb_size = resource_size(fb_mem);
-	fb_phys = fb_mem->start;
-	fb_base = ioremap_nocache(fb_phys, fb_size);
-	if (!fb_base) {
-		dev_err(&ndev->dev, "fb can't be mapped\n");
-		ret = -EBUSY;
-		goto err_free;
-	}
-
 	tegra_fb = info->par;
 	tegra_fb->win = win;
 	tegra_fb->ndev = ndev;
@@ -236,6 +309,18 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 	tegra_fb->yres = fb_data->yres;
 	atomic_set(&tegra_fb->in_use, 0);
 
+	if (fb_mem) {
+		fb_size = resource_size(fb_mem);
+		fb_phys = fb_mem->start;
+		fb_base = ioremap_nocache(fb_phys, fb_size);
+		if (!fb_base) {
+			dev_err(&ndev->dev, "fb can't be mapped\n");
+			ret = -EBUSY;
+			goto err_free;
+		}
+		tegra_fb->valid = true;
+	}
+
 	info->fbops = &tegra_fb_ops;
 	info->pseudo_palette = pseudo_palette;
 	info->screen_base = fb_base;
@@ -253,13 +338,13 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 	info->var.xres			= fb_data->xres;
 	info->var.yres			= fb_data->yres;
 	info->var.xres_virtual		= fb_data->xres;
-	info->var.yres_virtual		= fb_data->yres*2;
+	info->var.yres_virtual		= fb_data->yres * 2;
 	info->var.bits_per_pixel	= fb_data->bits_per_pixel;
 	info->var.activate		= FB_ACTIVATE_VBL;
 	/* TODO: fill in the following by querying the DC */
 	info->var.height		= -1;
 	info->var.width			= -1;
-	info->var.pixclock		= 24500;
+	info->var.pixclock		= 0;
 	info->var.left_margin		= 0;
 	info->var.right_margin		= 0;
 	info->var.upper_margin		= 0;
@@ -279,7 +364,8 @@ struct tegra_fb_info *tegra_fb_register(struct nvhost_device *ndev,
 	win->virt_addr = fb_base;
 	win->flags = TEGRA_WIN_FLAG_ENABLED | TEGRA_WIN_FLAG_COLOR_EXPAND;
 
-	tegra_fb_set_par(info);
+	if (fb_mem)
+		tegra_fb_set_par(info);
 
 	if (register_framebuffer(info)) {
 		dev_err(&ndev->dev, "failed to register framebuffer\n");