summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS17
-rw-r--r--drivers/video/omap/dispc.c18
-rw-r--r--drivers/video/omap/lcd_htcherald.c4
-rw-r--r--drivers/video/omap/omapfb.h2
-rw-r--r--drivers/video/omap/omapfb_main.c25
-rw-r--r--drivers/video/omap/rfbi.c4
-rw-r--r--drivers/video/omap2/dss/Kconfig7
-rw-r--r--drivers/video/omap2/dss/core.c10
-rw-r--r--drivers/video/omap2/dss/dispc.c74
-rw-r--r--drivers/video/omap2/dss/dsi.c159
-rw-r--r--drivers/video/omap2/dss/dss.c6
-rw-r--r--drivers/video/omap2/dss/dss.h14
-rw-r--r--drivers/video/omap2/dss/rfbi.c30
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c6
14 files changed, 309 insertions, 67 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 6914588eef89..c8f47bf154f4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3940,29 +3940,20 @@ S:	Maintained
 F:	sound/soc/omap/
 
 OMAP FRAMEBUFFER SUPPORT
-M:	Imre Deak <imre.deak@nokia.com>
+M:	Tomi Valkeinen <tomi.valkeinen@nokia.com>
 L:	linux-fbdev@vger.kernel.org
 L:	linux-omap@vger.kernel.org
 S:	Maintained
 F:	drivers/video/omap/
 
-OMAP DISPLAY SUBSYSTEM SUPPORT (DSS2)
+OMAP DISPLAY SUBSYSTEM and FRAMEBUFFER SUPPORT (DSS2)
 M:	Tomi Valkeinen <tomi.valkeinen@nokia.com>
 L:	linux-omap@vger.kernel.org
-L:	linux-fbdev@vger.kernel.org (moderated for non-subscribers)
+L:	linux-fbdev@vger.kernel.org
 S:	Maintained
-F:	drivers/video/omap2/dss/
-F:	drivers/video/omap2/vrfb.c
-F:	drivers/video/omap2/vram.c
+F:	drivers/video/omap2/
 F:	Documentation/arm/OMAP/DSS
 
-OMAP FRAMEBUFFER SUPPORT (FOR DSS2)
-M:	Tomi Valkeinen <tomi.valkeinen@nokia.com>
-L:	linux-omap@vger.kernel.org
-L:	linux-fbdev@vger.kernel.org (moderated for non-subscribers)
-S:	Maintained
-F:	drivers/video/omap2/omapfb/
-
 OMAP MMC SUPPORT
 M:	Jarkko Lavinen <jarkko.lavinen@nokia.com>
 L:	linux-omap@vger.kernel.org
diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c
index c7c6455f1fa8..e192b058a688 100644
--- a/drivers/video/omap/dispc.c
+++ b/drivers/video/omap/dispc.c
@@ -189,11 +189,6 @@ static struct {
 	struct omapfb_color_key	color_key;
 } dispc;
 
-static struct platform_device omapdss_device = {
-	.name		= "omapdss",
-	.id		= -1,
-};
-
 static void enable_lcd_clocks(int enable);
 
 static void inline dispc_write_reg(int idx, u32 val)
@@ -920,20 +915,20 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
 
 static int get_dss_clocks(void)
 {
-	dispc.dss_ick = clk_get(&omapdss_device.dev, "ick");
+	dispc.dss_ick = clk_get(&dispc.fbdev->dssdev->dev, "ick");
 	if (IS_ERR(dispc.dss_ick)) {
 		dev_err(dispc.fbdev->dev, "can't get ick\n");
 		return PTR_ERR(dispc.dss_ick);
 	}
 
-	dispc.dss1_fck = clk_get(&omapdss_device.dev, "dss1_fck");
+	dispc.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "dss1_fck");
 	if (IS_ERR(dispc.dss1_fck)) {
 		dev_err(dispc.fbdev->dev, "can't get dss1_fck\n");
 		clk_put(dispc.dss_ick);
 		return PTR_ERR(dispc.dss1_fck);
 	}
 
-	dispc.dss_54m_fck = clk_get(&omapdss_device.dev, "tv_fck");
+	dispc.dss_54m_fck = clk_get(&dispc.fbdev->dssdev->dev, "tv_fck");
 	if (IS_ERR(dispc.dss_54m_fck)) {
 		dev_err(dispc.fbdev->dev, "can't get tv_fck\n");
 		clk_put(dispc.dss_ick);
@@ -1385,12 +1380,6 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
 	int skip_init = 0;
 	int i;
 
-	r = platform_device_register(&omapdss_device);
-	if (r) {
-		dev_err(fbdev->dev, "can't register omapdss device\n");
-		return r;
-	}
-
 	memset(&dispc, 0, sizeof(dispc));
 
 	dispc.base = ioremap(DISPC_BASE, SZ_1K);
@@ -1534,7 +1523,6 @@ static void omap_dispc_cleanup(void)
 	free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
 	put_dss_clocks();
 	iounmap(dispc.base);
-	platform_device_unregister(&omapdss_device);
 }
 
 const struct lcd_ctrl omap2_int_ctrl = {
diff --git a/drivers/video/omap/lcd_htcherald.c b/drivers/video/omap/lcd_htcherald.c
index a9007c5d1fad..4802419da83b 100644
--- a/drivers/video/omap/lcd_htcherald.c
+++ b/drivers/video/omap/lcd_htcherald.c
@@ -115,12 +115,12 @@ struct platform_driver htcherald_panel_driver = {
 	},
 };
 
-static int htcherald_panel_drv_init(void)
+static int __init htcherald_panel_drv_init(void)
 {
 	return platform_driver_register(&htcherald_panel_driver);
 }
 
-static void htcherald_panel_drv_cleanup(void)
+static void __exit htcherald_panel_drv_cleanup(void)
 {
 	platform_driver_unregister(&htcherald_panel_driver);
 }
diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h
index 46e4714014e8..af3c9e571ec3 100644
--- a/drivers/video/omap/omapfb.h
+++ b/drivers/video/omap/omapfb.h
@@ -203,6 +203,8 @@ struct omapfb_device {
 
 	struct omapfb_mem_desc		mem_desc;
 	struct fb_info			*fb_info[OMAPFB_PLANE_NUM];
+
+	struct platform_device	*dssdev;	/* dummy dev for clocks */
 };
 
 #ifdef CONFIG_ARCH_OMAP1
diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c
index c7f59a5ccdbc..2c4f470fa086 100644
--- a/drivers/video/omap/omapfb_main.c
+++ b/drivers/video/omap/omapfb_main.c
@@ -83,6 +83,19 @@ static struct caps_table_struct color_caps[] = {
 	{ 1 << OMAPFB_COLOR_YUY422,	"YUY422", },
 };
 
+static void omapdss_release(struct device *dev)
+{
+}
+
+/* dummy device for clocks */
+static struct platform_device omapdss_device = {
+	.name		= "omapdss",
+	.id		= -1,
+	.dev            = {
+		.release = omapdss_release,
+	},
+};
+
 /*
  * ---------------------------------------------------------------------------
  * LCD panel
@@ -1700,6 +1713,7 @@ static int omapfb_do_probe(struct platform_device *pdev,
 
 	fbdev->dev = &pdev->dev;
 	fbdev->panel = panel;
+	fbdev->dssdev = &omapdss_device;
 	platform_set_drvdata(pdev, fbdev);
 
 	mutex_init(&fbdev->rqueue_mutex);
@@ -1814,8 +1828,16 @@ cleanup:
 
 static int omapfb_probe(struct platform_device *pdev)
 {
+	int r;
+
 	BUG_ON(fbdev_pdev != NULL);
 
+	r = platform_device_register(&omapdss_device);
+	if (r) {
+		dev_err(&pdev->dev, "can't register omapdss device\n");
+		return r;
+	}
+
 	/* Delay actual initialization until the LCD is registered */
 	fbdev_pdev = pdev;
 	if (fbdev_panel != NULL)
@@ -1843,6 +1865,9 @@ static int omapfb_remove(struct platform_device *pdev)
 	fbdev->state = OMAPFB_DISABLED;
 	omapfb_free_resources(fbdev, saved_state);
 
+	platform_device_unregister(&omapdss_device);
+	fbdev->dssdev = NULL;
+
 	return 0;
 }
 
diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c
index fed7b1bda19c..1162603c72e5 100644
--- a/drivers/video/omap/rfbi.c
+++ b/drivers/video/omap/rfbi.c
@@ -83,13 +83,13 @@ static inline u32 rfbi_read_reg(int idx)
 
 static int rfbi_get_clocks(void)
 {
-	rfbi.dss_ick = clk_get(rfbi.fbdev->dev, "ick");
+	rfbi.dss_ick = clk_get(&dispc.fbdev->dssdev->dev, "ick");
 	if (IS_ERR(rfbi.dss_ick)) {
 		dev_err(rfbi.fbdev->dev, "can't get ick\n");
 		return PTR_ERR(rfbi.dss_ick);
 	}
 
-	rfbi.dss1_fck = clk_get(rfbi.fbdev->dev, "dss1_fck");
+	rfbi.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "dss1_fck");
 	if (IS_ERR(rfbi.dss1_fck)) {
 		dev_err(rfbi.fbdev->dev, "can't get dss1_fck\n");
 		clk_put(rfbi.dss_ick);
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
index 71d8dec30635..c63ce767b277 100644
--- a/drivers/video/omap2/dss/Kconfig
+++ b/drivers/video/omap2/dss/Kconfig
@@ -25,6 +25,13 @@ config OMAP2_DSS_DEBUG_SUPPORT
 	  This enables debug messages. You need to enable printing
 	  with 'debug' module parameter.
 
+config OMAP2_DSS_COLLECT_IRQ_STATS
+	bool "Collect DSS IRQ statistics"
+	depends on OMAP2_DSS_DEBUG_SUPPORT
+	default n
+	help
+	  Collect DSS IRQ statistics, printable via debugfs
+
 config OMAP2_DSS_RFBI
 	bool "RFBI support"
         default n
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index 29497a0c9a91..82918eec6d2e 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -124,6 +124,7 @@ static void restore_all_ctx(void)
 	dss_clk_disable_all_no_ctx();
 }
 
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
 /* CLOCKS */
 static void core_dump_clocks(struct seq_file *s)
 {
@@ -149,6 +150,7 @@ static void core_dump_clocks(struct seq_file *s)
 				clocks[i]->usecount);
 	}
 }
+#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */
 
 static int dss_get_clock(struct clk **clock, const char *clk_name)
 {
@@ -395,6 +397,14 @@ static int dss_initialize_debugfs(void)
 	debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
 			&dss_debug_dump_clocks, &dss_debug_fops);
 
+	debugfs_create_file("dispc_irq", S_IRUGO, dss_debugfs_dir,
+			&dispc_dump_irqs, &dss_debug_fops);
+
+#ifdef CONFIG_OMAP2_DSS_DSI
+	debugfs_create_file("dsi_irq", S_IRUGO, dss_debugfs_dir,
+			&dsi_dump_irqs, &dss_debug_fops);
+#endif
+
 	debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir,
 			&dss_dump_regs, &dss_debug_fops);
 	debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir,
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 6dabf4b2f005..de8bfbac9e26 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -148,6 +148,12 @@ static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES,
 	DISPC_VID_ATTRIBUTES(0),
 	DISPC_VID_ATTRIBUTES(1) };
 
+struct dispc_irq_stats {
+	unsigned long last_reset;
+	unsigned irq_count;
+	unsigned irqs[32];
+};
+
 static struct {
 	void __iomem    *base;
 
@@ -160,6 +166,11 @@ static struct {
 	struct work_struct error_work;
 
 	u32		ctx[DISPC_SZ_REGS / sizeof(u32)];
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+	spinlock_t irq_stats_lock;
+	struct dispc_irq_stats irq_stats;
+#endif
 } dispc;
 
 static void _omap_dispc_set_irqs(void);
@@ -1443,7 +1454,10 @@ static unsigned long calc_fclk_five_taps(u16 width, u16 height,
 		do_div(tmp, 2 * out_height * ppl);
 		fclk = tmp;
 
-		if (height > 2 * out_height && ppl != out_width) {
+		if (height > 2 * out_height) {
+			if (ppl == out_width)
+				return 0;
+
 			tmp = pclk * (height - 2 * out_height) * out_width;
 			do_div(tmp, 2 * out_height * (ppl - out_width));
 			fclk = max(fclk, (u32) tmp);
@@ -1623,7 +1637,7 @@ static int _dispc_setup_plane(enum omap_plane plane,
 		DSSDBG("required fclk rate = %lu Hz\n", fclk);
 		DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate());
 
-		if (fclk > dispc_fclk_rate()) {
+		if (!fclk || fclk > dispc_fclk_rate()) {
 			DSSERR("failed to set up scaling, "
 					"required fclk rate = %lu Hz, "
 					"current fclk rate = %lu Hz\n",
@@ -2247,6 +2261,50 @@ void dispc_dump_clocks(struct seq_file *s)
 	enable_clocks(0);
 }
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+void dispc_dump_irqs(struct seq_file *s)
+{
+	unsigned long flags;
+	struct dispc_irq_stats stats;
+
+	spin_lock_irqsave(&dispc.irq_stats_lock, flags);
+
+	stats = dispc.irq_stats;
+	memset(&dispc.irq_stats, 0, sizeof(dispc.irq_stats));
+	dispc.irq_stats.last_reset = jiffies;
+
+	spin_unlock_irqrestore(&dispc.irq_stats_lock, flags);
+
+	seq_printf(s, "period %u ms\n",
+			jiffies_to_msecs(jiffies - stats.last_reset));
+
+	seq_printf(s, "irqs %d\n", stats.irq_count);
+#define PIS(x) \
+	seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
+
+	PIS(FRAMEDONE);
+	PIS(VSYNC);
+	PIS(EVSYNC_EVEN);
+	PIS(EVSYNC_ODD);
+	PIS(ACBIAS_COUNT_STAT);
+	PIS(PROG_LINE_NUM);
+	PIS(GFX_FIFO_UNDERFLOW);
+	PIS(GFX_END_WIN);
+	PIS(PAL_GAMMA_MASK);
+	PIS(OCP_ERR);
+	PIS(VID1_FIFO_UNDERFLOW);
+	PIS(VID1_END_WIN);
+	PIS(VID2_FIFO_UNDERFLOW);
+	PIS(VID2_END_WIN);
+	PIS(SYNC_LOST);
+	PIS(SYNC_LOST_DIGIT);
+	PIS(WAKEUP);
+#undef PIS
+}
+#else
+void dispc_dump_irqs(struct seq_file *s) { }
+#endif
+
 void dispc_dump_regs(struct seq_file *s)
 {
 #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r))
@@ -2665,6 +2723,13 @@ void dispc_irq_handler(void)
 
 	irqstatus = dispc_read_reg(DISPC_IRQSTATUS);
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+	spin_lock(&dispc.irq_stats_lock);
+	dispc.irq_stats.irq_count++;
+	dss_collect_irq_stats(irqstatus, dispc.irq_stats.irqs);
+	spin_unlock(&dispc.irq_stats_lock);
+#endif
+
 #ifdef DEBUG
 	if (dss_debug)
 		print_irq_status(irqstatus);
@@ -3012,6 +3077,11 @@ int dispc_init(void)
 
 	spin_lock_init(&dispc.irq_lock);
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+	spin_lock_init(&dispc.irq_stats_lock);
+	dispc.irq_stats.last_reset = jiffies;
+#endif
+
 	INIT_WORK(&dispc.error_work, dispc_error_worker);
 
 	dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS);
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 5936487b5def..6122178f5f85 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -204,6 +204,14 @@ struct dsi_update_region {
 	struct omap_dss_device *device;
 };
 
+struct dsi_irq_stats {
+	unsigned long last_reset;
+	unsigned irq_count;
+	unsigned dsi_irqs[32];
+	unsigned vc_irqs[4][32];
+	unsigned cio_irqs[32];
+};
+
 static struct
 {
 	void __iomem	*base;
@@ -258,6 +266,11 @@ static struct
 #endif
 	int debug_read;
 	int debug_write;
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+	spinlock_t irq_stats_lock;
+	struct dsi_irq_stats irq_stats;
+#endif
 } dsi;
 
 #ifdef DEBUG
@@ -528,6 +541,12 @@ void dsi_irq_handler(void)
 
 	irqstatus = dsi_read_reg(DSI_IRQSTATUS);
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+	spin_lock(&dsi.irq_stats_lock);
+	dsi.irq_stats.irq_count++;
+	dss_collect_irq_stats(irqstatus, dsi.irq_stats.dsi_irqs);
+#endif
+
 	if (irqstatus & DSI_IRQ_ERROR_MASK) {
 		DSSERR("DSI error, irqstatus %x\n", irqstatus);
 		print_irq_status(irqstatus);
@@ -549,6 +568,10 @@ void dsi_irq_handler(void)
 
 		vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i));
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+		dss_collect_irq_stats(vcstatus, dsi.irq_stats.vc_irqs[i]);
+#endif
+
 		if (vcstatus & DSI_VC_IRQ_BTA)
 			complete(&dsi.bta_completion);
 
@@ -568,6 +591,10 @@ void dsi_irq_handler(void)
 	if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
 		ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+		dss_collect_irq_stats(ciostatus, dsi.irq_stats.cio_irqs);
+#endif
+
 		dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
 		/* flush posted write */
 		dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
@@ -579,6 +606,10 @@ void dsi_irq_handler(void)
 	dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
 	/* flush posted write */
 	dsi_read_reg(DSI_IRQSTATUS);
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+	spin_unlock(&dsi.irq_stats_lock);
+#endif
 }
 
 
@@ -797,12 +828,12 @@ static int dsi_pll_power(enum dsi_pll_power_state state)
 
 	/* PLL_PWR_STATUS */
 	while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) {
-		udelay(1);
-		if (t++ > 1000) {
+		if (++t > 1000) {
 			DSSERR("Failed to set DSI PLL power mode to %d\n",
 					state);
 			return -ENODEV;
 		}
+		udelay(1);
 	}
 
 	return 0;
@@ -1226,6 +1257,95 @@ void dsi_dump_clocks(struct seq_file *s)
 	enable_clocks(0);
 }
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+void dsi_dump_irqs(struct seq_file *s)
+{
+	unsigned long flags;
+	struct dsi_irq_stats stats;
+
+	spin_lock_irqsave(&dsi.irq_stats_lock, flags);
+
+	stats = dsi.irq_stats;
+	memset(&dsi.irq_stats, 0, sizeof(dsi.irq_stats));
+	dsi.irq_stats.last_reset = jiffies;
+
+	spin_unlock_irqrestore(&dsi.irq_stats_lock, flags);
+
+	seq_printf(s, "period %u ms\n",
+			jiffies_to_msecs(jiffies - stats.last_reset));
+
+	seq_printf(s, "irqs %d\n", stats.irq_count);
+#define PIS(x) \
+	seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]);
+
+	seq_printf(s, "-- DSI interrupts --\n");
+	PIS(VC0);
+	PIS(VC1);
+	PIS(VC2);
+	PIS(VC3);
+	PIS(WAKEUP);
+	PIS(RESYNC);
+	PIS(PLL_LOCK);
+	PIS(PLL_UNLOCK);
+	PIS(PLL_RECALL);
+	PIS(COMPLEXIO_ERR);
+	PIS(HS_TX_TIMEOUT);
+	PIS(LP_RX_TIMEOUT);
+	PIS(TE_TRIGGER);
+	PIS(ACK_TRIGGER);
+	PIS(SYNC_LOST);
+	PIS(LDO_POWER_GOOD);
+	PIS(TA_TIMEOUT);
+#undef PIS
+
+#define PIS(x) \
+	seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
+			stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
+			stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
+			stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
+			stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
+
+	seq_printf(s, "-- VC interrupts --\n");
+	PIS(CS);
+	PIS(ECC_CORR);
+	PIS(PACKET_SENT);
+	PIS(FIFO_TX_OVF);
+	PIS(FIFO_RX_OVF);
+	PIS(BTA);
+	PIS(ECC_NO_CORR);
+	PIS(FIFO_TX_UDF);
+	PIS(PP_BUSY_CHANGE);
+#undef PIS
+
+#define PIS(x) \
+	seq_printf(s, "%-20s %10d\n", #x, \
+			stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
+
+	seq_printf(s, "-- CIO interrupts --\n");
+	PIS(ERRSYNCESC1);
+	PIS(ERRSYNCESC2);
+	PIS(ERRSYNCESC3);
+	PIS(ERRESC1);
+	PIS(ERRESC2);
+	PIS(ERRESC3);
+	PIS(ERRCONTROL1);
+	PIS(ERRCONTROL2);
+	PIS(ERRCONTROL3);
+	PIS(STATEULPS1);
+	PIS(STATEULPS2);
+	PIS(STATEULPS3);
+	PIS(ERRCONTENTIONLP0_1);
+	PIS(ERRCONTENTIONLP1_1);
+	PIS(ERRCONTENTIONLP0_2);
+	PIS(ERRCONTENTIONLP1_2);
+	PIS(ERRCONTENTIONLP0_3);
+	PIS(ERRCONTENTIONLP1_3);
+	PIS(ULPSACTIVENOT_ALL0);
+	PIS(ULPSACTIVENOT_ALL1);
+#undef PIS
+}
+#endif
+
 void dsi_dump_regs(struct seq_file *s)
 {
 #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r))
@@ -1321,12 +1441,12 @@ static int dsi_complexio_power(enum dsi_complexio_power_state state)
 
 	/* PWR_STATUS */
 	while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) {
-		udelay(1);
-		if (t++ > 1000) {
+		if (++t > 1000) {
 			DSSERR("failed to set complexio power state to "
 					"%d\n", state);
 			return -ENODEV;
 		}
+		udelay(1);
 	}
 
 	return 0;
@@ -1526,10 +1646,10 @@ static void dsi_complexio_uninit(void)
 
 static int _dsi_wait_reset(void)
 {
-	int i = 0;
+	int t = 0;
 
 	while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) {
-		if (i++ > 5) {
+		if (++t > 5) {
 			DSSERR("soft reset failed\n");
 			return -ENODEV;
 		}
@@ -1999,7 +2119,7 @@ static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc)
 		return -EINVAL;
 	}
 
-	data_id = data_type | channel << 6;
+	data_id = data_type | dsi.vc[channel].dest_per << 6;
 
 	r = (data_id << 0) | (data << 8) | (ecc << 24);
 
@@ -2011,7 +2131,7 @@ static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc)
 int dsi_vc_send_null(int channel)
 {
 	u8 nullpkg[] = {0, 0, 0, 0};
-	return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0);
+	return dsi_vc_send_long(channel, DSI_DT_NULL_PACKET, nullpkg, 4, 0);
 }
 EXPORT_SYMBOL(dsi_vc_send_null);
 
@@ -2058,7 +2178,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
 	int r;
 
 	if (dsi.debug_read)
-		DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %u)\n", channel, dcs_cmd);
+		DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %x)\n", channel, dcs_cmd);
 
 	r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0);
 	if (r)
@@ -2586,7 +2706,6 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
 		/* using fifo not empty */
 		/* TX_FIFO_NOT_EMPTY */
 		while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) {
-			udelay(1);
 			fifo_stalls++;
 			if (fifo_stalls > 0xfffff) {
 				DSSERR("fifo stalls overflow, pixels left %d\n",
@@ -2594,6 +2713,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
 				dsi_if_enable(0);
 				return -EIO;
 			}
+			udelay(1);
 		}
 #elif 1
 		/* using fifo emptiness */
@@ -2812,11 +2932,15 @@ static int dsi_set_update_mode(struct omap_dss_device *dssdev,
 
 static int dsi_set_te(struct omap_dss_device *dssdev, bool enable)
 {
-	int r;
-	r = dssdev->driver->enable_te(dssdev, enable);
-	/* XXX for some reason, DSI TE breaks if we don't wait here.
-	 * Panel bug? Needs more studying */
-	msleep(100);
+	int r = 0;
+
+	if (dssdev->driver->enable_te) {
+		r = dssdev->driver->enable_te(dssdev, enable);
+		/* XXX for some reason, DSI TE breaks if we don't wait here.
+		 * Panel bug? Needs more studying */
+		msleep(100);
+	}
+
 	return r;
 }
 
@@ -3637,6 +3761,11 @@ int dsi_init(struct platform_device *pdev)
 	spin_lock_init(&dsi.errors_lock);
 	dsi.errors = 0;
 
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+	spin_lock_init(&dsi.irq_stats_lock);
+	dsi.irq_stats.last_reset = jiffies;
+#endif
+
 	init_completion(&dsi.bta_completion);
 	init_completion(&dsi.update_completion);
 
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index 9b05ee65a15d..0a26b7d84d41 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -467,14 +467,14 @@ static irqreturn_t dss_irq_handler_omap3(int irq, void *arg)
 
 static int _omap_dss_wait_reset(void)
 {
-	unsigned timeout = 1000;
+	int t = 0;
 
 	while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
-		udelay(1);
-		if (!--timeout) {
+		if (++t > 1000) {
 			DSSERR("soft reset failed\n");
 			return -ENODEV;
 		}
+		udelay(1);
 	}
 
 	return 0;
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 8da5ac42151b..2bcb1245d6c2 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -240,6 +240,7 @@ int dsi_init(struct platform_device *pdev);
 void dsi_exit(void);
 
 void dsi_dump_clocks(struct seq_file *s);
+void dsi_dump_irqs(struct seq_file *s);
 void dsi_dump_regs(struct seq_file *s);
 
 void dsi_save_context(void);
@@ -268,6 +269,7 @@ int dpi_init_display(struct omap_dss_device *dssdev);
 int dispc_init(void);
 void dispc_exit(void);
 void dispc_dump_clocks(struct seq_file *s);
+void dispc_dump_irqs(struct seq_file *s);
 void dispc_dump_regs(struct seq_file *s);
 void dispc_irq_handler(void);
 void dispc_fake_vsync_irq(void);
@@ -367,4 +369,16 @@ void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t);
 unsigned long rfbi_get_max_tx_rate(void);
 int rfbi_init_display(struct omap_dss_device *display);
 
+
+#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
+static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr)
+{
+	int b;
+	for (b = 0; b < 32; ++b) {
+		if (irqstatus & (1 << b))
+			irq_arr[b]++;
+	}
+}
+#endif
+
 #endif
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index d0b3006ad8a5..b936495c065d 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -120,7 +120,7 @@ static struct {
 
 	struct omap_dss_device *dssdev[2];
 
-	struct kfifo      *cmd_fifo;
+	struct kfifo      cmd_fifo;
 	spinlock_t        cmd_lock;
 	struct completion cmd_done;
 	atomic_t          cmd_fifo_full;
@@ -1011,20 +1011,20 @@ static void process_cmd_fifo(void)
 		return;
 
 	while (true) {
-		spin_lock_irqsave(rfbi.cmd_fifo->lock, flags);
+		spin_lock_irqsave(&rfbi.cmd_lock, flags);
 
-		len = __kfifo_get(rfbi.cmd_fifo, (unsigned char *)&p,
+		len = kfifo_out(&rfbi.cmd_fifo, (unsigned char *)&p,
 				  sizeof(struct update_param));
 		if (len == 0) {
 			DSSDBG("nothing more in fifo\n");
 			atomic_set(&rfbi.cmd_pending, 0);
-			spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+			spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
 			break;
 		}
 
 		/* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/
 
-		spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+		spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
 
 		BUG_ON(len != sizeof(struct update_param));
 		BUG_ON(p.rfbi_module > 1);
@@ -1052,25 +1052,25 @@ static void rfbi_push_cmd(struct update_param *p)
 		unsigned long flags;
 		int available;
 
-		spin_lock_irqsave(rfbi.cmd_fifo->lock, flags);
+		spin_lock_irqsave(&rfbi.cmd_lock, flags);
 		available = RFBI_CMD_FIFO_LEN_BYTES -
-			__kfifo_len(rfbi.cmd_fifo);
+			kfifo_len(&rfbi.cmd_fifo);
 
 /*		DSSDBG("%d bytes left in fifo\n", available); */
 		if (available < sizeof(struct update_param)) {
 			DSSDBG("Going to wait because FIFO FULL..\n");
-			spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+			spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
 			atomic_inc(&rfbi.cmd_fifo_full);
 			wait_for_completion(&rfbi.cmd_done);
 			/*DSSDBG("Woke up because fifo not full anymore\n");*/
 			continue;
 		}
 
-		ret = __kfifo_put(rfbi.cmd_fifo, (unsigned char *)p,
+		ret = kfifo_in(&rfbi.cmd_fifo, (unsigned char *)p,
 				  sizeof(struct update_param));
 /*		DSSDBG("pushed %d bytes\n", ret);*/
 
-		spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+		spin_unlock_irqrestore(&rfbi.cmd_lock, flags);
 
 		BUG_ON(ret != sizeof(struct update_param));
 
@@ -1155,12 +1155,12 @@ int rfbi_init(void)
 {
 	u32 rev;
 	u32 l;
+	int r;
 
 	spin_lock_init(&rfbi.cmd_lock);
-	rfbi.cmd_fifo = kfifo_alloc(RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL,
-				    &rfbi.cmd_lock);
-	if (IS_ERR(rfbi.cmd_fifo))
-		return -ENOMEM;
+	r = kfifo_alloc(&rfbi.cmd_fifo, RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL);
+	if (r)
+		return r;
 
 	init_completion(&rfbi.cmd_done);
 	atomic_set(&rfbi.cmd_fifo_full, 0);
@@ -1196,7 +1196,7 @@ void rfbi_exit(void)
 {
 	DSSDBG("rfbi_exit\n");
 
-	kfifo_free(rfbi.cmd_fifo);
+	kfifo_free(&rfbi.cmd_fifo);
 
 	iounmap(rfbi.base);
 }
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index ef299839858a..d17caef6915a 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -1311,6 +1311,7 @@ static void omapfb_free_fbmem(struct fb_info *fbi)
 		if (rg->vrfb.vaddr[0]) {
 			iounmap(rg->vrfb.vaddr[0]);
 			omap_vrfb_release_ctx(&rg->vrfb);
+			rg->vrfb.vaddr[0] = NULL;
 		}
 	}
 
@@ -2114,6 +2115,11 @@ static int omapfb_probe(struct platform_device *pdev)
 	dssdev = NULL;
 	for_each_dss_dev(dssdev) {
 		omap_dss_get_device(dssdev);
+		if (!dssdev->driver) {
+			dev_err(&pdev->dev, "no driver for display\n");
+			r = -EINVAL;
+			goto cleanup;
+		}
 		fbdev->displays[fbdev->num_displays++] = dssdev;
 	}