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;
