From 2042106d96a13c2a15f1425fe9133257b1e0fbed Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sat, 21 Nov 2009 21:42:16 +0100 Subject: [PATCH 04/13] JBT6k74 work for KMS This simplifies the JBT6k74 driver, and adds hooks for the Glamo driver to cooperate more closely with it. Signed-off-by: Thomas White --- drivers/video/backlight/jbt6k74.c | 252 ++++++++++++++++++------------------ include/linux/jbt6k74.h | 17 +++- 2 files changed, 142 insertions(+), 127 deletions(-) diff --git a/drivers/video/backlight/jbt6k74.c b/drivers/video/backlight/jbt6k74.c index 8450904..91651e7 100644 --- a/drivers/video/backlight/jbt6k74.c +++ b/drivers/video/backlight/jbt6k74.c @@ -101,20 +101,10 @@ enum jbt_register { JBT_REG_HCLOCK_QVGA = 0xed, }; -enum jbt_resolution { - JBT_RESOLUTION_VGA, - JBT_RESOLUTION_QVGA, -}; - -enum jbt_power_mode { - JBT_POWER_MODE_DEEP_STANDBY, - JBT_POWER_MODE_SLEEP, - JBT_POWER_MODE_NORMAL, -}; static const char *jbt_power_mode_names[] = { - [JBT_POWER_MODE_DEEP_STANDBY] = "deep-standby", - [JBT_POWER_MODE_SLEEP] = "sleep", + [JBT_POWER_MODE_OFF] = "off", + [JBT_POWER_MODE_STANDBY] = "standby", [JBT_POWER_MODE_NORMAL] = "normal", }; @@ -123,6 +113,7 @@ static const char *jbt_resolution_names[] = { [JBT_RESOLUTION_QVGA] = "qvga", }; + struct jbt_info { struct mutex lock; /* protects this structure */ enum jbt_resolution resolution; @@ -141,6 +132,8 @@ struct jbt_info { uint16_t reg_cache[0xEE]; }; +struct jbt_info *jbt_global; + #define JBT_COMMAND 0x000 #define JBT_DATA 0x100 @@ -156,6 +149,8 @@ static int jbt_reg_write_nodata(struct jbt_info *jbt, uint8_t reg) else dev_err(&jbt->spi->dev, "Write failed: %d\n", ret); + mdelay(1); + return ret; } @@ -173,6 +168,8 @@ static int jbt_reg_write(struct jbt_info *jbt, uint8_t reg, uint8_t data) else dev_err(&jbt->spi->dev, "Write failed: %d\n", ret); + mdelay(1); + return ret; } @@ -191,6 +188,8 @@ static int jbt_reg_write16(struct jbt_info *jbt, uint8_t reg, uint16_t data) else dev_err(&jbt->spi->dev, "Write failed: %d\n", ret); + mdelay(1); + return ret; } @@ -218,7 +217,7 @@ static int jbt_init_regs(struct jbt_info *jbt) * to avoid red / blue flicker */ ret |= jbt_reg_write(jbt, JBT_REG_ASW_SLEW, 0x00 | (1 << 5)); - ret |= jbt_reg_write(jbt, JBT_REG_DUMMY_DISPLAY, 0x00); + ret |= jbt_reg_write(jbt, JBT_REG_DUMMY_DISPLAY, 0xff); ret |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_A, 0x11); ret |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_B, 0x11); @@ -260,14 +259,18 @@ static int jbt_init_regs(struct jbt_info *jbt) return ret ? -EIO : 0; } -static int jbt_standby_to_sleep(struct jbt_info *jbt) + +static int jbt_off_to_normal(struct jbt_info *jbt) { int ret; + struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data; gpio_set_value_cansleep(pdata->gpio_reset, 1); ret = regulator_bulk_enable(ARRAY_SIZE(jbt->supplies), jbt->supplies); + mdelay(120); + /* three times command zero */ ret |= jbt_reg_write_nodata(jbt, 0x00); mdelay(1); @@ -279,18 +282,11 @@ static int jbt_standby_to_sleep(struct jbt_info *jbt) /* deep standby out */ ret |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x11); mdelay(1); - ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x28); + ret |= jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x28); /* (re)initialize register set */ ret |= jbt_init_regs(jbt); - return ret ? -EIO : 0; -} - -static int jbt_sleep_to_normal(struct jbt_info *jbt) -{ - int ret; - /* Make sure we are 120 ms after SLEEP_OUT */ if (time_before(jiffies, jbt->next_sleep)) mdelay(jiffies_to_msecs(jbt->next_sleep - jiffies)); @@ -320,6 +316,7 @@ static int jbt_sleep_to_normal(struct jbt_info *jbt) /* Sleep mode off */ ret |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT); + jbt->next_sleep = jiffies + msecs_to_jiffies(120); /* Allow the booster and display controller to restart stably */ @@ -328,45 +325,68 @@ static int jbt_sleep_to_normal(struct jbt_info *jbt) return ret ? -EIO : 0; } -static int jbt_normal_to_sleep(struct jbt_info *jbt) +static int jbt_normal_to_off(struct jbt_info *jbt) { int ret; + struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data; - /* Make sure we are 120 ms after SLEEP_OUT */ - while (time_before(jiffies, jbt->next_sleep)) - cpu_relax(); + /* Pull the plug! */ + ret = regulator_bulk_disable(ARRAY_SIZE(jbt->supplies), + jbt->supplies); + if (!ret) + gpio_set_value_cansleep(pdata->gpio_reset, 0); + + return ret ? -EIO : 0; +} + + +static int jbt_normal_to_standby(struct jbt_info *jbt) +{ + int ret; + + if ( jbt->power_mode != JBT_POWER_MODE_NORMAL ) return 0; + + /* Make sure we are 120 ms after SLEEP_{IN,OUT} */ + while (time_before(jiffies, jbt->next_sleep)) cpu_relax(); + + /* Sleep mode on */ ret = jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF); ret |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0x8000 | 1 << 3); + ret |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_IN); - jbt->next_sleep = jiffies + msecs_to_jiffies(120); + jbt->next_sleep = jiffies + msecs_to_jiffies(150); - /* Allow the internal circuits to stop automatically */ - mdelay(5); + jbt->power_mode = JBT_POWER_MODE_STANDBY; return ret ? -EIO : 0; } -static int jbt_sleep_to_standby(struct jbt_info *jbt) + +static int jbt_standby_to_normal(struct jbt_info *jbt) { int ret; - struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data; - ret = jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x00); + if ( jbt->power_mode != JBT_POWER_MODE_STANDBY ) return 0; - if (!ret) - ret = regulator_bulk_disable(ARRAY_SIZE(jbt->supplies), jbt->supplies); + /* Make sure we are 120 ms after SLEEP_{IN,OUT} */ + while (time_before(jiffies, jbt->next_sleep)) cpu_relax(); - if (!ret) - gpio_set_value_cansleep(pdata->gpio_reset, 0); + ret = jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT); + jbt->next_sleep = jiffies + msecs_to_jiffies(150); - return ret; + ret |= jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_ON); + ret |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0xdff9); + + jbt->power_mode = JBT_POWER_MODE_NORMAL; + + return ret ? -EIO : 0; } + static int jbt6k74_enter_power_mode(struct jbt_info *jbt, enum jbt_power_mode new_mode) { - struct jbt6k74_platform_data *pdata = jbt->spi->dev.platform_data; int ret = -EINVAL; dev_dbg(&jbt->spi->dev, "entering (old_state=%s, new_state=%s)\n", @@ -375,49 +395,17 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt, mutex_lock(&jbt->lock); - if (jbt->suspended) { - switch (new_mode) { - case JBT_POWER_MODE_DEEP_STANDBY: - case JBT_POWER_MODE_SLEEP: - case JBT_POWER_MODE_NORMAL: - ret = 0; - jbt->suspend_mode = new_mode; - break; - default: - break; - } - } else if (new_mode == JBT_POWER_MODE_NORMAL && - pdata->enable_pixel_clock) { - pdata->enable_pixel_clock(&jbt->spi->dev, 1); - } - switch (jbt->power_mode) { - case JBT_POWER_MODE_DEEP_STANDBY: + case JBT_POWER_MODE_OFF: switch (new_mode) { - case JBT_POWER_MODE_DEEP_STANDBY: + case JBT_POWER_MODE_OFF: ret = 0; break; - case JBT_POWER_MODE_SLEEP: - ret = jbt_standby_to_sleep(jbt); - break; case JBT_POWER_MODE_NORMAL: - /* first transition into sleep */ - ret = jbt_standby_to_sleep(jbt); - /* then transition into normal */ - ret |= jbt_sleep_to_normal(jbt); + ret = jbt_off_to_normal(jbt); break; - } - break; - case JBT_POWER_MODE_SLEEP: - switch (new_mode) { - case JBT_POWER_MODE_SLEEP: - ret = 0; - break; - case JBT_POWER_MODE_DEEP_STANDBY: - ret = jbt_sleep_to_standby(jbt); - break; - case JBT_POWER_MODE_NORMAL: - ret = jbt_sleep_to_normal(jbt); + case JBT_POWER_MODE_STANDBY: + ret = -EINVAL; break; } break; @@ -426,25 +414,23 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt, case JBT_POWER_MODE_NORMAL: ret = 0; break; - case JBT_POWER_MODE_DEEP_STANDBY: - /* first transition into sleep */ - ret = jbt_normal_to_sleep(jbt); - /* then transition into deep standby */ - ret |= jbt_sleep_to_standby(jbt); + case JBT_POWER_MODE_OFF: + ret = jbt_normal_to_off(jbt); break; - case JBT_POWER_MODE_SLEEP: - ret = jbt_normal_to_sleep(jbt); + case JBT_POWER_MODE_STANDBY: + ret = -EINVAL; break; } + break; + case JBT_POWER_MODE_STANDBY: + ret = -EINVAL; + break; } if (ret == 0) { jbt->power_mode = new_mode; - if (new_mode != JBT_POWER_MODE_NORMAL && - pdata->enable_pixel_clock) - pdata->enable_pixel_clock(&jbt->spi->dev, 0); } else { - dev_err(&jbt->spi->dev, "Failed enter state '%s': %d\n", + dev_err(&jbt->spi->dev, "Failed to enter state '%s': %d\n", jbt_power_mode_names[new_mode], ret); } @@ -456,28 +442,46 @@ static int jbt6k74_enter_power_mode(struct jbt_info *jbt, static int jbt6k74_set_resolution(struct jbt_info *jbt, enum jbt_resolution new_resolution) { + int old_resolution; int ret = 0; - enum jbt_resolution old_resolution; + + if ( !jbt ) return -1; mutex_lock(&jbt->lock); - if (jbt->resolution == new_resolution) - goto out_unlock; + if ( jbt->resolution == new_resolution ) goto out_unlock; + if ( jbt->power_mode == JBT_POWER_MODE_OFF ) goto out_unlock; old_resolution = jbt->resolution; jbt->resolution = new_resolution; - if (jbt->power_mode == JBT_POWER_MODE_NORMAL) { + if ( jbt->power_mode == JBT_POWER_MODE_NORMAL ) { + + ret = jbt_normal_to_standby(jbt); + + mdelay(25); - /* first transition into sleep */ - ret = jbt_normal_to_sleep(jbt); - ret |= jbt_sleep_to_normal(jbt); + if (jbt->resolution == JBT_RESOLUTION_VGA) { + /* Quad mode off */ + ret |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x00); + ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x80); + } else { + /* Quad mode on */ + ret |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x22); + ret = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x81); + } + + mdelay(25); + + ret |= jbt_standby_to_normal(jbt); if (ret) { jbt->resolution = old_resolution; - dev_err(&jbt->spi->dev, "Failed to set resolution '%s')\n", + dev_err(&jbt->spi->dev, + "Failed to set resolution '%s')\n", jbt_resolution_names[new_resolution]); } + } out_unlock: @@ -589,7 +593,7 @@ static ssize_t reset_write(struct device *dev, struct device_attribute *attr, mutex_unlock(&jbt->lock); - jbt->power_mode = JBT_POWER_MODE_DEEP_STANDBY; + jbt->power_mode = JBT_POWER_MODE_OFF; jbt6k74_enter_power_mode(jbt, old_power_mode); return count; @@ -616,27 +620,6 @@ static struct attribute_group jbt_attr_group = { .attrs = jbt_sysfs_entries, }; -/* FIXME: This in an ugly hack to delay display blanking. - When the jbt is in sleep mode it displays an all white screen and thus one - will a see a short flash. - By delaying the blanking we will give the backlight a chance to turn off and - thus avoid getting the flash */ -static void jbt_blank_worker(struct work_struct *work) -{ - struct jbt_info *jbt = container_of(work, struct jbt_info, - blank_work.work); - - switch (jbt->blank_mode) { - case FB_BLANK_NORMAL: - jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_SLEEP); - break; - case FB_BLANK_POWERDOWN: - jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_DEEP_STANDBY); - break; - default: - break; - } -} static int jbt6k74_set_mode(struct lcd_device *ld, struct fb_videomode *m) { @@ -649,7 +632,7 @@ static int jbt6k74_set_mode(struct lcd_device *ld, struct fb_videomode *m) ret = jbt6k74_set_resolution(jbt, JBT_RESOLUTION_VGA); } else { dev_err(&jbt->spi->dev, "Unknown resolution.\n"); - jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_SLEEP); + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF); } return ret; @@ -671,11 +654,11 @@ static int jbt6k74_set_power(struct lcd_device *ld, int power) break; case FB_BLANK_NORMAL: dev_dbg(&jbt->spi->dev, "blank\n"); - ret = schedule_delayed_work(&jbt->blank_work, HZ); + ret = jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF); break; case FB_BLANK_POWERDOWN: dev_dbg(&jbt->spi->dev, "powerdown\n"); - ret = schedule_delayed_work(&jbt->blank_work, HZ); + ret = jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF); break; default: break; @@ -691,10 +674,10 @@ static int jbt6k74_get_power(struct lcd_device *ld) switch (jbt->power_mode) { case JBT_POWER_MODE_NORMAL: return FB_BLANK_UNBLANK; - case JBT_POWER_MODE_SLEEP: + case JBT_POWER_MODE_OFF: return FB_BLANK_NORMAL; default: - return JBT_POWER_MODE_DEEP_STANDBY; + return JBT_POWER_MODE_OFF; } } @@ -728,6 +711,8 @@ static int __devinit jbt_probe(struct spi_device *spi) if (!jbt) return -ENOMEM; + jbt_global = jbt; + jbt->spi = spi; jbt->lcd_dev = lcd_device_register("jbt6k74-lcd", &spi->dev, jbt, @@ -738,11 +723,9 @@ static int __devinit jbt_probe(struct spi_device *spi) goto err_free_drvdata; } - INIT_DELAYED_WORK(&jbt->blank_work, jbt_blank_worker); - jbt->resolution = JBT_RESOLUTION_VGA; - jbt->power_mode = JBT_POWER_MODE_DEEP_STANDBY; jbt->next_sleep = jiffies + msecs_to_jiffies(120); + jbt->power_mode = JBT_POWER_MODE_OFF; mutex_init(&jbt->lock); dev_set_drvdata(&spi->dev, jbt); @@ -831,6 +814,24 @@ static int __devexit jbt_remove(struct spi_device *spi) return 0; } +/* Begin horrible layering violations in the interest of making stuff work */ + +int jbt6k74_finish_resolutionchange(enum jbt_resolution new_resolution) +{ + if ( !jbt_global ) return 0; + return jbt6k74_set_resolution(jbt_global, new_resolution); +} +EXPORT_SYMBOL_GPL(jbt6k74_finish_resolutionchange); + +void jbt6k74_setpower(enum jbt_power_mode new_power) +{ + if ( !jbt_global ) return; + jbt6k74_enter_power_mode(jbt_global, new_power); +} +EXPORT_SYMBOL_GPL(jbt6k74_setpower); + +/* End horrible layering violations */ + #ifdef CONFIG_PM static int jbt_suspend(struct spi_device *spi, pm_message_t state) { @@ -838,8 +839,7 @@ static int jbt_suspend(struct spi_device *spi, pm_message_t state) jbt->suspend_mode = jbt->power_mode; - jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_DEEP_STANDBY); - jbt->suspended = 1; + jbt6k74_enter_power_mode(jbt, JBT_POWER_MODE_OFF); dev_info(&spi->dev, "suspended\n"); diff --git a/include/linux/jbt6k74.h b/include/linux/jbt6k74.h index 75488c4..2010bdc 100644 --- a/include/linux/jbt6k74.h +++ b/include/linux/jbt6k74.h @@ -3,6 +3,22 @@ #include +enum jbt_resolution { + JBT_RESOLUTION_VGA, + JBT_RESOLUTION_QVGA, +}; + +enum jbt_power_mode { + JBT_POWER_MODE_OFF, + JBT_POWER_MODE_STANDBY, + JBT_POWER_MODE_NORMAL, +}; + +extern void jbt6k74_setpower(enum jbt_power_mode new_power); +extern int jbt6k74_prepare_resolutionchange(enum jbt_resolution new_resolution); +extern int jbt6k74_finish_resolutionchange(enum jbt_resolution new_resolution); + + /* * struct jbt6k74_platform_data - Platform data for jbt6k74 driver * @probe_completed: Callback to be called when the driver has been @@ -13,7 +29,6 @@ */ struct jbt6k74_platform_data { void (*probe_completed)(struct device *dev); - void (*enable_pixel_clock)(struct device *dev, int enable); int gpio_reset; }; -- 1.7.1