summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Cai <R63905@freescale.com>2009-12-14 21:37:52 +0800
committerRobby Cai <R63905@freescale.com>2009-12-15 15:24:13 +0800
commitb5646ea772e050a1dd75eb885dd77d2d609a4b5e (patch)
tree38a7734f1c6488fed4642de3f5414bb75761e6e1
parentcf2dd458f769e3229a8a25d00ee95bf05121de0b (diff)
ENGR00118892 MX23: iMX233 disable lcd clock when LCD off
Disable lcdif controller when suspend or screen blanked. By doing so, can set CPU freq to 64000Khz (ENGR00119096) Signed-off-by: Robby Cai <R63905@freescale.com>
-rw-r--r--arch/arm/mach-stmp378x/include/mach/lcdif.h1
-rw-r--r--drivers/video/stmp37xxfb.c222
2 files changed, 171 insertions, 52 deletions
diff --git a/arch/arm/mach-stmp378x/include/mach/lcdif.h b/arch/arm/mach-stmp378x/include/mach/lcdif.h
index ef5647a7a618..28551b55ef97 100644
--- a/arch/arm/mach-stmp378x/include/mach/lcdif.h
+++ b/arch/arm/mach-stmp378x/include/mach/lcdif.h
@@ -73,6 +73,7 @@ struct stmp3xxx_platform_fb_entry {
struct stmp3xxx_platform_fb_data {
struct list_head list;
struct stmp3xxx_platform_fb_entry *cur;
+ struct stmp3xxx_platform_fb_entry *next;
};
#define STMP3XXX_LCDIF_PANEL_INIT 1
diff --git a/drivers/video/stmp37xxfb.c b/drivers/video/stmp37xxfb.c
index cb140fd73554..07cbb27352e8 100644
--- a/drivers/video/stmp37xxfb.c
+++ b/drivers/video/stmp37xxfb.c
@@ -25,7 +25,6 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/mutex.h>
-#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/uaccess.h>
@@ -42,10 +41,20 @@
#define NUM_SCREENS 1
+enum {
+ F_DISABLE = 0,
+ F_ENABLE,
+ F_REENABLE,
+ F_STARTUP,
+};
+
struct stmp3xxx_fb_data {
struct fb_info info;
struct stmp3xxx_platform_fb_data *pdata;
- int is_blank;
+ struct work_struct work;
+ struct mutex blank_mutex;
+ u32 state;
+ u32 task_state;
ssize_t mem_size;
ssize_t map_size;
dma_addr_t phys_start;
@@ -63,6 +72,122 @@ struct stmp3xxx_fb_data {
static int stmp3xxxfb_blank(int blank, struct fb_info *info);
static unsigned char *default_panel_name;
static struct stmp3xxx_fb_data *cdata;
+static void init_timings(struct stmp3xxx_fb_data *data);
+
+static void stmp3xxxfb_enable_controller(struct stmp3xxx_fb_data *data)
+{
+ struct stmp3xxx_platform_fb_entry *pentry = data->pdata->cur;
+
+ if (!data || !data->pdata || !data->pdata->cur)
+ return;
+
+ stmp3xxx_init_lcdif();
+ init_timings(data);
+ pentry->init_panel(data->dev, data->phys_start,
+ data->info.fix.smem_len, data->pdata->cur);
+ pentry->run_panel();
+
+ if (pentry->blank_panel)
+ pentry->blank_panel(FB_BLANK_UNBLANK);
+}
+
+static void stmp3xxxfb_disable_controller(struct stmp3xxx_fb_data *data)
+{
+ struct stmp3xxx_platform_fb_entry *pentry = data->pdata->cur;
+
+ if (!data || !data->pdata || !data->pdata->cur)
+ return;
+
+ if (pentry->blank_panel)
+ pentry->blank_panel(FB_BLANK_POWERDOWN);
+
+ if (pentry->stop_panel)
+ pentry->stop_panel();
+ pentry->release_panel(data->dev, pentry);
+}
+
+static void set_controller_state(struct stmp3xxx_fb_data *data, u32 state)
+{
+ struct stmp3xxx_platform_fb_entry *pentry = data->pdata->cur;
+ struct fb_info *info = &data->info;
+ u32 old_state;
+
+ mutex_lock(&data->blank_mutex);
+ old_state = data->state;
+ pr_debug("%s, old_state %d, state %d\n", __func__, old_state, state);
+
+ switch (state) {
+ case F_DISABLE:
+ /*
+ * Disable controller
+ */
+ if (old_state != F_DISABLE) {
+ data->state = F_DISABLE;
+ stmp3xxxfb_disable_controller(data);
+ }
+ break;
+
+ case F_REENABLE:
+ /*
+ * Re-enable the controller when panel changed.
+ */
+ if (old_state == F_ENABLE || old_state == F_STARTUP) {
+ stmp3xxxfb_disable_controller(data);
+
+ pentry = data->pdata->cur = data->pdata->next;
+ info->fix.smem_len = pentry->y_res * pentry->x_res *
+ pentry->bpp / 8;
+ info->screen_size = info->fix.smem_len;
+ memset((void *)info->screen_base, 0, info->screen_size);
+
+ stmp3xxxfb_enable_controller(data);
+
+ data->state = F_ENABLE;
+ } else if (old_state == F_DISABLE) {
+ pentry = data->pdata->cur = data->pdata->next;
+ info->fix.smem_len = pentry->y_res * pentry->x_res *
+ pentry->bpp / 8;
+ info->screen_size = info->fix.smem_len;
+ memset((void *)info->screen_base, 0, info->screen_size);
+
+ data->state = F_DISABLE;
+ }
+ break;
+
+ case F_ENABLE:
+ if (old_state != F_ENABLE) {
+ data->state = F_ENABLE;
+ stmp3xxxfb_enable_controller(data);
+ }
+ break;
+ }
+ mutex_unlock(&data->blank_mutex);
+
+}
+
+static void stmp3xxxfb_task(struct work_struct *work)
+{
+ struct stmp3xxx_fb_data *data =
+ container_of(work, struct stmp3xxx_fb_data, work);
+
+ u32 state = xchg(&data->task_state, -1);
+ pr_debug("%s: state = %d, data->task_state = %d\n",
+ __func__, state, data->task_state);
+
+ set_controller_state(data, state);
+}
+
+static void stmp3xxx_schedule_work(struct stmp3xxx_fb_data *data, u32 state)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ data->task_state = state;
+ schedule_work(&data->work);
+
+ local_irq_restore(flags);
+}
static irqreturn_t lcd_irq_handler(int irq, void *dev_id)
{
@@ -295,23 +420,10 @@ static int stmp3xxxfb_set_par(struct fb_info *info)
if (pentry == pdata->cur || !pdata->cur)
return 0;
- /* release prev panel */
- stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
- if (pdata->cur->stop_panel)
- pdata->cur->stop_panel();
- pdata->cur->release_panel(data->dev, pdata->cur);
-
- info->fix.smem_len = pentry->y_res * pentry->x_res * pentry->bpp / 8;
- info->screen_size = info->fix.smem_len;
- memset((void *)info->screen_base, 0, info->screen_size);
-
/* init next panel */
- pdata->cur = pentry;
- stmp3xxx_init_lcdif();
- pentry->init_panel(data->dev, data->phys_start, info->fix.smem_len,
- pentry);
- pentry->run_panel();
- stmp3xxxfb_blank(FB_BLANK_UNBLANK, &data->info);
+ pdata->next = pentry;
+
+ set_controller_state(data, F_REENABLE);
return 0;
}
@@ -441,11 +553,25 @@ static int stmp3xxxfb_ioctl(struct fb_info *info, unsigned int cmd,
static int stmp3xxxfb_blank(int blank, struct fb_info *info)
{
struct stmp3xxx_fb_data *data = (struct stmp3xxx_fb_data *)info;
- int ret = data->pdata->cur->blank_panel ?
- data->pdata->cur->blank_panel(blank) :
- -ENOTSUPP;
- if (ret == 0)
- data->is_blank = (blank != FB_BLANK_UNBLANK);
+ int ret = 0;
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_POWERDOWN:
+ pr_debug("%s: FB_BLANK_POWERDOWN\n", __func__);
+ stmp3xxx_schedule_work(data, F_DISABLE);
+ break;
+
+ case FB_BLANK_UNBLANK:
+ pr_debug("%s: FB_BLANK_UNBLANK\n", __func__);
+ stmp3xxx_schedule_work(data, F_ENABLE);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
return ret;
}
@@ -519,14 +645,24 @@ static int stmp3xxxfb_notifier(struct notifier_block *self,
struct stmp3xxxfb_notifier_block *block =
container_of(self, struct stmp3xxxfb_notifier_block, nb);
struct stmp3xxx_fb_data *data = block->fb_data;
+ struct stmp3xxx_platform_fb_entry *pentry = data->pdata->cur;
+ u32 old_state = data->state;
+ if (!data || !data->pdata || !data->pdata->cur)
+ return NOTIFY_BAD;
+
+ /* REVISIT */
switch (phase) {
- case CPUFREQ_POSTCHANGE:
- stmp3xxxfb_blank(FB_BLANK_UNBLANK, &data->info);
+ case CPUFREQ_PRECHANGE:
+ if (old_state == F_ENABLE)
+ if (pentry->blank_panel)
+ pentry->blank_panel(FB_BLANK_POWERDOWN);
break;
- case CPUFREQ_PRECHANGE:
- stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
+ case CPUFREQ_POSTCHANGE:
+ if (old_state == F_ENABLE)
+ if (pentry->blank_panel)
+ pentry->blank_panel(FB_BLANK_UNBLANK);
break;
default:
@@ -618,6 +754,9 @@ static int __devinit stmp3xxxfb_probe(struct platform_device *pdev)
}
dev_dbg(&pdev->dev, "allocated at %p:0x%x\n", data->virt_start,
data->phys_start);
+ mutex_init(&data->blank_mutex);
+ INIT_WORK(&data->work, stmp3xxxfb_task);
+ data->state = F_STARTUP;
stmp3xxxfb_default.bits_per_pixel = pentry->bpp;
/* NB: rotated */
@@ -750,13 +889,8 @@ out:
static int stmp3xxxfb_remove(struct platform_device *pdev)
{
struct stmp3xxx_fb_data *data = platform_get_drvdata(pdev);
- struct stmp3xxx_platform_fb_data *pdata = pdev->dev.platform_data;
- struct stmp3xxx_platform_fb_entry *pentry = pdata->cur;
- stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
- if (pentry->stop_panel)
- pentry->stop_panel();
- pentry->release_panel(&pdev->dev, pentry);
+ set_controller_state(data, F_DISABLE);
unregister_framebuffer(&data->info);
framebuffer_release(&data->info);
@@ -774,33 +908,17 @@ static int stmp3xxxfb_remove(struct platform_device *pdev)
static int stmp3xxxfb_suspend(struct platform_device *pdev, pm_message_t state)
{
struct stmp3xxx_fb_data *data = platform_get_drvdata(pdev);
- struct stmp3xxx_platform_fb_data *pdata = pdev->dev.platform_data;
- struct stmp3xxx_platform_fb_entry *pentry = pdata->cur;
- int ret;
- ret = stmp3xxxfb_blank(FB_BLANK_POWERDOWN, &data->info);
- if (ret)
- goto out;
- if (pentry->stop_panel)
- pentry->stop_panel();
- pentry->release_panel(data->dev, pentry);
+ set_controller_state(data, F_DISABLE);
-out:
- return ret;
+ return 0;
}
static int stmp3xxxfb_resume(struct platform_device *pdev)
{
struct stmp3xxx_fb_data *data = platform_get_drvdata(pdev);
- struct stmp3xxx_platform_fb_data *pdata = pdev->dev.platform_data;
- struct stmp3xxx_platform_fb_entry *pentry = pdata->cur;
- stmp3xxx_init_lcdif();
- init_timings(data);
- pentry->init_panel(data->dev, data->phys_start, data->info.fix.smem_len,
- pentry);
- pentry->run_panel();
- stmp3xxxfb_blank(FB_BLANK_UNBLANK, &data->info);
+ set_controller_state(data, F_ENABLE);
return 0;
}
#else