From aded2e51a7a2113dae6431c858df1d95fb312851 Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dbaryshkov@gmail.com>
Date: Wed, 13 Feb 2008 19:34:08 +0300
Subject: [PATCH] modeswitching

Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
---
 arch/arm/mach-pxa/tosa.c          |   33 +++++++++++++++++++++++++++++++-
 drivers/mfd/tc6393xb.c            |    2 +-
 drivers/video/backlight/tosa_bl.c |   38 +++++++++++++++++++++++++++++++++---
 drivers/video/tmiofb.c            |    8 +++---
 include/asm-arm/arch-pxa/tosa.h   |    2 +
 include/linux/mfd/tc6393xb.h      |    2 +-
 include/linux/mfd/tmio.h          |    2 +-
 7 files changed, 75 insertions(+), 12 deletions(-)

diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c
index 94c9915..6631de2 100644
--- a/arch/arm/mach-pxa/tosa.c
+++ b/arch/arm/mach-pxa/tosa.c
@@ -455,9 +455,40 @@ static struct fb_videomode tosa_tc6393xb_lcd_mode[] = {
 	}
 };
 
+static DEFINE_SPINLOCK(tosa_lcd_mode_lock);
+
+static const struct fb_videomode *tosa_lcd_mode = &tosa_tc6393xb_lcd_mode[0];
+
+static int tosa_lcd_set_mode(struct platform_device *fb_dev,
+					const struct fb_videomode *mode)
+{
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tosa_lcd_mode_lock, flags);
+	rc = tc6393xb_lcd_mode(fb_dev, mode);
+	if (!rc)
+		tosa_lcd_mode = mode;
+	spin_unlock_irqrestore(&tosa_lcd_mode_lock, flags);
+
+	return rc;
+}
+
+const struct fb_videomode *tosa_lcd_get_mode(void)
+{
+	unsigned long flags;
+	const struct fb_videomode *mode;
+
+	spin_lock_irqsave(&tosa_lcd_mode_lock, flags);
+	mode = tosa_lcd_mode;
+	spin_unlock_irqrestore(&tosa_lcd_mode_lock, flags);
+
+	return mode;
+}
+
 static struct tmio_fb_data tosa_tc6393xb_fb_config = {
 	.lcd_set_power	= tc6393xb_lcd_set_power,
-	.lcd_mode	= tc6393xb_lcd_mode,
+	.lcd_mode	= tosa_lcd_set_mode,
 	.num_modes	= ARRAY_SIZE(tosa_tc6393xb_lcd_mode),
 	.modes		= &tosa_tc6393xb_lcd_mode[0],
 };
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index 9001687..21190f3 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -196,7 +196,7 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on)
 EXPORT_SYMBOL(tc6393xb_lcd_set_power);
 
 int tc6393xb_lcd_mode(struct platform_device *fb_dev,
-					struct fb_videomode *mode) {
+					const struct fb_videomode *mode) {
 	struct tc6393xb			*tc6393xb =
 		platform_get_drvdata(to_platform_device(fb_dev->dev.parent));
 	struct tc6393xb_scr __iomem	*scr	= tc6393xb->scr;
diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c
index 9ef0bfb..45fc6ae 100644
--- a/drivers/video/backlight/tosa_bl.c
+++ b/drivers/video/backlight/tosa_bl.c
@@ -48,6 +48,9 @@ struct tosa_bl_data {
 	struct ssp_dev		nssp_dev;
 	struct ssp_state	nssp_state;
 
+	/* listen for mode changes */
+	struct notifier_block	fb_notif;
+
 	struct backlight_device	*bl_dev;
 };
 
@@ -103,14 +106,19 @@ static void tosa_lcd_tg_init(struct tosa_bl_data *data)
 	pxa_nssp_output(data, TG_GPOSR,0x02);		/* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
 }
 
-static void tosa_lcd_tg_on(struct tosa_bl_data *data/*, const struct fb_videomode *mode*/)
+static void tosa_lcd_tg_on(struct tosa_bl_data *data)
 {
-	const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
+	const struct fb_videomode *mode = tosa_lcd_get_mode();
+
+	int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
+
+	if (mode->yres == 640)
+		value |= TG_REG0_VQV;
 
 	tosa_lcd_tg_init(data);
 
-	dev_dbg(&data->bl_dev->dev, "tosa_lcd_on\n");
-	pxa_nssp_output(data, TG_PNLCTL, value | (/*mode->yres == 320 ? 0 : */ TG_REG0_VQV));
+	dev_dbg(&data->bl_dev->dev, "tosa_lcd_on: %04x (%d)\n", value, mode->yres);
+	pxa_nssp_output(data, TG_PNLCTL, value);
 
 	/* TG LCD pannel power up */
 	pxa_nssp_output(data, TG_PINICTL,0x4);
@@ -173,6 +181,20 @@ static struct backlight_ops tosa_bl_ops = {
 	.update_status		= tosa_bl_update_status,
 };
 
+static int fb_notifier_callback(struct notifier_block *self,
+				unsigned long event, void *_data)
+{
+	struct tosa_bl_data *bl_data =
+		container_of(self, struct tosa_bl_data, fb_notif);
+
+	if (event != FB_EVENT_MODE_CHANGE && event != FB_EVENT_MODE_CHANGE_ALL)
+		return 0;
+
+	tosa_bl_update_status(bl_data->bl_dev);
+
+	return 0;
+}
+
 static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address,
                         int kind)
 {
@@ -238,9 +260,15 @@ static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address,
 	data->bl_dev->props.power = FB_BLANK_UNBLANK;
 	backlight_update_status(data->bl_dev);
 
+	data->fb_notif.notifier_call = fb_notifier_callback;
+	err = fb_register_client(&data->fb_notif);
+	if (err)
+		goto err_fb_register;
 
 	return 0;
 
+err_fb_register:
+	backlight_device_unregister(data->bl_dev);
 err_bl_register:
 	tosa_set_backlight(data, 0);
 	tosa_lcd_tg_off(data);
@@ -265,6 +293,8 @@ static int tosa_bl_detach_client(struct i2c_client *client)
 	int err = 0;
 	struct tosa_bl_data *data = i2c_get_clientdata(client);
 
+	fb_unregister_client(&data->fb_notif);
+
 	backlight_device_unregister(data->bl_dev);
 
 	tosa_set_backlight(data, 0);
diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
index 958ee8a..cc75df9 100644
--- a/drivers/video/tmiofb.c
+++ b/drivers/video/tmiofb.c
@@ -618,17 +618,17 @@ static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 
 static int tmiofb_set_par(struct fb_info *info)
 {
-/*	struct fb_var_screeninfo	*var	= &info->var;
+	struct fb_var_screeninfo	*var	= &info->var;
 	struct fb_videomode		*mode;
 
 	mode = tmiofb_find_mode(info, var);
 	if (!mode)
 		return -EINVAL;
 
-	if (info->mode == mode)
-		return 0;
+/*	if (info->mode == mode)
+		return 0;*/
 
-	info->mode		= mode; */
+	info->mode		= mode;
 	info->fix.line_length	= info->mode->xres * 2;
 
 	tmiofb_hw_mode(to_platform_device(info->device));
diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h
index 410fa9a..624c636 100644
--- a/include/asm-arm/arch-pxa/tosa.h
+++ b/include/asm-arm/arch-pxa/tosa.h
@@ -230,4 +230,6 @@ extern struct platform_device tosascoop_device;
 #define TOSA_KEY_MAIL		KEY_MAIL
 #endif
 
+const struct fb_videomode *tosa_lcd_get_mode(void);
+
 #endif /* _ASM_ARCH_TOSA_H_ */
diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h
index 97c4c7c..8ab9e91 100644
--- a/include/linux/mfd/tc6393xb.h
+++ b/include/linux/mfd/tc6393xb.h
@@ -53,7 +53,7 @@ struct tc6393xb_platform_data {
 
 extern int tc6393xb_lcd_set_power(struct platform_device *fb_dev, bool on);
 extern int tc6393xb_lcd_mode(struct platform_device *fb_dev,
-					struct fb_videomode *mode);
+					const struct fb_videomode *mode);
 
 /*
  * Relative to irq_base
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index b6d4aac..fe7ff2d 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -19,7 +19,7 @@ struct tmio_fb_data {
 	int			(*lcd_set_power)(struct platform_device *fb_dev,
 								bool on);
 	int			(*lcd_mode)(struct platform_device *fb_dev,
-						struct fb_videomode *mode);
+					const struct fb_videomode *mode);
 	int			num_modes;
 	struct fb_videomode	*modes;
 };
-- 
1.5.4.1