Michael,

I've implemented the changes describe in the specification part provided
by Joerg. I tested this new version and I observe no regression. It
would be nice if you could test it on your device.

Nicolas
commit 65f5e3d476073f52c4a63099a12ab01281dbd126
Author: Nicolas Dufresne <[email protected]>
Date:   Sat Mar 21 13:13:05 2009 -0400

    jbt6k74: Safer delays
    
    - Removed unused variable have_resumed
    - Added a timespec to keep track of the last sleep
      command and space them according to the specification.
      Those commands must be spaced of at least 120 ms.
    - Delays have been augmented for safer operation.
    - Transition from sleep to normal and sleep to qvga normal
      as been grouped to reduce code duplication.
    - Added 6 ms after SLEEP_OUT and SLEEP_IN command.
      The specification says we must wait at least 5 ms.
    - Made reset_write lock safe and fixed recommended
      delays.
    
    Signed-off-by: Nicolas Dufresne <[email protected]>

diff --git a/.gitignore b/.gitignore
index 869e1a3..aa818b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,3 +64,4 @@ ncscope.*
 *.orig
 *~
 \#*#
+GTA02
diff --git a/drivers/video/display/jbt6k74.c b/drivers/video/display/jbt6k74.c
index 9add1c6..16add20 100644
--- a/drivers/video/display/jbt6k74.c
+++ b/drivers/video/display/jbt6k74.c
@@ -31,6 +31,7 @@
 #include <linux/delay.h>
 #include <linux/jbt6k74.h>
 #include <linux/fb.h>
+#include <linux/time.h>
 
 enum jbt_register {
 	JBT_REG_SLEEP_IN		= 0x10,
@@ -117,12 +118,18 @@ struct jbt_info {
 	struct notifier_block fb_notif;
 	u16 tx_buf[8];
 	u16 reg_cache[0xEE];
-	int have_resumed;
+	struct timespec last_sleep;
 };
 
 #define JBT_COMMAND	0x000
 #define JBT_DATA	0x100
 
+static inline unsigned int timespec_sub_ms(struct timespec lhs,
+					struct timespec rhs)
+{
+	struct timespec ts = timespec_sub(lhs, rhs);
+	return (ts.tv_sec * MSEC_PER_SEC) + (ts.tv_nsec / NSEC_PER_MSEC);
+}
 
 static int jbt_reg_write_nodata(struct jbt_info *jbt, u8 reg)
 {
@@ -179,7 +186,7 @@ static int jbt_init_regs(struct jbt_info *jbt)
 {
 	int rc;
 
-	dev_dbg(&jbt->spi_dev->dev, "entering %cVGA mode\n", 
+	dev_dbg(&jbt->spi_dev->dev, "entering %cVGA mode\n",
 			jbt->normal_state == JBT_STATE_QVGA_NORMAL ? 'Q' : ' ');
 
 	rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE1, 0x01);
@@ -247,15 +254,15 @@ static int standby_to_sleep(struct jbt_info *jbt)
 
 	/* three times command zero */
 	rc = jbt_reg_write_nodata(jbt, 0x00);
-	mdelay(1);
+	mdelay(2);
 	rc |= jbt_reg_write_nodata(jbt, 0x00);
-	mdelay(1);
+	mdelay(2);
 	rc |= jbt_reg_write_nodata(jbt, 0x00);
-	mdelay(1);
+	mdelay(2);
 
 	/* deep standby out */
 	rc |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x11);
-	mdelay(1);
+	mdelay(2);
 	rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x28);
 
 	/* (re)initialize register set */
@@ -268,36 +275,27 @@ static int sleep_to_normal(struct jbt_info *jbt)
 {
 	int rc;
 
-	/* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */
-	rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x80);
-
-	/* Quad mode off */
-	rc |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x00);
-
-	/* AVDD on, XVDD on */
-	rc |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x16);
-
-	/* Output control */
-	rc |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0xfff9);
-
-	/* Turn on display */
-	rc |= jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_ON);
-
-	/* Sleep mode off */
-	rc |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT);
-
-	return rc ? -EIO : 0;
-}
+	/* Make sure we are 120 ms after SLEEP_OUT */
+	unsigned int sleep_time = timespec_sub_ms(current_kernel_time(),
+							jbt->last_sleep);
+	if (sleep_time < 130) {
+		mdelay(130 - sleep_time);
+	}
 
-static int sleep_to_qvga_normal(struct jbt_info *jbt)
-{
-	int rc;
+	if (jbt->normal_state == JBT_STATE_NORMAL) {
+		/* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */
+		rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x80);
 
-	/* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */
-	rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x81);
+		/* Quad mode off */
+		rc |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x00);
+	}
+	else {
+		/* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */
+		rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x81);
 
-	/* Quad mode on */
-	rc |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x22);
+		/* Quad mode on */
+		rc |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x22);
+	}
 
 	/* AVDD on, XVDD on */
 	rc |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x16);
@@ -310,6 +308,10 @@ static int sleep_to_qvga_normal(struct jbt_info *jbt)
 
 	/* Sleep mode off */
 	rc |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT);
+	jbt->last_sleep = current_kernel_time();
+
+	/* Allow the booster and display controller to restart stably */
+	mdelay(6);
 
 	return rc ? -EIO : 0;
 }
@@ -318,9 +320,20 @@ static int normal_to_sleep(struct jbt_info *jbt)
 {
 	int rc;
 
+	/* Make sure we are 120 ms after SLEEP_OUT */
+	unsigned int sleep_time = timespec_sub_ms(current_kernel_time(),
+							jbt->last_sleep);
+	if (sleep_time < 130) {
+		mdelay(130 - sleep_time);
+	}
+
 	rc = jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF);
 	rc |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0x8002);
 	rc |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_IN);
+	jbt->last_sleep = current_kernel_time();
+
+	/* Allow the internal circuits to stop automatically */
+	mdelay(6);
 
 	return rc ? -EIO : 0;
 }
@@ -363,7 +376,7 @@ int jbt6k74_enter_state(struct jbt_info *jbt, enum jbt_state new_state)
 			/* first transition into sleep */
 			rc = standby_to_sleep(jbt);
 			/* then transition into normal */
-			rc |= sleep_to_qvga_normal(jbt);
+			rc |= sleep_to_normal(jbt);
 			break;
 		}
 		break;
@@ -376,10 +389,8 @@ int jbt6k74_enter_state(struct jbt_info *jbt, enum jbt_state new_state)
 			rc = sleep_to_standby(jbt);
 			break;
 		case JBT_STATE_NORMAL:
-			rc = sleep_to_normal(jbt);
-			break;
 		case JBT_STATE_QVGA_NORMAL:
-			rc = sleep_to_qvga_normal(jbt);
+			rc = sleep_to_normal(jbt);
 			break;
 		}
 		break;
@@ -405,7 +416,7 @@ int jbt6k74_enter_state(struct jbt_info *jbt, enum jbt_state new_state)
 			/* third transition into sleep */
 			rc |= standby_to_sleep(jbt);
 			/* fourth transition into normal */
-			rc |= sleep_to_qvga_normal(jbt);
+			rc |= sleep_to_normal(jbt);
 			break;
 		}
 		break;
@@ -525,18 +536,27 @@ static ssize_t gamma_write(struct device *dev, struct device_attribute *attr,
 static ssize_t reset_write(struct device *dev, struct device_attribute *attr,
 			   const char *buf, size_t count)
 {
+	int rc;
 	struct jbt_info *jbt = dev_get_drvdata(dev);
 	struct jbt6k74_platform_data *jbt6k74_pdata = jbt->spi_dev->dev.platform_data;
 
 	dev_info(dev, "**** jbt6k74 reset\n");
 
-	jbt6k74_enter_state(jbt, JBT_STATE_DEEP_STANDBY);
+	mutex_lock(&jbt->lock);
+
+	jbt->state = JBT_STATE_DEEP_STANDBY;
 
 	/* hard reset the jbt6k74 */
-	mutex_lock(&jbt->lock);
 	(jbt6k74_pdata->reset)(0, 0);
-	mdelay(1);
+	mdelay(2);
 	(jbt6k74_pdata->reset)(0, 1);
+	mdelay(130);
+
+	rc = jbt_reg_write_nodata(jbt, 0x01);
+	if (rc < 0)
+		dev_err(&jbt->spi_dev->dev, "cannot soft reset\n");
+	mdelay(130);
+
 	mutex_unlock(&jbt->lock);
 
 	jbt6k74_enter_state(jbt, jbt->normal_state);
@@ -631,6 +651,7 @@ static int __devinit jbt_probe(struct spi_device *spi)
 	jbt->spi_dev = spi;
 	jbt->normal_state = JBT_STATE_NORMAL;
 	jbt->state = JBT_STATE_DEEP_STANDBY;
+	jbt->last_sleep = current_kernel_time();
 	mutex_init(&jbt->lock);
 
 	dev_set_drvdata(&spi->dev, jbt);
@@ -693,8 +714,6 @@ static int jbt_suspend(struct spi_device *spi, pm_message_t state)
 
 	jbt6k74_enter_state(jbt, JBT_STATE_DEEP_STANDBY);
 
-	jbt->have_resumed = 0;
-
 	dev_info(&spi->dev, "**** jbt6k74 suspend end\n");
 
 	return 0;

Reply via email to