From 863d980d4392041b36edd53b248ae7709609325d Mon Sep 17 00:00:00 2001
From: Andy Green <andy@openmoko.com>
Date: Fri, 25 Jul 2008 23:06:04 +0100
Subject: [PATCH] fix-glamofb-cmd-mode-locking.patch

Glamo "cmd mode" is modal, but nothing took care about locking.
Also cmd mode was entered recursively in rotate_lcd().

Signed-off-by: Andy Green <andy@openmoko.com>
---
 drivers/mfd/glamo/glamo-core.c |    2 +-
 drivers/mfd/glamo/glamo-fb.c   |  106 +++++++++++++++++++++++++--------------
 include/linux/glamofb.h        |    2 +-
 3 files changed, 70 insertions(+), 40 deletions(-)

diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c
index accd933..19ca363 100644
--- a/drivers/mfd/glamo/glamo-core.c
+++ b/drivers/mfd/glamo/glamo-core.c
@@ -588,7 +588,7 @@ int glamo_engine_reclock(struct glamo_core *glamo,
 	if (val) {
 		val--;
 		reg_set_bit_mask(glamo, reg, mask, val);
-		msleep(5); /* wait some time to stabilize */
+		mdelay(5); /* wait some time to stabilize */
 
 		return 0;
 	} else {
diff --git a/drivers/mfd/glamo/glamo-fb.c b/drivers/mfd/glamo/glamo-fb.c
index 87c7420..8633e44 100644
--- a/drivers/mfd/glamo/glamo-fb.c
+++ b/drivers/mfd/glamo/glamo-fb.c
@@ -36,6 +36,7 @@
 #include <linux/wait.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/spinlock.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -50,7 +51,7 @@
 #include "glamo-regs.h"
 #include "glamo-core.h"
 
-#ifdef DEBUG
+#ifndef DEBUG
 #define GLAMO_LOG(...)
 #else
 #define GLAMO_LOG(...) \
@@ -72,6 +73,7 @@ struct glamofb_handle {
 	struct glamofb_platform_data *mach_info;
 	char __iomem *cursor_addr;
 	u_int32_t pseudo_pal[16];
+	spinlock_t lock_cmd;
 };
 
 /* 'sibling' spi device for lcm init */
@@ -237,6 +239,7 @@ static void rotate_lcd(struct glamofb_handle *glamo,
                        __u32 rotation)
 {
 	int glamo_rot;
+	unsigned long flags;
 
 	switch (rotation) {
 		case FB_ROTATE_UR:
@@ -255,7 +258,15 @@ static void rotate_lcd(struct glamofb_handle *glamo,
 			glamo_rot = GLAMO_LCD_ROT_MODE_0;
 			break;
 	}
-	glamofb_cmd_mode(glamo, 1);
+
+	/*
+ 	 * ha ha we are only called when we are in cmd mode already
+	 * printk(KERN_ERR"rotate_lcd spin_lock_irqsave\n");
+	 * spin_lock_irqsave(&glamo->lock_cmd, flags);
+	 *
+	 * if (glamofb_cmd_mode(glamo, 1))
+	 *		goto out_unlock;
+	 */
 	reg_set_bit_mask(glamo,
 			 GLAMO_REG_LCD_WIDTH,
 			 GLAMO_LCD_ROT_MODE_MASK,
@@ -265,17 +276,19 @@ static void rotate_lcd(struct glamofb_handle *glamo,
 			 GLAMO_LCD_MODE1_ROTATE_EN,
 			 (glamo_rot != GLAMO_LCD_ROT_MODE_0)?
 				 GLAMO_LCD_MODE1_ROTATE_EN : 0);
-	glamofb_cmd_mode(glamo, 0);
+/*	glamofb_cmd_mode(glamo, 0);
+
+out_unlock:
+	printk(KERN_ERR"rotate_lcd spin_unlock_irqrestore\n");
+	spin_unlock_irqrestore(&glamo->lock_cmd, flags);
+*/
 }
 
 static enum orientation get_orientation(struct fb_var_screeninfo *var)
 {
-	GLAMO_LOG("mark\n")
-	if (var->xres <= var->yres) {
-		GLAMO_LOG("portrait\n")
+	if (var->xres <= var->yres)
 		return ORIENTATION_PORTRAIT;
-	}
-	GLAMO_LOG("landscape\n")
+
 	return ORIENTATION_LANDSCAPE;
 }
 
@@ -299,12 +312,18 @@ static void glamofb_update_lcd_controller(struct glamofb_handle *glamo,
 					  struct fb_var_screeninfo *var)
 {
 	int sync, bp, disp, fp, total, xres, yres, pitch, orientation_changing;
+	unsigned long flags;
 
-	GLAMO_LOG("enter: glamo:%#x, var:%#x\n", (unsigned)glamo, (unsigned)var);
+/*	GLAMO_LOG("enter: glamo:%#x, var:%#x\n", (unsigned)glamo, (unsigned)var);
+*/
 	if (!glamo || !var)
 		return;
 
-	glamofb_cmd_mode(glamo, 1);
+	printk(KERN_ERR"glamofb_update_lcd_controller spin_lock_irqsave\n");
+	spin_lock_irqsave(&glamo->lock_cmd, flags);
+
+	if (glamofb_cmd_mode(glamo, 1))
+		goto out_unlock;
 
 	if (var->pixclock)
 		glamo_engine_reclock(glamo->mach_info->glamo,
@@ -313,14 +332,14 @@ static void glamofb_update_lcd_controller(struct glamofb_handle *glamo,
 
 	xres = var->xres;
 	yres = var->yres;
-	GLAMO_LOG("xres:%d, yres:%d, rotate:%d\n", xres, yres, var->rotate);
-
+/*	GLAMO_LOG("xres:%d, yres:%d, rotate:%d\n", xres, yres, var->rotate);
+*/
 	/*
 	 * figure out if orientation is going to change
 	 */
 	orientation_changing = will_orientation_change(var);
-	GLAMO_LOG("orientation_changing:%d\n", orientation_changing);
-
+/*	GLAMO_LOG("orientation_changing:%d\n", orientation_changing);
+*/
         /*
          * adjust the pitch according to new orientation to come
          */
@@ -329,8 +348,8 @@ static void glamofb_update_lcd_controller(struct glamofb_handle *glamo,
         } else {
 		pitch = var->xres * var->bits_per_pixel / 8;
         }
-	GLAMO_LOG("pitch:%d\n", pitch);
-
+/*	GLAMO_LOG("pitch:%d\n", pitch);
+*/
 	/*
 	 * set the awaiten LCD geometry
 	 */
@@ -347,7 +366,7 @@ static void glamofb_update_lcd_controller(struct glamofb_handle *glamo,
 			 GLAMO_LCD_PITCH_MASK,
 			 pitch);
 
-	GLAMO_LOG("mark:\n");
+/*	GLAMO_LOG("mark:\n");*/
 	/*
 	 * honour the rotation request
 	 */
@@ -365,7 +384,8 @@ static void glamofb_update_lcd_controller(struct glamofb_handle *glamo,
 		var->yres_virtual = var->yres = yres;
 	}
 
-	GLAMO_LOG("reported res:(%d,%d)\n", var->xres, var->yres);
+/*	GLAMO_LOG("reported res:(%d,%d)\n", var->xres, var->yres);
+*/
 	/*
 	 * update scannout timings
 	 */
@@ -386,8 +406,8 @@ static void glamofb_update_lcd_controller(struct glamofb_handle *glamo,
 	reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_DISP_END,
 			 GLAMO_LCD_HV_RETR_DISP_END_MASK, fp);
 
-	GLAMO_LOG("mark:\n");
-
+/*	GLAMO_LOG("mark:\n");
+*/
 	sync = 0;
 	bp = sync + var->vsync_len;
 	disp = bp + var->upper_margin;
@@ -405,10 +425,13 @@ static void glamofb_update_lcd_controller(struct glamofb_handle *glamo,
 	reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_DISP_END,
 			 GLAMO_LCD_HV_RETR_DISP_END_MASK, fp);
 
-	GLAMO_LOG("mark:\n");
+/*	GLAMO_LOG("mark:\n"); */
 	glamofb_cmd_mode(glamo, 0);
 
-	GLAMO_LOG("leave:\n");
+/*	GLAMO_LOG("leave:\n"); */
+out_unlock:
+	printk(KERN_ERR"glamofb_update_lcd_controller spin_unlock_irqrestore\n");
+	spin_unlock_irqrestore(&glamo->lock_cmd, flags);
 }
 
 static int glamofb_set_par(struct fb_info *info)
@@ -552,23 +575,25 @@ static inline int glamofb_cmdq_empty(struct glamofb_handle *gfb)
 	return reg_read(gfb, GLAMO_REG_LCD_STATUS1) & (1 << 15);
 }
 
-void glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
+/* call holding gfb->lock_cmd  when locking, until you unlock */
+
+int glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
 {
-	int timeout = 20000;
+	int timeout = 200000;
 
-	dev_dbg(gfb->dev, "glamofb_cmd_mode(gfb=%p, on=%d)\n", gfb, on);
+/*	dev_dbg(gfb->dev, "glamofb_cmd_mode(gfb=%p, on=%d)\n", gfb, on); */
 	if (on) {
-		dev_dbg(gfb->dev, "%s: waiting for cmdq empty: ",
-			__FUNCTION__);
+/*		dev_dbg(gfb->dev, "%s: waiting for cmdq empty: ",
+			__FUNCTION__); */
 		while ((!glamofb_cmdq_empty(gfb)) && (timeout--))
 			yield();
 		if (timeout < 0) {
 			printk(KERN_ERR"*************"
 				       "glamofb cmd_queue never got empty"
 				       "*************\n");
-			return;
+			return -EIO;
 		}
-		dev_dbg(gfb->dev, "empty!\n");
+/*		dev_dbg(gfb->dev, "empty!\n"); */
 
 		/* display the entire frame then switch to command */
 		reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
@@ -576,8 +601,8 @@ void glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
 			  GLAMO_LCD_CMD_DATA_FIRE_VSYNC);
 
 		/* wait until LCD is idle */
-		dev_dbg(gfb->dev, "waiting for LCD idle: ");
-		timeout = 2000;
+/*		dev_dbg(gfb->dev, "waiting for LCD idle: "); */
+		timeout = 200000;
 		while ((!reg_read(gfb, GLAMO_REG_LCD_STATUS2) & (1 << 12)) &&
 		      (timeout--))
 			yield();
@@ -585,11 +610,11 @@ void glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
 			printk(KERN_ERR"*************"
 				       "glamofb lcd never idle"
 				       "*************\n");
-			return;
+			return -EIO;
 		}
-		dev_dbg(gfb->dev, "idle!\n");
+/*		dev_dbg(gfb->dev, "idle!\n"); */
 
-		msleep(90);
+		mdelay(100);
 	} else {
 		/* RGB interface needs vsync/hsync */
 		if (reg_read(gfb, GLAMO_REG_LCD_MODE3) & GLAMO_LCD_MODE3_RGB)
@@ -601,15 +626,17 @@ void glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
 			  GLAMO_LCD_CMD_TYPE_DISP |
 			  GLAMO_LCD_CMD_DATA_DISP_FIRE);
 	}
+
+	return 0;
 }
 EXPORT_SYMBOL_GPL(glamofb_cmd_mode);
 
 int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val)
 {
-	int timeout = 20000;
+	int timeout = 200000;
 
-	dev_dbg(gfb->dev, "%s: waiting for cmdq empty\n",
-		__FUNCTION__);
+/*	dev_dbg(gfb->dev, "%s: waiting for cmdq empty\n",
+		__FUNCTION__); */
 	while ((!glamofb_cmdq_empty(gfb)) && (timeout--))
 		yield();
 	if (timeout < 0) {
@@ -618,7 +645,7 @@ int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val)
 				"*************\n");
 		return 1;
 	}
-	dev_dbg(gfb->dev, "idle, writing 0x%04x\n", val);
+/*	dev_dbg(gfb->dev, "idle, writing 0x%04x\n", val); */
 
 	reg_write(gfb, GLAMO_REG_LCD_COMMAND1, val);
 
@@ -758,6 +785,9 @@ static int __init glamofb_probe(struct platform_device *pdev)
 
 	glamo_engine_enable(mach_info->glamo, GLAMO_ENGINE_LCD);
 	glamo_engine_reset(mach_info->glamo, GLAMO_ENGINE_LCD);
+
+	printk(KERN_ERR"spin_lock_init\n");
+	spin_lock_init(&glamofb->lock_cmd);
 	glamofb_init_regs(glamofb);
 
 	rc = register_framebuffer(fbinfo);
diff --git a/include/linux/glamofb.h b/include/linux/glamofb.h
index 75eefef..51bf593 100644
--- a/include/linux/glamofb.h
+++ b/include/linux/glamofb.h
@@ -33,7 +33,7 @@ struct glamofb_platform_data {
 	int		(*glamo_irq_is_wired)(void);
 };
 
-void glamofb_cmd_mode(struct glamofb_handle *gfb, int on);
+int glamofb_cmd_mode(struct glamofb_handle *gfb, int on);
 int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val);
 void glamo_lcm_reset(int level);
 
-- 
1.5.6.3