Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH] i2c: Split I2C_M_NOSTART support out of I2C_FUNC_PROTOCOL_MANGLING
From: Wolfram Sang @ 2012-05-03  9:52 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jean Delvare, Florian Tobias Schandinat, Dmitry Torokhov,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1335443839-22872-1-git-send-email-broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 2093 bytes --]

On Thu, Apr 26, 2012 at 01:37:19PM +0100, Mark Brown wrote:
> Since there are uses for I2C_M_NOSTART which are much more sensible and
> standard than most of the protocol mangling functionality (the main one
> being gather writes to devices where something like a register address
> needs to be inserted before a block of data) create a new I2C_FUNC_NOSTART
> for this feature and update all the users to use it.
> 
> In the case of regmap-i2c we remove the requirement for mangling as
> I2C_M_NOSTART is the only mangling feature which is being used.
> 
> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

In general, I like it. One thing below:

> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -540,7 +540,7 @@ struct i2c_msg {
>  	__u16 flags;
>  #define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
>  #define I2C_M_RD		0x0001	/* read data, from slave to master */
> -#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
> +#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
>  #define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
>  #define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
>  #define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
> @@ -568,6 +568,7 @@ struct i2c_msg {
>  #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
>  #define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
>  #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */
> +#define I2C_FUNC_NOSTART		0x10000000 /* I2C_M_NOSTART */

In this file, the comment for MANGLING should be adapted, too. It says
currently:

#define I2C_FUNC_PROTOCOL_MANGLING      0x00000004 /* I2C_M_NOSTART etc. */

NOSTART is now out of MANGLING.

Also, I'd think the FUNC_NOSTART bit should be 0x08 and SMBUS_PEC 0x4000. This
will be more intuitive, probably?

Thanks,

   Wolfram

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* [PATCH v2 4/4] OMAPDSS: APPLY: Remove display dependency from overlay and manager checks
From: Archit Taneja @ 2012-05-03  7:19 UTC (permalink / raw)
  To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1336028864-13895-1-git-send-email-archit@ti.com>

In order to check the validity of overlay and manager info, there was a need to
use the omap_dss_device struct to get the panel resolution. The manager's
private data in APPLY now contains the manager timings. Hence, we don't need to
rely on the display resolution any more.

Create a function dss_mgr_get_timings() which returns the timings in manager's
private data. Remove the need of passing omap_dss_device structs in the
functions which check for overlay and managers.

Have some initial values for manager timings in apply_init(), these would ensure
that manager checks don't fail if an interface driver or a panel driver hasn't
set the manager timings yet.

Signed-off-by: Archit Taneja <archit@ti.com>
---
 drivers/video/omap2/dss/apply.c   |   51 +++++++++++++++++++++++++++---------
 drivers/video/omap2/dss/dss.h     |    5 +--
 drivers/video/omap2/dss/manager.c |    6 ++--
 drivers/video/omap2/dss/overlay.c |   20 +++++++-------
 4 files changed, 53 insertions(+), 29 deletions(-)

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 42c3854..052d9a2 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -137,10 +137,30 @@ static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
 void dss_apply_init(void)
 {
 	const int num_ovls = dss_feat_get_num_ovls();
+	const int num_mgrs = dss_feat_get_num_ovls();
 	int i;
+	/* Use dummy manager timings during initialization */
+	struct omap_video_timings timings = {
+		.hsw		= 1,
+		.hfp		= 1,
+		.hbp		= 1,
+		.vsw		= 1,
+		.vfp		= 0,
+		.vbp		= 0,
+		.x_res	= dss_feat_get_param_max(FEAT_PARAM_MGR_WIDTH),
+		.y_res	= dss_feat_get_param_max(FEAT_PARAM_MGR_HEIGHT),
+	};
 
 	spin_lock_init(&data_lock);
 
+	for (i = 0; i < num_mgrs; i++) {
+		struct mgr_priv_data *mp;
+
+		mp = &dss_data.mgr_priv_data_array[i];
+
+		mp->timings = timings;
+	}
+
 	for (i = 0; i < num_ovls; ++i) {
 		struct ovl_priv_data *op;
 
@@ -181,7 +201,7 @@ static bool mgr_manual_update(struct omap_overlay_manager *mgr)
 }
 
 static int dss_check_settings_low(struct omap_overlay_manager *mgr,
-		struct omap_dss_device *dssdev, bool applying)
+		bool applying)
 {
 	struct omap_overlay_info *oi;
 	struct omap_overlay_manager_info *mi;
@@ -211,26 +231,24 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr,
 		ois[ovl->id] = oi;
 	}
 
-	return dss_mgr_check(mgr, dssdev, mi, ois);
+	return dss_mgr_check(mgr, mi, ois);
 }
 
 /*
  * check manager and overlay settings using overlay_info from data->info
  */
-static int dss_check_settings(struct omap_overlay_manager *mgr,
-		struct omap_dss_device *dssdev)
+static int dss_check_settings(struct omap_overlay_manager *mgr)
 {
-	return dss_check_settings_low(mgr, dssdev, false);
+	return dss_check_settings_low(mgr, false);
 }
 
 /*
  * check manager and overlay settings using overlay_info from ovl->info if
  * dirty and from data->info otherwise
  */
-static int dss_check_settings_apply(struct omap_overlay_manager *mgr,
-		struct omap_dss_device *dssdev)
+static int dss_check_settings_apply(struct omap_overlay_manager *mgr)
 {
-	return dss_check_settings_low(mgr, dssdev, true);
+	return dss_check_settings_low(mgr, true);
 }
 
 static bool need_isr(void)
@@ -684,7 +702,7 @@ static void dss_write_regs(void)
 		if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
 			continue;
 
-		r = dss_check_settings(mgr, mgr->device);
+		r = dss_check_settings(mgr);
 		if (r) {
 			DSSERR("cannot write registers for manager %s: "
 					"illegal configuration\n", mgr->name);
@@ -751,7 +769,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
 
 	WARN_ON(mp->updating);
 
-	r = dss_check_settings(mgr, mgr->device);
+	r = dss_check_settings(mgr);
 	if (r) {
 		DSSERR("cannot start manual update: illegal configuration\n");
 		spin_unlock_irqrestore(&data_lock, flags);
@@ -898,7 +916,7 @@ int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
 
 	spin_lock_irqsave(&data_lock, flags);
 
-	r = dss_check_settings_apply(mgr, mgr->device);
+	r = dss_check_settings_apply(mgr);
 	if (r) {
 		spin_unlock_irqrestore(&data_lock, flags);
 		DSSERR("failed to apply settings: illegal configuration.\n");
@@ -1091,7 +1109,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
 
 	mp->enabled = true;
 
-	r = dss_check_settings(mgr, mgr->device);
+	r = dss_check_settings(mgr);
 	if (r) {
 		DSSERR("failed to enable manager %d: check_settings failed\n",
 				mgr->id);
@@ -1291,6 +1309,13 @@ void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
 	mutex_unlock(&apply_lock);
 }
 
+struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr)
+{
+	struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+	return &mp->timings;
+}
+
 int dss_ovl_set_info(struct omap_overlay *ovl,
 		struct omap_overlay_info *info)
 {
@@ -1458,7 +1483,7 @@ int dss_ovl_enable(struct omap_overlay *ovl)
 
 	op->enabling = true;
 
-	r = dss_check_settings(ovl->manager, ovl->manager->device);
+	r = dss_check_settings(ovl->manager);
 	if (r) {
 		DSSERR("failed to enable overlay %d: check_settings failed\n",
 				ovl->id);
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index a148712..f2a51c6 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -179,6 +179,7 @@ int dss_mgr_set_device(struct omap_overlay_manager *mgr,
 int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
 void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
 		struct omap_video_timings *timings);
+struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr);
 
 bool dss_ovl_is_enabled(struct omap_overlay *ovl);
 int dss_ovl_enable(struct omap_overlay *ovl);
@@ -211,7 +212,6 @@ int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
 int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
 		struct omap_video_timings *timings);
 int dss_mgr_check(struct omap_overlay_manager *mgr,
-		struct omap_dss_device *dssdev,
 		struct omap_overlay_manager_info *info,
 		struct omap_overlay_info **overlay_infos);
 
@@ -222,8 +222,7 @@ void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr);
 void dss_recheck_connections(struct omap_dss_device *dssdev, bool force);
 int dss_ovl_simple_check(struct omap_overlay *ovl,
 		const struct omap_overlay_info *info);
-int dss_ovl_check(struct omap_overlay *ovl,
-		struct omap_overlay_info *info, struct omap_dss_device *dssdev);
+int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info);
 
 /* DSS */
 int dss_init_platform_driver(void);
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index 264ad7c..1fd339b 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -666,11 +666,11 @@ int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
 }
 
 int dss_mgr_check(struct omap_overlay_manager *mgr,
-		struct omap_dss_device *dssdev,
 		struct omap_overlay_manager_info *info,
 		struct omap_overlay_info **overlay_infos)
 {
 	struct omap_overlay *ovl;
+	struct omap_video_timings *mgr_timings = dss_mgr_get_timings(mgr);
 	int r;
 
 	if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) {
@@ -679,7 +679,7 @@ int dss_mgr_check(struct omap_overlay_manager *mgr,
 			return r;
 	}
 
-	r = dss_mgr_check_timings(mgr, &dssdev->panel.timings);
+	r = dss_mgr_check_timings(mgr, mgr_timings);
 	if (r)
 		return r;
 
@@ -692,7 +692,7 @@ int dss_mgr_check(struct omap_overlay_manager *mgr,
 		if (oi = NULL)
 			continue;
 
-		r = dss_ovl_check(ovl, oi, dssdev);
+		r = dss_ovl_check(ovl, oi);
 		if (r)
 			return r;
 	}
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index 6e82181..8629e1a 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -631,16 +631,16 @@ int dss_ovl_simple_check(struct omap_overlay *ovl,
 	return 0;
 }
 
-int dss_ovl_check(struct omap_overlay *ovl,
-		struct omap_overlay_info *info, struct omap_dss_device *dssdev)
+int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info)
 {
 	u16 outw, outh;
-	u16 dw, dh;
+	struct omap_video_timings *mgr_timings;
+	u16 mgr_width, mgr_height;
 
-	if (dssdev = NULL)
-		return 0;
+	mgr_timings = dss_mgr_get_timings(ovl->manager);
 
-	dssdev->driver->get_resolution(dssdev, &dw, &dh);
+	mgr_width = mgr_timings->x_res;
+	mgr_height = mgr_timings->y_res;
 
 	if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) = 0) {
 		outw = info->width;
@@ -657,17 +657,17 @@ int dss_ovl_check(struct omap_overlay *ovl,
 			outh = info->out_height;
 	}
 
-	if (dw < info->pos_x + outw) {
+	if (mgr_width < info->pos_x + outw) {
 		DSSERR("overlay %d horizontally not inside the display area "
 				"(%d + %d >= %d)\n",
-				ovl->id, info->pos_x, outw, dw);
+				ovl->id, info->pos_x, outw, mgr_width);
 		return -EINVAL;
 	}
 
-	if (dh < info->pos_y + outh) {
+	if (mgr_height < info->pos_y + outh) {
 		DSSERR("overlay %d vertically not inside the display area "
 				"(%d + %d >= %d)\n",
-				ovl->id, info->pos_y, outh, dh);
+				ovl->id, info->pos_y, outh, mgr_height);
 		return -EINVAL;
 	}
 
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH v2 3/4] OMAPDSS: MANAGER: Create a function to check manager timings
From: Archit Taneja @ 2012-05-03  7:19 UTC (permalink / raw)
  To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1336028864-13895-1-git-send-email-archit@ti.com>

Create a function dss_mgr_check_timings() which wraps around the function
dispc_mgr_timings_ok(). This is mainly a clean up to hide dispc functions
from interface drivers.

dss_mgr_check_timings() is added in the function dss_mgr_check(), it currently
takes the timings maintained in the omap_dss_device struct. This would be later
replaced by the timings stored in the manager's private data.

Signed-off-by: Archit Taneja <archit@ti.com>
---
 drivers/video/omap2/dss/dpi.c     |    2 +-
 drivers/video/omap2/dss/dss.h     |    2 ++
 drivers/video/omap2/dss/manager.c |   15 +++++++++++++++
 3 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index 5d84ab0..8127f46 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -312,7 +312,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
 	unsigned long pck;
 	struct dispc_clock_info dispc_cinfo;
 
-	if (!dispc_mgr_timings_ok(dssdev->manager->id, timings))
+	if (!dss_mgr_check_timings(dssdev->manager, timings))
 		return -EINVAL;
 
 	if (timings->pixel_clock = 0)
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index ca59481..a148712 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -208,6 +208,8 @@ int dss_init_overlay_managers(struct platform_device *pdev);
 void dss_uninit_overlay_managers(struct platform_device *pdev);
 int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
 		const struct omap_overlay_manager_info *info);
+int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
+		struct omap_video_timings *timings);
 int dss_mgr_check(struct omap_overlay_manager *mgr,
 		struct omap_dss_device *dssdev,
 		struct omap_overlay_manager_info *info,
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index e736460..264ad7c 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -654,6 +654,17 @@ static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr,
 	return 0;
 }
 
+int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
+		struct omap_video_timings *timings)
+{
+	if (!dispc_mgr_timings_ok(mgr->id, timings)) {
+		DSSERR("check_manager: invalid timings\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 int dss_mgr_check(struct omap_overlay_manager *mgr,
 		struct omap_dss_device *dssdev,
 		struct omap_overlay_manager_info *info,
@@ -668,6 +679,10 @@ int dss_mgr_check(struct omap_overlay_manager *mgr,
 			return r;
 	}
 
+	r = dss_mgr_check_timings(mgr, &dssdev->panel.timings);
+	if (r)
+		return r;
+
 	list_for_each_entry(ovl, &mgr->overlays, list) {
 		struct omap_overlay_info *oi;
 		int r;
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH v2 2/4] OMAPDSS: Apply manager timings instead of direct DISPC writes
From: Archit Taneja @ 2012-05-03  7:19 UTC (permalink / raw)
  To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1336028864-13895-1-git-send-email-archit@ti.com>

Replace the function dispc_mgr_set_timings() with dss_mgr_set_timings() in the
interface drivers. The latter function ensures that the timing related DISPC
registers are configured according to the shadow register programming model.

Signed-off-by: Archit Taneja <archit@ti.com>
---
 drivers/video/omap2/dss/dpi.c  |    2 +-
 drivers/video/omap2/dss/dsi.c  |    5 ++---
 drivers/video/omap2/dss/hdmi.c |    2 +-
 drivers/video/omap2/dss/rfbi.c |    4 ++--
 drivers/video/omap2/dss/sdi.c  |    2 +-
 drivers/video/omap2/dss/venc.c |    2 +-
 6 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index cec1166..5d84ab0 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -156,7 +156,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
 		t->pixel_clock = pck;
 	}
 
-	dispc_mgr_set_timings(dssdev->manager->id, t);
+	dss_mgr_set_timings(dssdev->manager, t);
 
 	return 0;
 }
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index b6cf03c..db73598 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -4219,13 +4219,12 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
 		dispc_mgr_enable_stallmode(dssdev->manager->id, true);
 		dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1);
 
-		dispc_mgr_set_timings(dssdev->manager->id, &timings);
+		dss_mgr_set_timings(dssdev->manager, &timings);
 	} else {
 		dispc_mgr_enable_stallmode(dssdev->manager->id, false);
 		dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0);
 
-		dispc_mgr_set_timings(dssdev->manager->id,
-			&dssdev->panel.timings);
+		dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings);
 	}
 
 		dispc_mgr_set_lcd_display_type(dssdev->manager->id,
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 56f6e9c..8d4ff8c 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -376,7 +376,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
 	dispc_enable_gamma_table(0);
 
 	/* tv size */
-	dispc_mgr_set_timings(dssdev->manager->id, &dssdev->panel.timings);
+	dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings);
 
 	hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1);
 
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index a81ffcb..feadfab 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -320,7 +320,7 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
 
 	DSSDBG("rfbi_transfer_area %dx%d\n", width, height);
 
-	dispc_mgr_set_timings(dssdev->manager->id, &timings);
+	dss_mgr_set_timings(dssdev->manager, &timings);
 
 	dispc_mgr_enable(dssdev->manager->id, true);
 
@@ -804,7 +804,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev,
 	if (*w = 0 || *h = 0)
 		return -EINVAL;
 
-	dispc_mgr_set_timings(dssdev->manager->id, &timings);
+	dss_mgr_set_timings(dssdev->manager, &timings);
 
 	return 0;
 }
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index 741b834..67fbe7c 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -107,7 +107,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
 	}
 
 
-	dispc_mgr_set_timings(dssdev->manager->id, t);
+	dss_mgr_set_timings(dssdev->manager, t);
 
 	r = dss_set_clock_div(&dss_cinfo);
 	if (r)
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 30bbb63..e237464 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -444,7 +444,7 @@ static int venc_power_on(struct omap_dss_device *dssdev)
 	timings = dssdev->panel.timings;
 	timings.y_res /= 2;
 
-	dispc_mgr_set_timings(dssdev->manager->id, &timings);
+	dss_mgr_set_timings(dssdev->manager, &timings);
 
 	r = regulator_enable(venc.vdda_dac_reg);
 	if (r)
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH v2 1/4] OMAPDSS: APPLY: Add manager timings as extra_info in private data
From: Archit Taneja @ 2012-05-03  7:19 UTC (permalink / raw)
  To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1336028864-13895-1-git-send-email-archit@ti.com>

DISPC manager size and DISPC manager blanking parameters(for LCD managers)
follow the shadow register programming model. Currently, they are programmed
directly by the interface drivers.

To configure manager timings using APPLY, there is a need to introduce extra
info flags for managers, similar to what is done for overlays. This is needed
because timings aren't a part of overlay_manager_info struct configured by a
user of DSS, they are configured internally by the interface or panel drivers.

Add dirty and shadow_dirty extra_info flags for managers, update these flags
at the appropriate places. Rewrite the function extra_info_update_ongoing()
slightly as checking for manager's extra_info flags can simplify the code a bit.

Create function dss_mgr_set_timings() which applies the new manager timings to
extra_info.

Signed-off-by: Archit Taneja <archit@ti.com>
---
 drivers/video/omap2/dss/apply.c |   91 +++++++++++++++++++++++++++++++++------
 drivers/video/omap2/dss/dss.h   |    2 +
 2 files changed, 80 insertions(+), 13 deletions(-)

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index b10b3bc..42c3854 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -99,6 +99,11 @@ struct mgr_priv_data {
 
 	/* If true, a display is enabled using this manager */
 	bool enabled;
+
+	bool extra_info_dirty;
+	bool shadow_extra_info_dirty;
+
+	struct omap_video_timings timings;
 };
 
 static struct {
@@ -261,6 +266,20 @@ static bool need_isr(void)
 			if (mp->shadow_info_dirty)
 				return true;
 
+			/*
+			 * NOTE: we don't check extra_info flags for disabled
+			 * managers, once the manager is enabled, the extra_info
+			 * related manager changes will be taken in by HW.
+			 */
+
+			/* to write new values to registers */
+			if (mp->extra_info_dirty)
+				return true;
+
+			/* to set GO bit */
+			if (mp->shadow_extra_info_dirty)
+				return true;
+
 			list_for_each_entry(ovl, &mgr->overlays, list) {
 				struct ovl_priv_data *op;
 
@@ -305,7 +324,7 @@ static bool need_go(struct omap_overlay_manager *mgr)
 
 	mp = get_mgr_priv(mgr);
 
-	if (mp->shadow_info_dirty)
+	if (mp->shadow_info_dirty || mp->shadow_extra_info_dirty)
 		return true;
 
 	list_for_each_entry(ovl, &mgr->overlays, list) {
@@ -320,20 +339,16 @@ static bool need_go(struct omap_overlay_manager *mgr)
 /* returns true if an extra_info field is currently being updated */
 static bool extra_info_update_ongoing(void)
 {
-	const int num_ovls = omap_dss_get_num_overlays();
-	struct ovl_priv_data *op;
-	struct omap_overlay *ovl;
-	struct mgr_priv_data *mp;
+	const int num_mgrs = dss_feat_get_num_mgrs();
 	int i;
 
-	for (i = 0; i < num_ovls; ++i) {
-		ovl = omap_dss_get_overlay(i);
-		op = get_ovl_priv(ovl);
-
-		if (!ovl->manager)
-			continue;
+	for (i = 0; i < num_mgrs; ++i) {
+		struct omap_overlay_manager *mgr;
+		struct omap_overlay *ovl;
+		struct mgr_priv_data *mp;
 
-		mp = get_mgr_priv(ovl->manager);
+		mgr = omap_dss_get_overlay_manager(i);
+		mp = get_mgr_priv(mgr);
 
 		if (!mp->enabled)
 			continue;
@@ -341,8 +356,15 @@ static bool extra_info_update_ongoing(void)
 		if (!mp->updating)
 			continue;
 
-		if (op->extra_info_dirty || op->shadow_extra_info_dirty)
+		if (mp->extra_info_dirty || mp->shadow_extra_info_dirty)
 			return true;
+
+		list_for_each_entry(ovl, &mgr->overlays, list) {
+			struct ovl_priv_data *op = get_ovl_priv(ovl);
+
+			if (op->extra_info_dirty || op->shadow_extra_info_dirty)
+				return true;
+		}
 	}
 
 	return false;
@@ -601,6 +623,22 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
 	}
 }
 
+static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
+{
+	struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+	DSSDBGF("%d", mgr->id);
+
+	if (!mp->extra_info_dirty)
+		return;
+
+	dispc_mgr_set_timings(mgr->id, &mp->timings);
+
+	mp->extra_info_dirty = false;
+	if (mp->updating)
+		mp->shadow_extra_info_dirty = true;
+}
+
 static void dss_write_regs_common(void)
 {
 	const int num_mgrs = omap_dss_get_num_overlay_managers();
@@ -654,6 +692,7 @@ static void dss_write_regs(void)
 		}
 
 		dss_mgr_write_regs(mgr);
+		dss_mgr_write_regs_extra(mgr);
 	}
 }
 
@@ -693,6 +732,7 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
 
 	mp = get_mgr_priv(mgr);
 	mp->shadow_info_dirty = false;
+	mp->shadow_extra_info_dirty = false;
 
 	list_for_each_entry(ovl, &mgr->overlays, list) {
 		op = get_ovl_priv(ovl);
@@ -719,6 +759,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
 	}
 
 	dss_mgr_write_regs(mgr);
+	dss_mgr_write_regs_extra(mgr);
 
 	dss_write_regs_common();
 
@@ -1225,6 +1266,30 @@ err:
 	return r;
 }
 
+static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr,
+		struct omap_video_timings *timings)
+{
+	struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+	mp->timings = *timings;
+	mp->extra_info_dirty = true;
+}
+
+void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
+		struct omap_video_timings *timings)
+{
+	unsigned long flags;
+
+	mutex_lock(&apply_lock);
+
+	spin_lock_irqsave(&data_lock, flags);
+
+	dss_apply_mgr_timings(mgr, timings);
+
+	spin_unlock_irqrestore(&data_lock, flags);
+
+	mutex_unlock(&apply_lock);
+}
 
 int dss_ovl_set_info(struct omap_overlay *ovl,
 		struct omap_overlay_info *info)
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 5ca67f1..ca59481 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -177,6 +177,8 @@ void dss_mgr_get_info(struct omap_overlay_manager *mgr,
 int dss_mgr_set_device(struct omap_overlay_manager *mgr,
 		struct omap_dss_device *dssdev);
 int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
+void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
+		struct omap_video_timings *timings);
 
 bool dss_ovl_is_enabled(struct omap_overlay *ovl);
 int dss_ovl_enable(struct omap_overlay *ovl);
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH v2 0/4] OMAPDSS: APPLY: Treat overlay manager timings as shadow registers
From: Archit Taneja @ 2012-05-03  7:19 UTC (permalink / raw)
  To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1334561027-28569-1-git-send-email-archit@ti.com>

An overlay manager's timings (the manager size, and blanking parameters if an
LCD manager) are DISPC shadow registers, and they should hence follow the
correct programming model.

This set makes the timings an extra_info parameter in manager's private data .
The interface drivers now apply the timings in instead of directly writing to
registers.

This change also prevents the need to use display resolution for overlay
checks, hence making some of the APPLY functions less dependent on the display.

Changes since v2:

- Manager timings are represented as extra_info.
- The DISPC related cleanups are removed since they have already been taken.

These patches apply over:

git://gitorious.org/linux-omap-dss2/linux.git dev

Archit Taneja (4):
  OMAPDSS: APPLY: Add manager timings as extra_info in private data
  OMAPDSS: Apply manager timings instead of direct DISPC writes
  OMAPDSS: MANAGER: Create a function to check manager timings
  OMAPDSS: APPLY: Remove display dependency from overlay and manager
    checks

 drivers/video/omap2/dss/apply.c   |  142 ++++++++++++++++++++++++++++++-------
 drivers/video/omap2/dss/dpi.c     |    4 +-
 drivers/video/omap2/dss/dsi.c     |    5 +-
 drivers/video/omap2/dss/dss.h     |    9 ++-
 drivers/video/omap2/dss/hdmi.c    |    2 +-
 drivers/video/omap2/dss/manager.c |   19 +++++-
 drivers/video/omap2/dss/overlay.c |   20 +++---
 drivers/video/omap2/dss/rfbi.c    |    4 +-
 drivers/video/omap2/dss/sdi.c     |    2 +-
 drivers/video/omap2/dss/venc.c    |    2 +-
 10 files changed, 158 insertions(+), 51 deletions(-)

-- 
1.7.5.4


^ permalink raw reply

* [PATCH] backlight: apple_bl.c: include header for exported symbol prototypes
From: H Hartley Sweeten @ 2012-05-03  0:47 UTC (permalink / raw)
  To: Linux Kernel; +Cc: linux-fbdev, rpurdie, FlorianSchandinat

Include the header to pickup the exported symbol prototype.

Quites the sparse warning:

warning: symbol 'apple_bl_register' was not declared. Should it be static?
warning: symbol 'apple_bl_unregister' was not declared. Should it be static?

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>

---

diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c
index a523b25..ef5ca0d 100644
--- a/drivers/video/backlight/apple_bl.c
+++ b/drivers/video/backlight/apple_bl.c
@@ -25,6 +25,7 @@
 #include <linux/pci.h>
 #include <linux/acpi.h>
 #include <linux/atomic.h>
+#include <linux/apple_bl.h>
 
 static struct backlight_device *apple_backlight_device;
 

^ permalink raw reply related

* [PATCH] video: mb862xxfbdrv.c: local functions should not be exposed globally
From: H Hartley Sweeten @ 2012-05-03  0:37 UTC (permalink / raw)
  To: Linux Kernel; +Cc: linux-fbdev, FlorianSchandinat, agust

Functions not referenced outside of a source file should be marked
static to prevent them from being exposed globally.

Quiets the sparse warnings:

warning: symbol 'mb862xx_intr' was not declared. Should it be static?

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Cc: Anatolij Gustschin <agust@denx.de>

---

diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c
index 11a7a33..00ce1f3 100644
--- a/drivers/video/mb862xx/mb862xxfbdrv.c
+++ b/drivers/video/mb862xx/mb862xxfbdrv.c
@@ -579,7 +579,7 @@ static ssize_t mb862xxfb_show_dispregs(struct device *dev,
 
 static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL);
 
-irqreturn_t mb862xx_intr(int irq, void *dev_id)
+static irqreturn_t mb862xx_intr(int irq, void *dev_id)
 {
 	struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id;
 	unsigned long reg_ist, mask;

^ permalink raw reply related

* [PATCH] video: mb862xx-i2c: local functions should not be exposed globally
From: H Hartley Sweeten @ 2012-05-03  0:34 UTC (permalink / raw)
  To: Linux Kernel; +Cc: linux-fbdev, FlorianSchandinat, agust

Functions not referenced outside of a source file should be marked
static to prevent them from being exposed globally.

Quiets the sparse warnings:

warning: symbol 'mb862xx_i2c_stop' was not declared. Should it be static?

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Cc: Anatolij Gustschin <agust@denx.de>

---

diff --git a/drivers/video/mb862xx/mb862xx-i2c.c b/drivers/video/mb862xx/mb862xx-i2c.c
index 273769b..c87e17a 100644
--- a/drivers/video/mb862xx/mb862xx-i2c.c
+++ b/drivers/video/mb862xx/mb862xx-i2c.c
@@ -68,7 +68,7 @@ static int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last)
 	return 1;
 }
 
-void mb862xx_i2c_stop(struct i2c_adapter *adap)
+static void mb862xx_i2c_stop(struct i2c_adapter *adap)
 {
 	struct mb862xxfb_par *par = adap->algo_data;
 

^ permalink raw reply related

* [PATCH] video: fb_defio.c: local functions should not be exposed globally
From: H Hartley Sweeten @ 2012-05-03  0:23 UTC (permalink / raw)
  To: Linux Kernel; +Cc: linux-fbdev, jayalk, FlorianSchandinat

Functions not referenced outside of a source file should be marked
static to prevent them from being exposed globally.

Quiets the sparse warning:

warning: symbol 'fb_deferred_io_page' was not declared. Should it be static?

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Jaya Kumar <jayalk@intworks.biz>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>

---

diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 070f26f..1ddeb11 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -23,7 +23,7 @@
 #include <linux/rmap.h>
 #include <linux/pagemap.h>
 
-struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
+static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
 {
 	void *screen_base = (void __force *) info->screen_base;
 	struct page *page;

^ permalink raw reply related

* [PATCH] video: smscufx.c: local functions should not be exposed globally
From: H Hartley Sweeten @ 2012-05-03  0:17 UTC (permalink / raw)
  To: Linux Kernel; +Cc: linux-fbdev, steve.glendinning, FlorianSchandinat

Functions not referenced outside of a source file should be marked
static to prevent them from being exposed globally.

Quiets the sparse warnings:

warning: symbol 'ufx_handle_damage' was not declared. Should it be static?

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Steve Glendinning <steve.glendinning@smsc.com>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>

---

diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c
index 9985785..af3ef27 100644
--- a/drivers/video/smscufx.c
+++ b/drivers/video/smscufx.c
@@ -846,7 +846,7 @@ static void ufx_raw_rect(struct ufx_data *dev, u16 *cmd, int x, int y,
 	}
 }
 
-int ufx_handle_damage(struct ufx_data *dev, int x, int y,
+static int ufx_handle_damage(struct ufx_data *dev, int x, int y,
 	int width, int height)
 {
 	size_t packed_line_len = ALIGN((width * 2), 4);

^ permalink raw reply related

* Re: [PATCH v2] video/sis: Use SiS_DRAMType from init.h and annotate it __devinitconst
From: Florian Tobias Schandinat @ 2012-05-02 23:12 UTC (permalink / raw)
  To: Peter Huewe; +Cc: Thomas Winischhofer, linux-fbdev, linux-kernel
In-Reply-To: <1335731167-3311-1-git-send-email-peterhuewe@gmx.de>

Hi Peter,

On 04/29/2012 08:26 PM, Peter Huewe wrote:
> This patch removes the duplicated SiS_DRAMType from sis_main.c since it
> is already defined exactly the same in init.h.
> Since we don't want to include init.h (for size reasons) we move the
> struct to sis_main.h.
> Since SiS_DRAMType is const and only used by sisfb_post_300_rwtest which is
> marked __devinit we can also annotate SiS_DRAMType with __devinitconst.
> 
> And since hardcoded values are bad we use ARRAY_SIZE for determining the
> size of SiS_DRAMType ;)
> 
> v2:
> Move struct to sis_main.h to decrease module size

As far as I can see it should be possible to keep the array in
sis_main.c and just delete it in the header files, shouldn't it? I'd
prefer to do it this way.


Best regards,

Florian Tobias Schandinat

> 
> Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
> ---
>  drivers/video/sis/init.h     |   20 --------------------
>  drivers/video/sis/sis_main.c |   21 +--------------------
>  drivers/video/sis/sis_main.h |   20 ++++++++++++++++++++
>  3 files changed, 21 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h
> index a22e892..85d6738 100644
> --- a/drivers/video/sis/init.h
> +++ b/drivers/video/sis/init.h
> @@ -105,26 +105,6 @@ static const unsigned short ModeIndex_1920x1440[]    = {0x68, 0x69, 0x00, 0x6b};
>  static const unsigned short ModeIndex_300_2048x1536[]= {0x6c, 0x6d, 0x00, 0x00};
>  static const unsigned short ModeIndex_310_2048x1536[]= {0x6c, 0x6d, 0x00, 0x6e};
>  
> -static const unsigned short SiS_DRAMType[17][5]={
> -	{0x0C,0x0A,0x02,0x40,0x39},
> -	{0x0D,0x0A,0x01,0x40,0x48},
> -	{0x0C,0x09,0x02,0x20,0x35},
> -	{0x0D,0x09,0x01,0x20,0x44},
> -	{0x0C,0x08,0x02,0x10,0x31},
> -	{0x0D,0x08,0x01,0x10,0x40},
> -	{0x0C,0x0A,0x01,0x20,0x34},
> -	{0x0C,0x09,0x01,0x08,0x32},
> -	{0x0B,0x08,0x02,0x08,0x21},
> -	{0x0C,0x08,0x01,0x08,0x30},
> -	{0x0A,0x08,0x02,0x04,0x11},
> -	{0x0B,0x0A,0x01,0x10,0x28},
> -	{0x09,0x08,0x02,0x02,0x01},
> -	{0x0B,0x09,0x01,0x08,0x24},
> -	{0x0B,0x08,0x01,0x04,0x20},
> -	{0x0A,0x08,0x01,0x02,0x10},
> -	{0x09,0x08,0x01,0x01,0x00}
> -};
> -
>  static const unsigned char SiS_MDA_DAC[] >  {
>  	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
> diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
> index 078ca21..8abd42b 100644
> --- a/drivers/video/sis/sis_main.c
> +++ b/drivers/video/sis/sis_main.c
> @@ -4231,27 +4231,8 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth
>  	unsigned short sr14;
>  	unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid;
>  	unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage;
> -	static const unsigned short SiS_DRAMType[17][5] = {
> -		{0x0C,0x0A,0x02,0x40,0x39},
> -		{0x0D,0x0A,0x01,0x40,0x48},
> -		{0x0C,0x09,0x02,0x20,0x35},
> -		{0x0D,0x09,0x01,0x20,0x44},
> -		{0x0C,0x08,0x02,0x10,0x31},
> -		{0x0D,0x08,0x01,0x10,0x40},
> -		{0x0C,0x0A,0x01,0x20,0x34},
> -		{0x0C,0x09,0x01,0x08,0x32},
> -		{0x0B,0x08,0x02,0x08,0x21},
> -		{0x0C,0x08,0x01,0x08,0x30},
> -		{0x0A,0x08,0x02,0x04,0x11},
> -		{0x0B,0x0A,0x01,0x10,0x28},
> -		{0x09,0x08,0x02,0x02,0x01},
> -		{0x0B,0x09,0x01,0x08,0x24},
> -		{0x0B,0x08,0x01,0x04,0x20},
> -		{0x0A,0x08,0x01,0x02,0x10},
> -		{0x09,0x08,0x01,0x01,0x00}
> -	};
>  
> -	 for(k = 0; k <= 16; k++) {
> +	 for (k = 0; k < ARRAY_SIZE(SiS_DRAMType); k++) {
>  
>  		RankCapacity = buswidth * SiS_DRAMType[k][3];
>  
> diff --git a/drivers/video/sis/sis_main.h b/drivers/video/sis/sis_main.h
> index 9540e97..242c1c3 100644
> --- a/drivers/video/sis/sis_main.h
> +++ b/drivers/video/sis/sis_main.h
> @@ -776,6 +776,26 @@ extern void		SiS_Chrontel701xBLOff(struct SiS_Private *SiS_Pr);
>  #endif
>  extern void		SiS_SiS30xBLOn(struct SiS_Private *SiS_Pr);
>  extern void		SiS_SiS30xBLOff(struct SiS_Private *SiS_Pr);
> +
> +static const unsigned short __devinitconst SiS_DRAMType[17][5] = {
> +	{0x0C,0x0A,0x02,0x40,0x39},
> +	{0x0D,0x0A,0x01,0x40,0x48},
> +	{0x0C,0x09,0x02,0x20,0x35},
> +	{0x0D,0x09,0x01,0x20,0x44},
> +	{0x0C,0x08,0x02,0x10,0x31},
> +	{0x0D,0x08,0x01,0x10,0x40},
> +	{0x0C,0x0A,0x01,0x20,0x34},
> +	{0x0C,0x09,0x01,0x08,0x32},
> +	{0x0B,0x08,0x02,0x08,0x21},
> +	{0x0C,0x08,0x01,0x08,0x30},
> +	{0x0A,0x08,0x02,0x04,0x11},
> +	{0x0B,0x0A,0x01,0x10,0x28},
> +	{0x09,0x08,0x02,0x02,0x01},
> +	{0x0B,0x09,0x01,0x08,0x24},
> +	{0x0B,0x08,0x01,0x04,0x20},
> +	{0x0A,0x08,0x01,0x02,0x10},
> +	{0x09,0x08,0x01,0x01,0x00}
> +};
>  #endif
>  
>  


^ permalink raw reply

* Re: [PATCH v3] backlight: Add LMS501KF03 LCD panel driver
From: Andrew Morton @ 2012-04-30 21:30 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1335762762-20532-1-git-send-email-sachin.kamat@linaro.org>

On Mon, 30 Apr 2012 10:42:42 +0530
Sachin Kamat <sachin.kamat@linaro.org> wrote:

> LMS501KF03 is a 480x800 LCD module with brightness control.
> The driver uses 3-wired SPI inteface.
> 
>
> ...
>
> +struct lms501kf03 {
> +	struct device			*dev;
> +	struct spi_device		*spi;
> +	unsigned int			power;
> +	struct lcd_device		*ld;
> +	struct lcd_platform_data	*lcd_pd;
> +};
> +
> +const unsigned short seq_password[] = {

static

> +	0xb9, 0xff, 0x83, 0x69,
> +	ENDDEF
> +};
> +
> +const unsigned short seq_power[] = {

static

> +	0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
> +	0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
> +	ENDDEF
> +};
> +
> +const unsigned short seq_display[] = {

static

> +	0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
> +	0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
> +	ENDDEF
> +};
> +
> +const unsigned short seq_rgb_if[] = {

etcetera.

> +	0xb3, 0x09,
> +	ENDDEF
> +};
> +
>
> ...
>
> +static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd,
> +	const unsigned short *wbuf)
> +{
> +	int ret = 0, i = 0;
> +
> +	while (wbuf[i] != ENDDEF) {
> +		if (i = 0)
> +			ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]);
> +		else
> +			ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]);
> +		if (ret)
> +			break;
> +
> +		udelay(100);

That's a pretty long delay.

A problem with mdelay(), udelay() etc is that it is very hard for the
reader to work out why the delay is there, and why the particular
duration was chosen.  hence code comments are useful here.


> +		i += 1;
> +	}
> +	return ret;
> +}
> +
> +static int lms501kf03_ldi_init(struct lms501kf03 *lcd)
> +{
> +	int ret, i;
> +	const unsigned short *init_seq[] = {
> +		seq_password,
> +		seq_power,
> +		seq_display,
> +		seq_rgb_if,
> +		seq_display_inv,
> +		seq_vcom,
> +		seq_gate,
> +		seq_panel,
> +		seq_col_mod,
> +		seq_w_gamma,
> +		seq_rgb_gamma,
> +		seq_sleep_out,
> +	};

Can that be made static?  If so, you'll probably find that the code
gets smaller and faster.

> +	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
> +		ret = lms501kf03_panel_send_sequence(lcd, init_seq[i]);
> +		if (ret)
> +			break;
> +	}
> +	mdelay(120);

And that's a huuuuuuuge delay!  Definitely needs a comment describeing
why we need to do such an awkward thing.

> +
> +	return ret;
> +}
> +
>
> ...
>
> +static int lms501kf03_power_is_on(int power)
> +{
> +	return ((power) <= FB_BLANK_NORMAL);

Unneeded parentheses.

> +}
> +
>
> ...
>
> +
> +static int lms501kf03_power_off(struct lms501kf03 *lcd)
> +{
> +	int ret = 0;
> +	struct lcd_platform_data *pd = NULL;
> +
> +	pd = lcd->lcd_pd;
> +	if (!pd) {

I assume this can't happen?

> +		dev_err(lcd->dev, "platform data is NULL\n");
> +		return -EFAULT;
> +	}
> +
> +	ret = lms501kf03_ldi_disable(lcd);
> +	if (ret) {
> +		dev_err(lcd->dev, "lcd setting failed.\n");
> +		return -EIO;
> +	}
> +
> +	mdelay(pd->power_off_delay);
> +
> +	if (!pd->power_on) {

And this can't happen either?

> +		dev_err(lcd->dev, "power_on is NULL.\n");
> +		return -EFAULT;
> +	} else {
> +		pd->power_on(lcd->ld, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int lms501kf03_power(struct lms501kf03 *lcd, int power)
> +{
> +	int ret = 0;
> +
> +	if (lms501kf03_power_is_on(power) &&
> +			!lms501kf03_power_is_on(lcd->power))
> +		ret = lms501kf03_power_on(lcd);
> +	else if (!lms501kf03_power_is_on(power) &&
> +			lms501kf03_power_is_on(lcd->power))
> +		ret = lms501kf03_power_off(lcd);
> +
> +	if (!ret)
> +		lcd->power = power;
> +
> +	return ret;
> +}

This function needs a comment - what does it do and how does it
interpret its argument?

> +static int lms501kf03_get_power(struct lcd_device *ld)
> +{
> +	struct lms501kf03 *lcd = lcd_get_data(ld);
> +
> +	return lcd->power;
> +}
> +
>
> ...
>
> +#if defined(CONFIG_PM)
> +unsigned int before_power;

static.

Also, this restricts the driver to servicing a single device.  That's
probably not a problem in the real world but it's still a bit
regrettable.  Can't this be stored in `struct lms501kf03'?

>
> ...
>
> +static int lms501kf03_resume(struct spi_device *spi)
> +{
> +	int ret = 0;
> +	struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
> +
> +	/*
> +	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK

"After suspend".

> +	 * (at that time, before_power is FB_BLANK_UNBLANK) then
> +	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
> +	 */
> +	if (before_power = FB_BLANK_UNBLANK)
> +		lcd->power = FB_BLANK_POWERDOWN;

This lokos a bit odd.  Is it a workaround for some hardware bug? 
What's actually going on here and why don't other drivers need to do
this? (IOW: needs a better comment!)

> +	dev_dbg(&spi->dev, "before_power = %d\n", before_power);
> +
> +	ret = lms501kf03_power(lcd, before_power);
> +
> +	return ret;
> +}
>
> ...
>


^ permalink raw reply

* [PATCH v3] backlight: Add LMS501KF03 LCD panel driver
From: Sachin Kamat @ 2012-04-30  5:24 UTC (permalink / raw)
  To: linux-fbdev

LMS501KF03 is a 480x800 LCD module with brightness control.
The driver uses 3-wired SPI inteface.

Signed-off-by: Ilho Lee <Ilho215.lee@samsung.com>
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
---
Changes since v2:
Incorporated Jingoo Han's <jg1.han@samsung.com> review comments.
Deleted backlight control functions as PWM backlight driver
will be used to control it.

Changes since v1:
Incorporated review comments from Florian Tobias Schandinat
<FlorianSchandinat@gmx.de> - Simplied lms501kf03_ldi_enable and
lms501kf03_ldi_disable function implementations.
---
 drivers/video/backlight/Kconfig      |    8 +
 drivers/video/backlight/Makefile     |    1 +
 drivers/video/backlight/lms501kf03.c |  482 ++++++++++++++++++++++++++++++++++
 3 files changed, 491 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/backlight/lms501kf03.c

diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index af16884..f15d6d9 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -125,6 +125,14 @@ config LCD_AMS369FG06
 	  If you have an AMS369FG06 AMOLED Panel, say Y to enable its
 	  LCD control driver.
 
+config LCD_LMS501KF03
+	tristate "LMS501KF03 AMOLED LCD Driver"
+	depends on SPI_GPIO && BACKLIGHT_CLASS_DEVICE
+	default n
+	help
+	  If you have an 5.01" LMS501KF03 AMOLED Panel, say Y to enable its
+	  LCD control driver.
+
 endif # LCD_CLASS_DEVICE
 
 #
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 36855ae..1b1e62a 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_LCD_TOSA)		   += tosa_lcd.o
 obj-$(CONFIG_LCD_S6E63M0)	+= s6e63m0.o
 obj-$(CONFIG_LCD_LD9040)	+= ld9040.o
 obj-$(CONFIG_LCD_AMS369FG06)	+= ams369fg06.o
+obj-$(CONFIG_LCD_LMS501KF03)	+= lms501kf03.o
 
 obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
 obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o
diff --git a/drivers/video/backlight/lms501kf03.c b/drivers/video/backlight/lms501kf03.c
new file mode 100644
index 0000000..adcdb9e
--- /dev/null
+++ b/drivers/video/backlight/lms501kf03.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * LMS501KF03 5.01" LCD module driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+
+#define ENDDEF			0xFF00
+#define COMMAND_ONLY		0x00
+#define DATA_ONLY		0x01
+
+#define MIN_BRIGHTNESS		0
+#define MAX_BRIGHTNESS		255
+#define DEFAULT_BRIGHTNESS	150
+
+struct lms501kf03 {
+	struct device			*dev;
+	struct spi_device		*spi;
+	unsigned int			power;
+	struct lcd_device		*ld;
+	struct lcd_platform_data	*lcd_pd;
+};
+
+const unsigned short seq_password[] = {
+	0xb9, 0xff, 0x83, 0x69,
+	ENDDEF
+};
+
+const unsigned short seq_power[] = {
+	0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
+	0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+	ENDDEF
+};
+
+const unsigned short seq_display[] = {
+	0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
+	0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
+	ENDDEF
+};
+
+const unsigned short seq_rgb_if[] = {
+	0xb3, 0x09,
+	ENDDEF
+};
+
+const unsigned short seq_display_inv[] = {
+	0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06,
+	ENDDEF
+};
+
+const unsigned short seq_vcom[] = {
+	0xb6, 0x4c, 0x2e,
+	ENDDEF
+};
+
+const unsigned short seq_gate[] = {
+	0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13,
+	0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75,
+	0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07,
+	ENDDEF
+};
+
+const unsigned short seq_panel[] = {
+	0xcc, 0x02,
+	ENDDEF
+};
+
+const unsigned short seq_col_mod[] = {
+	0x3a, 0x77,
+	ENDDEF
+};
+
+const unsigned short seq_w_gamma[] = {
+	0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a,
+	0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04,
+	0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16,
+	0x18, 0x16, 0x17, 0x0d, 0x15,
+	ENDDEF
+};
+
+const unsigned short seq_rgb_gamma[] = {
+	0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c,
+	0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92,
+	0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde,
+	0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb,
+	0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a,
+	0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b,
+	0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca,
+	0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe,
+	0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17,
+	0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63,
+	0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0,
+	0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	ENDDEF
+};
+
+const unsigned short seq_up_dn[] = {
+	0x36, 0x10,
+	ENDDEF
+};
+
+const unsigned short seq_sleep_in[] = {
+	0x10,
+	ENDDEF
+};
+
+const unsigned short seq_sleep_out[] = {
+	0x11,
+	ENDDEF
+};
+
+const unsigned short seq_display_on[] = {
+	0x29,
+	ENDDEF
+};
+
+const unsigned short seq_display_off[] = {
+	0x10,
+	ENDDEF
+};
+
+static int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data)
+{
+	u16 buf[1];
+	struct spi_message msg;
+
+	struct spi_transfer xfer = {
+		.len	= 2,
+		.tx_buf	= buf,
+	};
+
+	buf[0] = (addr << 8) | data;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	return spi_sync(lcd->spi, &msg);
+}
+
+static int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address,
+	unsigned char command)
+{
+	int ret = 0;
+
+	ret = lms501kf03_spi_write_byte(lcd, address, command);
+
+	return ret;
+}
+
+static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd,
+	const unsigned short *wbuf)
+{
+	int ret = 0, i = 0;
+
+	while (wbuf[i] != ENDDEF) {
+		if (i = 0)
+			ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]);
+		else
+			ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]);
+		if (ret)
+			break;
+
+		udelay(100);
+		i += 1;
+	}
+	return ret;
+}
+
+static int lms501kf03_ldi_init(struct lms501kf03 *lcd)
+{
+	int ret, i;
+	const unsigned short *init_seq[] = {
+		seq_password,
+		seq_power,
+		seq_display,
+		seq_rgb_if,
+		seq_display_inv,
+		seq_vcom,
+		seq_gate,
+		seq_panel,
+		seq_col_mod,
+		seq_w_gamma,
+		seq_rgb_gamma,
+		seq_sleep_out,
+	};
+
+	for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
+		ret = lms501kf03_panel_send_sequence(lcd, init_seq[i]);
+		if (ret)
+			break;
+	}
+	mdelay(120);
+
+	return ret;
+}
+
+static int lms501kf03_ldi_enable(struct lms501kf03 *lcd)
+{
+	return lms501kf03_panel_send_sequence(lcd, seq_display_on);
+}
+
+static int lms501kf03_ldi_disable(struct lms501kf03 *lcd)
+{
+	return lms501kf03_panel_send_sequence(lcd, seq_display_off);
+}
+
+static int lms501kf03_power_is_on(int power)
+{
+	return ((power) <= FB_BLANK_NORMAL);
+}
+
+static int lms501kf03_power_on(struct lms501kf03 *lcd)
+{
+	int ret = 0;
+	struct lcd_platform_data *pd = NULL;
+
+	pd = lcd->lcd_pd;
+	if (!pd) {
+		dev_err(lcd->dev, "platform data is NULL.\n");
+		return -EFAULT;
+	}
+
+	if (!pd->power_on) {
+		dev_err(lcd->dev, "power_on is NULL.\n");
+		return -EFAULT;
+	} else {
+		pd->power_on(lcd->ld, 1);
+		mdelay(pd->power_on_delay);
+	}
+
+	if (!pd->reset) {
+		dev_err(lcd->dev, "reset is NULL.\n");
+		return -EFAULT;
+	} else {
+		pd->reset(lcd->ld);
+		mdelay(pd->reset_delay);
+	}
+
+	ret = lms501kf03_ldi_init(lcd);
+	if (ret) {
+		dev_err(lcd->dev, "failed to initialize ldi.\n");
+		return ret;
+	}
+
+	ret = lms501kf03_ldi_enable(lcd);
+	if (ret) {
+		dev_err(lcd->dev, "failed to enable ldi.\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int lms501kf03_power_off(struct lms501kf03 *lcd)
+{
+	int ret = 0;
+	struct lcd_platform_data *pd = NULL;
+
+	pd = lcd->lcd_pd;
+	if (!pd) {
+		dev_err(lcd->dev, "platform data is NULL\n");
+		return -EFAULT;
+	}
+
+	ret = lms501kf03_ldi_disable(lcd);
+	if (ret) {
+		dev_err(lcd->dev, "lcd setting failed.\n");
+		return -EIO;
+	}
+
+	mdelay(pd->power_off_delay);
+
+	if (!pd->power_on) {
+		dev_err(lcd->dev, "power_on is NULL.\n");
+		return -EFAULT;
+	} else {
+		pd->power_on(lcd->ld, 0);
+	}
+
+	return 0;
+}
+
+static int lms501kf03_power(struct lms501kf03 *lcd, int power)
+{
+	int ret = 0;
+
+	if (lms501kf03_power_is_on(power) &&
+			!lms501kf03_power_is_on(lcd->power))
+		ret = lms501kf03_power_on(lcd);
+	else if (!lms501kf03_power_is_on(power) &&
+			lms501kf03_power_is_on(lcd->power))
+		ret = lms501kf03_power_off(lcd);
+
+	if (!ret)
+		lcd->power = power;
+
+	return ret;
+}
+
+static int lms501kf03_get_power(struct lcd_device *ld)
+{
+	struct lms501kf03 *lcd = lcd_get_data(ld);
+
+	return lcd->power;
+}
+
+static int lms501kf03_set_power(struct lcd_device *ld, int power)
+{
+	struct lms501kf03 *lcd = lcd_get_data(ld);
+
+	if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
+		power != FB_BLANK_NORMAL) {
+		dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
+		return -EINVAL;
+	}
+
+	return lms501kf03_power(lcd, power);
+}
+
+static struct lcd_ops lms501kf03_lcd_ops = {
+	.get_power = lms501kf03_get_power,
+	.set_power = lms501kf03_set_power,
+};
+
+static int __devinit lms501kf03_probe(struct spi_device *spi)
+{
+	struct lms501kf03 *lcd = NULL;
+	struct lcd_device *ld = NULL;
+	int ret = 0;
+
+	lcd = kzalloc(sizeof(struct lms501kf03), GFP_KERNEL);
+	if (!lcd)
+		return -ENOMEM;
+
+	/* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */
+	spi->bits_per_word = 9;
+
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "spi setup failed.\n");
+		goto out_free_lcd;
+	}
+
+	lcd->spi = spi;
+	lcd->dev = &spi->dev;
+
+	lcd->lcd_pd = spi->dev.platform_data;
+	if (!lcd->lcd_pd) {
+		dev_err(&spi->dev, "platform data is NULL\n");
+		goto out_free_lcd;
+	}
+
+	ld = lcd_device_register("lms501kf03", &spi->dev, lcd,
+		&lms501kf03_lcd_ops);
+	if (IS_ERR(ld)) {
+		ret = PTR_ERR(ld);
+		goto out_free_lcd;
+	}
+
+	lcd->ld = ld;
+
+	if (!lcd->lcd_pd->lcd_enabled) {
+		/*
+		 * if lcd panel was off from bootloader then
+		 * current lcd status is powerdown and then
+		 * it enables lcd panel.
+		 */
+		lcd->power = FB_BLANK_POWERDOWN;
+
+		lms501kf03_power(lcd, FB_BLANK_UNBLANK);
+	} else {
+		lcd->power = FB_BLANK_UNBLANK;
+	}
+
+	dev_set_drvdata(&spi->dev, lcd);
+
+	dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n");
+
+	return 0;
+
+	lcd_device_unregister(ld);
+out_free_lcd:
+	kfree(lcd);
+	return ret;
+}
+
+static int __devexit lms501kf03_remove(struct spi_device *spi)
+{
+	struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
+
+	lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
+	lcd_device_unregister(lcd->ld);
+	kfree(lcd);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM)
+unsigned int before_power;
+
+static int lms501kf03_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+	int ret = 0;
+	struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
+
+	dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
+
+	before_power = lcd->power;
+
+	/*
+	 * when lcd panel is suspend, lcd panel becomes off
+	 * regardless of status.
+	 */
+	ret = lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
+
+	return ret;
+}
+
+static int lms501kf03_resume(struct spi_device *spi)
+{
+	int ret = 0;
+	struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
+
+	/*
+	 * after suspended, if lcd panel status is FB_BLANK_UNBLANK
+	 * (at that time, before_power is FB_BLANK_UNBLANK) then
+	 * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
+	 */
+	if (before_power = FB_BLANK_UNBLANK)
+		lcd->power = FB_BLANK_POWERDOWN;
+
+	dev_dbg(&spi->dev, "before_power = %d\n", before_power);
+
+	ret = lms501kf03_power(lcd, before_power);
+
+	return ret;
+}
+#else
+#define lms501kf03_suspend	NULL
+#define lms501kf03_resume	NULL
+#endif
+
+void lms501kf03_shutdown(struct spi_device *spi)
+{
+	struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
+
+	lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+static struct spi_driver lms501kf03_driver = {
+	.driver = {
+		.name	= "lms501kf03",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= lms501kf03_probe,
+	.remove		= __devexit_p(lms501kf03_remove),
+	.shutdown	= lms501kf03_shutdown,
+	.suspend	= lms501kf03_suspend,
+	.resume		= lms501kf03_resume,
+};
+
+module_spi_driver(lms501kf03_driver);
+
+MODULE_AUTHOR("Ilho Lee <Ilho215.lee@samsung.com>");
+MODULE_AUTHOR("Sachin Kamat <sachin.kamat@samsung.com>");
+MODULE_DESCRIPTION("LMS501KF03 LCD Driver");
+MODULE_LICENSE("GPL");
-- 
1.7.4.1


^ permalink raw reply related

* Re: [PATCH v2] backlight: Add LMS501KF03 LCD panel driver
From: Sachin Kamat @ 2012-04-30  5:20 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1334835208-32055-1-git-send-email-sachin.kamat@linaro.org>

On 30/04/2012, Jingoo Han <jg1.han@samsung.com> wrote:
> On 30 April 2012 13:42, Sachin Kamat <sachin.kamat@linaro.org> wrote:
>> On 30/04/2012, Jingoo Han <jg1.han@samsung.com> wrote:
>> > On 28 April 2012 11:02, Sachin Kamat <sachin.kamat@linaro.org> wrote:
>> >> Any comments on the revised patch?
>> >>
>> >> On 19 April 2012 17:03, Sachin Kamat <sachin.kamat@linaro.org> wrote:
>> >> > LMS501KF03 is a 480x800 LCD module with brightness control.
>> >> > The driver uses 3-wired SPI inteface.
>> >> >
>> <snip>
>>
>> >> > +
>> >> > +#define POWER_IS_ON(power)     ((power) <= FB_BLANK_NORMAL)
>> >
>> > This could have been implemented as a regular lower-case C function.
>> > They're better than macros.
>> >
>> > Please refer to ams369fg06 amoled panel driver.
>>
>> OK. Will modify.
>>
>> >
>>
>> <snip>
>> >> > +
>> >> > +const unsigned short SEQ_DISPLAY_OFF[] = {
>> >> > +       0x10,
>> >> > +       ENDDEF
>> >> > +};
>> >> > +
>> >
>> >
>> > It's not irregular that these symbols are all-uppercase.
>> > How about replacing them with lower-case?
>> > ex) SEQ_xxx -> seq_xxx
>>
>> OK. Will modify.
>>
>> >
>> >
>> <snip>
>>
>> >> > +static int lms501kf03_set_brightness(struct backlight_device *bd)
>> >> > +{
>> >> > +       int ret = 0;
>> >> > +       int brightness = bd->props.brightness;
>> >> > +
>> >> > +       if (brightness < MIN_BRIGHTNESS ||
>> >> > +               brightness > bd->props.max_brightness) {
>> >> > +               dev_err(&bd->dev, "lcd brightness should be %d to
>> >> > %d.\n",
>> >> > +                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
>> >> > +               return -EINVAL;
>> >> > +       }
>> >> > +
>> >> > +       return ret;
>> >> > +}
>> >> > +
>> >
>> > Are these backlight control functions really necessary?
>> > As I am concerned, this LMS501KF03 uses PWM backlight as backlight
>> > control.
>> > If PWM backlight driver is used, these backlight control functions are
>> > superfluous.
>>
>> Yes, you are right.  If PWM driver is used for backlight control, then
>> these functions are not necessary. However for the sake of
>> completeness of this driver, I have added them so that they could be
>> used to control backlight in cases where PWM is not used. Please let
>> me know your opinion.
>
>
> In my opinion, LMS501KF03 can have nothing but PWM backlight as a backlight
> control.
> As can be seen at datasheet of LMS501KF03 LCD panel, LMS501KF03 LCD panel is
> designed to use PWM backlight.
>
> So, if PWM is not available, backlight framework is not necessary
> because it is not possible to control backlight without PWM.
> Also, only LCD framework can be used for LMS501KF03 LCD panel in this case.
>
> To sum up, please remove backlight control functions above.

OK. Thanks.
Will send the updated patch shortly.

>
>
>>
>> >
>> >> > +static struct lcd_ops lms501kf03_lcd_ops = {
>> <snip>
>>
>> >> > +
>> >> > +       lcd->lcd_pd = (struct lcd_platform_data
>> >> > *)spi->dev.platform_data;
>> >
>> > This is an unnecessary cast of void*.
>>
>> OK. Will remove it.
>>
>> >
>> >
>>
>> Thank you for reviewing the patch and providing your comments. I will
>> modify and send the updated patch after I get your opinion about
>> backlight control.
>>
>>
>> --
>> With warm regards,
>> Sachin
>
>


-- 
With warm regards,
Sachin

^ permalink raw reply

* RE: [PATCH v2] backlight: Add LMS501KF03 LCD panel driver
From: Jingoo Han @ 2012-04-30  5:04 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1334835208-32055-1-git-send-email-sachin.kamat@linaro.org>

On 30 April 2012 13:42, Sachin Kamat <sachin.kamat@linaro.org> wrote:
> On 30/04/2012, Jingoo Han <jg1.han@samsung.com> wrote:
> > On 28 April 2012 11:02, Sachin Kamat <sachin.kamat@linaro.org> wrote:
> >> Any comments on the revised patch?
> >>
> >> On 19 April 2012 17:03, Sachin Kamat <sachin.kamat@linaro.org> wrote:
> >> > LMS501KF03 is a 480x800 LCD module with brightness control.
> >> > The driver uses 3-wired SPI inteface.
> >> >
> <snip>
> 
> >> > +
> >> > +#define POWER_IS_ON(power)     ((power) <= FB_BLANK_NORMAL)
> >
> > This could have been implemented as a regular lower-case C function.
> > They're better than macros.
> >
> > Please refer to ams369fg06 amoled panel driver.
> 
> OK. Will modify.
> 
> >
> 
> <snip>
> >> > +
> >> > +const unsigned short SEQ_DISPLAY_OFF[] = {
> >> > +       0x10,
> >> > +       ENDDEF
> >> > +};
> >> > +
> >
> >
> > It's not irregular that these symbols are all-uppercase.
> > How about replacing them with lower-case?
> > ex) SEQ_xxx -> seq_xxx
> 
> OK. Will modify.
> 
> >
> >
> <snip>
> 
> >> > +static int lms501kf03_set_brightness(struct backlight_device *bd)
> >> > +{
> >> > +       int ret = 0;
> >> > +       int brightness = bd->props.brightness;
> >> > +
> >> > +       if (brightness < MIN_BRIGHTNESS ||
> >> > +               brightness > bd->props.max_brightness) {
> >> > +               dev_err(&bd->dev, "lcd brightness should be %d to
> >> > %d.\n",
> >> > +                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
> >> > +               return -EINVAL;
> >> > +       }
> >> > +
> >> > +       return ret;
> >> > +}
> >> > +
> >
> > Are these backlight control functions really necessary?
> > As I am concerned, this LMS501KF03 uses PWM backlight as backlight control.
> > If PWM backlight driver is used, these backlight control functions are
> > superfluous.
> 
> Yes, you are right.  If PWM driver is used for backlight control, then
> these functions are not necessary. However for the sake of
> completeness of this driver, I have added them so that they could be
> used to control backlight in cases where PWM is not used. Please let
> me know your opinion.


In my opinion, LMS501KF03 can have nothing but PWM backlight as a backlight control.
As can be seen at datasheet of LMS501KF03 LCD panel, LMS501KF03 LCD panel is designed to use PWM backlight.

So, if PWM is not available, backlight framework is not necessary
because it is not possible to control backlight without PWM.
Also, only LCD framework can be used for LMS501KF03 LCD panel in this case.

To sum up, please remove backlight control functions above.


> 
> >
> >> > +static struct lcd_ops lms501kf03_lcd_ops = {
> <snip>
> 
> >> > +
> >> > +       lcd->lcd_pd = (struct lcd_platform_data
> >> > *)spi->dev.platform_data;
> >
> > This is an unnecessary cast of void*.
> 
> OK. Will remove it.
> 
> >
> >
> 
> Thank you for reviewing the patch and providing your comments. I will
> modify and send the updated patch after I get your opinion about
> backlight control.
> 
> 
> --
> With warm regards,
> Sachin


^ permalink raw reply

* Re: [PATCH v2] backlight: Add LMS501KF03 LCD panel driver
From: Sachin Kamat @ 2012-04-30  4:54 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1334835208-32055-1-git-send-email-sachin.kamat@linaro.org>

On 30/04/2012, Jingoo Han <jg1.han@samsung.com> wrote:
> On 28 April 2012 11:02, Sachin Kamat <sachin.kamat@linaro.org> wrote:
>> Any comments on the revised patch?
>>
>> On 19 April 2012 17:03, Sachin Kamat <sachin.kamat@linaro.org> wrote:
>> > LMS501KF03 is a 480x800 LCD module with brightness control.
>> > The driver uses 3-wired SPI inteface.
>> >
<snip>

>> > +
>> > +#define POWER_IS_ON(power)     ((power) <= FB_BLANK_NORMAL)
>
> This could have been implemented as a regular lower-case C function.
> They're better than macros.
>
> Please refer to ams369fg06 amoled panel driver.

OK. Will modify.

>

<snip>
>> > +
>> > +const unsigned short SEQ_DISPLAY_OFF[] = {
>> > +       0x10,
>> > +       ENDDEF
>> > +};
>> > +
>
>
> It's not irregular that these symbols are all-uppercase.
> How about replacing them with lower-case?
> ex) SEQ_xxx -> seq_xxx

OK. Will modify.

>
>
<snip>

>> > +static int lms501kf03_set_brightness(struct backlight_device *bd)
>> > +{
>> > +       int ret = 0;
>> > +       int brightness = bd->props.brightness;
>> > +
>> > +       if (brightness < MIN_BRIGHTNESS ||
>> > +               brightness > bd->props.max_brightness) {
>> > +               dev_err(&bd->dev, "lcd brightness should be %d to
>> > %d.\n",
>> > +                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
>> > +               return -EINVAL;
>> > +       }
>> > +
>> > +       return ret;
>> > +}
>> > +
>
> Are these backlight control functions really necessary?
> As I am concerned, this LMS501KF03 uses PWM backlight as backlight control.
> If PWM backlight driver is used, these backlight control functions are
> superfluous.

Yes, you are right.  If PWM driver is used for backlight control, then
these functions are not necessary. However for the sake of
completeness of this driver, I have added them so that they could be
used to control backlight in cases where PWM is not used. Please let
me know your opinion.

>
>> > +static struct lcd_ops lms501kf03_lcd_ops = {
<snip>

>> > +
>> > +       lcd->lcd_pd = (struct lcd_platform_data
>> > *)spi->dev.platform_data;
>
> This is an unnecessary cast of void*.

OK. Will remove it.

>
>

Thank you for reviewing the patch and providing your comments. I will
modify and send the updated patch after I get your opinion about
backlight control.


-- 
With warm regards,
Sachin

^ permalink raw reply

* Re: [PATCH v2] backlight: Add LMS501KF03 LCD panel driver
From: Jingoo Han @ 2012-04-30  0:00 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1334835208-32055-1-git-send-email-sachin.kamat@linaro.org>

On 28 April 2012 11:02, Sachin Kamat <sachin.kamat@linaro.org> wrote:
> Any comments on the revised patch?
> 
> On 19 April 2012 17:03, Sachin Kamat <sachin.kamat@linaro.org> wrote:
> > LMS501KF03 is a 480x800 LCD module with brightness control.
> > The driver uses 3-wired SPI inteface.
> >
> > Signed-off-by: Ilho Lee <Ilho215.lee@samsung.com>
> > Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
> > ---
> > Changes since v1:
> > Incorporated review comments from Florian Tobias Schandinat
> > <FlorianSchandinat@gmx.de> - Simplied lms501kf03_ldi_enable and
> > lms501kf03_ldi_disable function implementations.
> > ---
> >  drivers/video/backlight/Kconfig      |    8 +
> >  drivers/video/backlight/Makefile     |    1 +
> >  drivers/video/backlight/lms501kf03.c |  525 ++++++++++++++++++++++++++++++++++
> >  3 files changed, 534 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/video/backlight/lms501kf03.c
> >
> > diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
> > index af16884..e216f77 100644
> > --- a/drivers/video/backlight/Kconfig
> > +++ b/drivers/video/backlight/Kconfig
> > @@ -125,6 +125,14 @@ config LCD_AMS369FG06
> >          If you have an AMS369FG06 AMOLED Panel, say Y to enable its
> >          LCD control driver.
> >
> > +config LCD_LMS501KF03
> > +       tristate "LMS501KF03 AMOLED LCD Driver"
> > +       depends on SPI_GPIO && BACKLIGHT_CLASS_DEVICE
> > +       default n
> > +       help
> > +         If you have an 5.01" LMS501KF03 AMOLED Panel, say Y to enable its
> > +         LCD control driver.
> > +
> >  endif # LCD_CLASS_DEVICE
> >
> >  #
> > diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
> > index 36855ae..1b1e62a 100644
> > --- a/drivers/video/backlight/Makefile
> > +++ b/drivers/video/backlight/Makefile
> > @@ -14,6 +14,7 @@ obj-$(CONFIG_LCD_TOSA)                   += tosa_lcd.o
> >  obj-$(CONFIG_LCD_S6E63M0)      += s6e63m0.o
> >  obj-$(CONFIG_LCD_LD9040)       += ld9040.o
> >  obj-$(CONFIG_LCD_AMS369FG06)   += ams369fg06.o
> > +obj-$(CONFIG_LCD_LMS501KF03)   += lms501kf03.o
> >
> >  obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
> >  obj-$(CONFIG_BACKLIGHT_ATMEL_PWM)    += atmel-pwm-bl.o
> > diff --git a/drivers/video/backlight/lms501kf03.c b/drivers/video/backlight/lms501kf03.c
> > new file mode 100644
> > index 0000000..af25532
> > --- /dev/null
> > +++ b/drivers/video/backlight/lms501kf03.c
> > @@ -0,0 +1,525 @@
> > +/*
> > + * Copyright (c) 2012 Samsung Electronics Co., Ltd.
> > + *             http://www.samsung.com/
> > + *
> > + * LMS501KF03 5.01" LCD module driver
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > +*/
> > +
> > +#include <linux/backlight.h>
> > +#include <linux/delay.h>
> > +#include <linux/fb.h>
> > +#include <linux/gpio.h>
> > +#include <linux/lcd.h>
> > +#include <linux/module.h>
> > +#include <linux/spi/spi.h>
> > +#include <linux/wait.h>
> > +
> > +#define ENDDEF                 0xFF00
> > +#define COMMAND_ONLY           0x00
> > +#define DATA_ONLY              0x01
> > +
> > +#define MIN_BRIGHTNESS         0
> > +#define MAX_BRIGHTNESS         255
> > +#define DEFAULT_BRIGHTNESS     150
> > +
> > +#define POWER_IS_ON(power)     ((power) <= FB_BLANK_NORMAL)

This could have been implemented as a regular lower-case C function. 
They're better than macros.

Please refer to ams369fg06 amoled panel driver.

> > +
> > +struct lms501kf03 {
> > +       struct device                   *dev;
> > +       struct spi_device               *spi;
> > +       unsigned int                    power;
> > +       struct lcd_device               *ld;
> > +       struct backlight_device         *bd;
> > +       struct lcd_platform_data        *lcd_pd;
> > +};
> > +
> > +const unsigned short SEQ_PASSWORD[] = {
> > +       0xb9, 0xff, 0x83, 0x69,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_POWER[] = {
> > +       0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
> > +       0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_DISPLAY[] = {
> > +       0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
> > +       0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_RGB_IF[] = {
> > +       0xb3, 0x09,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_DISPLAY_INV[] = {
> > +       0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_VCOM[] = {
> > +       0xb6, 0x4c, 0x2e,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_GATE[] = {
> > +       0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13,
> > +       0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75,
> > +       0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_PANEL[] = {
> > +       0xcc, 0x02,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_COL_MOD[] = {
> > +       0x3a, 0x77,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_W_GAMMA[] = {
> > +       0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a,
> > +       0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04,
> > +       0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16,
> > +       0x18, 0x16, 0x17, 0x0d, 0x15,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_RGB_GAMMA[] = {
> > +       0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c,
> > +       0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92,
> > +       0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde,
> > +       0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb,
> > +       0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a,
> > +       0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b,
> > +       0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca,
> > +       0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe,
> > +       0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17,
> > +       0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63,
> > +       0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0,
> > +       0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00,
> > +       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_UP_DN[] = {
> > +       0x36, 0x10,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_SLEEP_IN[] = {
> > +       0x10,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_SLEEP_OUT[] = {
> > +       0x11,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_DISPLAY_ON[] = {
> > +       0x29,
> > +       ENDDEF
> > +};
> > +
> > +const unsigned short SEQ_DISPLAY_OFF[] = {
> > +       0x10,
> > +       ENDDEF
> > +};
> > +


It's not irregular that these symbols are all-uppercase.
How about replacing them with lower-case?
ex) SEQ_xxx -> seq_xxx


> > +static int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data)
> > +{
> > +       u16 buf[1];
> > +       struct spi_message msg;
> > +
> > +       struct spi_transfer xfer = {
> > +               .len    = 2,
> > +               .tx_buf = buf,
> > +       };
> > +
> > +       buf[0] = (addr << 8) | data;
> > +
> > +       spi_message_init(&msg);
> > +       spi_message_add_tail(&xfer, &msg);
> > +
> > +       return spi_sync(lcd->spi, &msg);
> > +}
> > +
> > +static int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address,
> > +       unsigned char command)
> > +{
> > +       int ret = 0;
> > +
> > +       ret = lms501kf03_spi_write_byte(lcd, address, command);
> > +
> > +       return ret;
> > +}
> > +
> > +static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd,
> > +       const unsigned short *wbuf)
> > +{
> > +       int ret = 0, i = 0;
> > +
> > +       while (wbuf[i] != ENDDEF) {
> > +               if (i = 0)
> > +                       ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]);
> > +               else
> > +                       ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]);
> > +               if (ret)
> > +                       break;
> > +
> > +               udelay(100);
> > +               i += 1;
> > +       }
> > +       return ret;
> > +}
> > +
> > +static int lms501kf03_ldi_init(struct lms501kf03 *lcd)
> > +{
> > +       int ret, i;
> > +       const unsigned short *init_seq[] = {
> > +               SEQ_PASSWORD,
> > +               SEQ_POWER,
> > +               SEQ_DISPLAY,
> > +               SEQ_RGB_IF,
> > +               SEQ_DISPLAY_INV,
> > +               SEQ_VCOM,
> > +               SEQ_GATE,
> > +               SEQ_PANEL,
> > +               SEQ_COL_MOD,
> > +               SEQ_W_GAMMA,
> > +               SEQ_RGB_GAMMA,
> > +               SEQ_SLEEP_OUT,
> > +       };
> > +
> > +       for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
> > +               ret = lms501kf03_panel_send_sequence(lcd, init_seq[i]);
> > +               if (ret)
> > +                       break;
> > +       }
> > +       mdelay(120);
> > +
> > +       return ret;
> > +}
> > +
> > +static int lms501kf03_ldi_enable(struct lms501kf03 *lcd)
> > +{
> > +       return lms501kf03_panel_send_sequence(lcd, SEQ_DISPLAY_ON);
> > +}
> > +
> > +static int lms501kf03_ldi_disable(struct lms501kf03 *lcd)
> > +{
> > +       return lms501kf03_panel_send_sequence(lcd, SEQ_DISPLAY_OFF);
> > +}
> > +
> > +static int lms501kf03_power_on(struct lms501kf03 *lcd)
> > +{
> > +       int ret = 0;
> > +       struct lcd_platform_data *pd = NULL;
> > +       struct backlight_device *bd = NULL;
> > +
> > +       pd = lcd->lcd_pd;
> > +       if (!pd) {
> > +               dev_err(lcd->dev, "platform data is NULL.\n");
> > +               return -EFAULT;
> > +       }
> > +
> > +       bd = lcd->bd;
> > +       if (!bd) {
> > +               dev_err(lcd->dev, "backlight device is NULL.\n");
> > +               return -EFAULT;
> > +       }
> > +
> > +       if (!pd->power_on) {
> > +               dev_err(lcd->dev, "power_on is NULL.\n");
> > +               return -EFAULT;
> > +       } else {
> > +               pd->power_on(lcd->ld, 1);
> > +               mdelay(pd->power_on_delay);
> > +       }
> > +
> > +       if (!pd->reset) {
> > +               dev_err(lcd->dev, "reset is NULL.\n");
> > +               return -EFAULT;
> > +       } else {
> > +               pd->reset(lcd->ld);
> > +               mdelay(pd->reset_delay);
> > +       }
> > +
> > +       ret = lms501kf03_ldi_init(lcd);
> > +       if (ret) {
> > +               dev_err(lcd->dev, "failed to initialize ldi.\n");
> > +               return ret;
> > +       }
> > +
> > +       ret = lms501kf03_ldi_enable(lcd);
> > +       if (ret) {
> > +               dev_err(lcd->dev, "failed to enable ldi.\n");
> > +               return ret;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int lms501kf03_power_off(struct lms501kf03 *lcd)
> > +{
> > +       int ret = 0;
> > +       struct lcd_platform_data *pd = NULL;
> > +
> > +       pd = lcd->lcd_pd;
> > +       if (!pd) {
> > +               dev_err(lcd->dev, "platform data is NULL\n");
> > +               return -EFAULT;
> > +       }
> > +
> > +       ret = lms501kf03_ldi_disable(lcd);
> > +       if (ret) {
> > +               dev_err(lcd->dev, "lcd setting failed.\n");
> > +               return -EIO;
> > +       }
> > +
> > +       mdelay(pd->power_off_delay);
> > +
> > +       if (!pd->power_on) {
> > +               dev_err(lcd->dev, "power_on is NULL.\n");
> > +               return -EFAULT;
> > +       } else
> > +               pd->power_on(lcd->ld, 0);
> > +
> > +       return 0;
> > +}
> > +
> > +static int lms501kf03_power(struct lms501kf03 *lcd, int power)
> > +{
> > +       int ret = 0;
> > +
> > +       if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
> > +               ret = lms501kf03_power_on(lcd);
> > +       else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
> > +               ret = lms501kf03_power_off(lcd);
> > +
> > +       if (!ret)
> > +               lcd->power = power;
> > +
> > +       return ret;
> > +}
> > +
> > +static int lms501kf03_get_power(struct lcd_device *ld)
> > +{
> > +       struct lms501kf03 *lcd = lcd_get_data(ld);
> > +
> > +       return lcd->power;
> > +}
> > +
> > +static int lms501kf03_set_power(struct lcd_device *ld, int power)
> > +{
> > +       struct lms501kf03 *lcd = lcd_get_data(ld);
> > +
> > +       if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
> > +               power != FB_BLANK_NORMAL) {
> > +               dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       return lms501kf03_power(lcd, power);
> > +}
> > +
> > +static int lms501kf03_get_brightness(struct backlight_device *bd)
> > +{
> > +       return bd->props.brightness;
> > +}
> > +
> > +static int lms501kf03_set_brightness(struct backlight_device *bd)
> > +{
> > +       int ret = 0;
> > +       int brightness = bd->props.brightness;
> > +
> > +       if (brightness < MIN_BRIGHTNESS ||
> > +               brightness > bd->props.max_brightness) {
> > +               dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
> > +                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
> > +               return -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +

Are these backlight control functions really necessary?
As I am concerned, this LMS501KF03 uses PWM backlight as backlight control.
If PWM backlight driver is used, these backlight control functions are superfluous.

> > +static struct lcd_ops lms501kf03_lcd_ops = {
> > +       .get_power = lms501kf03_get_power,
> > +       .set_power = lms501kf03_set_power,
> > +};
> > +
> > +static const struct backlight_ops lms501kf03_backlight_ops = {
> > +       .get_brightness = lms501kf03_get_brightness,
> > +       .update_status = lms501kf03_set_brightness,
> > +};
> > +
> > +static int __devinit lms501kf03_probe(struct spi_device *spi)
> > +{
> > +       struct lms501kf03 *lcd = NULL;
> > +       struct lcd_device *ld = NULL;
> > +       struct backlight_device *bd = NULL;
> > +       struct backlight_properties props;
> > +       int ret = 0;
> > +
> > +       lcd = kzalloc(sizeof(struct lms501kf03), GFP_KERNEL);
> > +       if (!lcd)
> > +               return -ENOMEM;
> > +
> > +       /* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */
> > +       spi->bits_per_word = 9;
> > +
> > +       ret = spi_setup(spi);
> > +       if (ret < 0) {
> > +               dev_err(&spi->dev, "spi setup failed.\n");
> > +               goto out_free_lcd;
> > +       }
> > +
> > +       lcd->spi = spi;
> > +       lcd->dev = &spi->dev;
> > +
> > +       lcd->lcd_pd = (struct lcd_platform_data *)spi->dev.platform_data;

This is an unnecessary cast of void*.

> > +       if (!lcd->lcd_pd) {
> > +               dev_err(&spi->dev, "platform data is NULL\n");
> > +               goto out_free_lcd;
> > +       }
> > +
> > +       ld = lcd_device_register("lms501kf03", &spi->dev, lcd,
> > +               &lms501kf03_lcd_ops);
> > +       if (IS_ERR(ld)) {
> > +               ret = PTR_ERR(ld);
> > +               goto out_free_lcd;
> > +       }
> > +
> > +       lcd->ld = ld;
> > +
> > +       memset(&props, 0, sizeof(struct backlight_properties));
> > +       props.type = BACKLIGHT_RAW;
> > +       props.max_brightness = MAX_BRIGHTNESS;
> > +
> > +       bd = backlight_device_register("lms501kf03-bl", &spi->dev, lcd,
> > +               &lms501kf03_backlight_ops, &props);
> > +       if (IS_ERR(bd)) {
> > +               ret = PTR_ERR(bd);
> > +               goto out_lcd_unregister;
> > +       }
> > +
> > +       bd->props.brightness = DEFAULT_BRIGHTNESS;
> > +       lcd->bd = bd;
> > +
> > +       if (!lcd->lcd_pd->lcd_enabled) {
> > +               /*
> > +                * if lcd panel was off from bootloader then
> > +                * current lcd status is powerdown and then
> > +                * it enables lcd panel.
> > +                */
> > +               lcd->power = FB_BLANK_POWERDOWN;
> > +
> > +               lms501kf03_power(lcd, FB_BLANK_UNBLANK);
> > +       } else
> > +               lcd->power = FB_BLANK_UNBLANK;
> > +
> > +       dev_set_drvdata(&spi->dev, lcd);
> > +
> > +       dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n");
> > +
> > +       return 0;
> > +
> > +out_lcd_unregister:
> > +       lcd_device_unregister(ld);
> > +out_free_lcd:
> > +       kfree(lcd);
> > +       return ret;
> > +}
> > +
> > +static int __devexit lms501kf03_remove(struct spi_device *spi)
> > +{
> > +       struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
> > +
> > +       lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
> > +       lcd_device_unregister(lcd->ld);
> > +       kfree(lcd);
> > +
> > +       return 0;
> > +}
> > +
> > +#if defined(CONFIG_PM)
> > +unsigned int before_power;
> > +
> > +static int lms501kf03_suspend(struct spi_device *spi, pm_message_t mesg)
> > +{
> > +       int ret = 0;
> > +       struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
> > +
> > +       dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
> > +
> > +       before_power = lcd->power;
> > +
> > +       /*
> > +        * when lcd panel is suspend, lcd panel becomes off
> > +        * regardless of status.
> > +        */
> > +       ret = lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
> > +
> > +       return ret;
> > +}
> > +
> > +static int lms501kf03_resume(struct spi_device *spi)
> > +{
> > +       int ret = 0;
> > +       struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
> > +
> > +       /*
> > +        * after suspended, if lcd panel status is FB_BLANK_UNBLANK
> > +        * (at that time, before_power is FB_BLANK_UNBLANK) then
> > +        * it changes that status to FB_BLANK_POWERDOWN to get lcd on.
> > +        */
> > +       if (before_power = FB_BLANK_UNBLANK)
> > +               lcd->power = FB_BLANK_POWERDOWN;
> > +
> > +       dev_dbg(&spi->dev, "before_power = %d\n", before_power);
> > +
> > +       ret = lms501kf03_power(lcd, before_power);
> > +
> > +       return ret;
> > +}
> > +#else
> > +#define lms501kf03_suspend     NULL
> > +#define lms501kf03_resume      NULL
> > +#endif
> > +
> > +void lms501kf03_shutdown(struct spi_device *spi)
> > +{
> > +       struct lms501kf03 *lcd = dev_get_drvdata(&spi->dev);
> > +
> > +       lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
> > +}
> > +
> > +static struct spi_driver lms501kf03_driver = {
> > +       .driver = {
> > +               .name   = "lms501kf03",
> > +               .bus    = &spi_bus_type,
> > +               .owner  = THIS_MODULE,
> > +       },
> > +       .probe          = lms501kf03_probe,
> > +       .remove         = __devexit_p(lms501kf03_remove),
> > +       .shutdown       = lms501kf03_shutdown,
> > +       .suspend        = lms501kf03_suspend,
> > +       .resume         = lms501kf03_resume,
> > +};
> > +
> > +module_spi_driver(lms501kf03_driver);
> > +
> > +MODULE_AUTHOR("Ilho Lee <Ilho215.lee@samsung.com>");
> > +MODULE_AUTHOR("Sachin Kamat <sachin.kamat@samsung.com>");
> > +MODULE_DESCRIPTION("LMS501KF03 LCD Driver");
> > +MODULE_LICENSE("GPL");
> > --
> > 1.7.4.1
> >
> 
> 
> 
> --
> With warm regards,
> Sachin


^ permalink raw reply

* Re: [PATCH v2 0/4] video: add support for the AUO-K190X epd controllers
From: Florian Tobias Schandinat @ 2012-04-29 23:22 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <201204281218.04180.heiko@sntech.de>

On 04/28/2012 10:18 AM, Heiko Stübner wrote:
> Second version of support for the Sipix epd controllers made by AUO.
> 
> Changes were made to the common code in patch 2, including fixing
> the Kconfig options as suggested by Florian Tobias Schandinat and
> fixing a possible runtime-pm mismatch if the deferred_io
> callback was called without a prior call to first_io.

Not entirely correct: I suggested to change it into menuconfig, you
changed it into config. Well, actually I like your solution better than
mine ;)

> 
> Heiko Stübner (4):
>   fb_defio: add first_io callback
>   video: auo_k190x: add code shared by controller drivers
>   video: auo_k190x: add driver for AUO-K1900 variant
>   video: auo_k190x: add driver for AUO-K1901 variant

Applied this series.


Thanks,

Florian Tobias Schandinat

> 
>  drivers/video/Kconfig       |   33 ++
>  drivers/video/Makefile      |    3 +
>  drivers/video/auo_k1900fb.c |  198 ++++++++
>  drivers/video/auo_k1901fb.c |  251 +++++++++++
>  drivers/video/auo_k190x.c   | 1046 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/video/auo_k190x.h   |  129 ++++++
>  drivers/video/fb_defio.c    |    4 +
>  drivers/video/smscufx.c     |    2 +-
>  drivers/video/udlfb.c       |    2 +-
>  include/linux/fb.h          |    1 +
>  include/video/auo_k190xfb.h |  106 +++++
>  11 files changed, 1773 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/video/auo_k1900fb.c
>  create mode 100644 drivers/video/auo_k1901fb.c
>  create mode 100644 drivers/video/auo_k190x.c
>  create mode 100644 drivers/video/auo_k190x.h
>  create mode 100644 include/video/auo_k190xfb.h
> 


^ permalink raw reply

* Re: [PULL for v3.5] fb: exynos: Fix MIPI/DSI front/back porch settings
From: Florian Tobias Schandinat @ 2012-04-29 23:18 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <4788368.X6fyZNtHh2@avalon>

On 04/24/2012 12:44 PM, Laurent Pinchart wrote:
> Hi Florian,
> 
> Could you please pull the following fix ?
> 
> The following changes since commit d313a86d2efb2c5568832389663322e93e291c59:
> 
>   i.MX28: Shut down the LCD controller to avoid BootROM sampling bug 
> (2012-04-21 21:53:12 +0000)
> 
> are available in the git repository at:
>   git://linuxtv.org/pinchartl/fbdev.git fixes

Merged.


Thanks,

Florian Tobias Schandinat

> 
> Laurent Pinchart (1):
>       fb: exynos: Fix MIPI/DSI front/back porch settings
> 
>  drivers/video/exynos/exynos_mipi_dsi_common.c |    8 ++++----
>  1 files changed, 4 insertions(+), 4 deletions(-)
> 


^ permalink raw reply

* [PATCH v2] video/sis: Use SiS_DRAMType from init.h and annotate it __devinitconst
From: Peter Huewe @ 2012-04-29 20:26 UTC (permalink / raw)
  To: Thomas Winischhofer
  Cc: Florian Tobias Schandinat, linux-fbdev, linux-kernel, Peter Huewe
In-Reply-To: <1334524209-20518-1-git-send-email-peterhuewe@gmx.de>

This patch removes the duplicated SiS_DRAMType from sis_main.c since it
is already defined exactly the same in init.h.
Since we don't want to include init.h (for size reasons) we move the
struct to sis_main.h.
Since SiS_DRAMType is const and only used by sisfb_post_300_rwtest which is
marked __devinit we can also annotate SiS_DRAMType with __devinitconst.

And since hardcoded values are bad we use ARRAY_SIZE for determining the
size of SiS_DRAMType ;)

v2:
Move struct to sis_main.h to decrease module size

Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
---
 drivers/video/sis/init.h     |   20 --------------------
 drivers/video/sis/sis_main.c |   21 +--------------------
 drivers/video/sis/sis_main.h |   20 ++++++++++++++++++++
 3 files changed, 21 insertions(+), 40 deletions(-)

diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h
index a22e892..85d6738 100644
--- a/drivers/video/sis/init.h
+++ b/drivers/video/sis/init.h
@@ -105,26 +105,6 @@ static const unsigned short ModeIndex_1920x1440[]    = {0x68, 0x69, 0x00, 0x6b};
 static const unsigned short ModeIndex_300_2048x1536[]= {0x6c, 0x6d, 0x00, 0x00};
 static const unsigned short ModeIndex_310_2048x1536[]= {0x6c, 0x6d, 0x00, 0x6e};
 
-static const unsigned short SiS_DRAMType[17][5]={
-	{0x0C,0x0A,0x02,0x40,0x39},
-	{0x0D,0x0A,0x01,0x40,0x48},
-	{0x0C,0x09,0x02,0x20,0x35},
-	{0x0D,0x09,0x01,0x20,0x44},
-	{0x0C,0x08,0x02,0x10,0x31},
-	{0x0D,0x08,0x01,0x10,0x40},
-	{0x0C,0x0A,0x01,0x20,0x34},
-	{0x0C,0x09,0x01,0x08,0x32},
-	{0x0B,0x08,0x02,0x08,0x21},
-	{0x0C,0x08,0x01,0x08,0x30},
-	{0x0A,0x08,0x02,0x04,0x11},
-	{0x0B,0x0A,0x01,0x10,0x28},
-	{0x09,0x08,0x02,0x02,0x01},
-	{0x0B,0x09,0x01,0x08,0x24},
-	{0x0B,0x08,0x01,0x04,0x20},
-	{0x0A,0x08,0x01,0x02,0x10},
-	{0x09,0x08,0x01,0x01,0x00}
-};
-
 static const unsigned char SiS_MDA_DAC[]  {
 	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
index 078ca21..8abd42b 100644
--- a/drivers/video/sis/sis_main.c
+++ b/drivers/video/sis/sis_main.c
@@ -4231,27 +4231,8 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth
 	unsigned short sr14;
 	unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid;
 	unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage;
-	static const unsigned short SiS_DRAMType[17][5] = {
-		{0x0C,0x0A,0x02,0x40,0x39},
-		{0x0D,0x0A,0x01,0x40,0x48},
-		{0x0C,0x09,0x02,0x20,0x35},
-		{0x0D,0x09,0x01,0x20,0x44},
-		{0x0C,0x08,0x02,0x10,0x31},
-		{0x0D,0x08,0x01,0x10,0x40},
-		{0x0C,0x0A,0x01,0x20,0x34},
-		{0x0C,0x09,0x01,0x08,0x32},
-		{0x0B,0x08,0x02,0x08,0x21},
-		{0x0C,0x08,0x01,0x08,0x30},
-		{0x0A,0x08,0x02,0x04,0x11},
-		{0x0B,0x0A,0x01,0x10,0x28},
-		{0x09,0x08,0x02,0x02,0x01},
-		{0x0B,0x09,0x01,0x08,0x24},
-		{0x0B,0x08,0x01,0x04,0x20},
-		{0x0A,0x08,0x01,0x02,0x10},
-		{0x09,0x08,0x01,0x01,0x00}
-	};
 
-	 for(k = 0; k <= 16; k++) {
+	 for (k = 0; k < ARRAY_SIZE(SiS_DRAMType); k++) {
 
 		RankCapacity = buswidth * SiS_DRAMType[k][3];
 
diff --git a/drivers/video/sis/sis_main.h b/drivers/video/sis/sis_main.h
index 9540e97..242c1c3 100644
--- a/drivers/video/sis/sis_main.h
+++ b/drivers/video/sis/sis_main.h
@@ -776,6 +776,26 @@ extern void		SiS_Chrontel701xBLOff(struct SiS_Private *SiS_Pr);
 #endif
 extern void		SiS_SiS30xBLOn(struct SiS_Private *SiS_Pr);
 extern void		SiS_SiS30xBLOff(struct SiS_Private *SiS_Pr);
+
+static const unsigned short __devinitconst SiS_DRAMType[17][5] = {
+	{0x0C,0x0A,0x02,0x40,0x39},
+	{0x0D,0x0A,0x01,0x40,0x48},
+	{0x0C,0x09,0x02,0x20,0x35},
+	{0x0D,0x09,0x01,0x20,0x44},
+	{0x0C,0x08,0x02,0x10,0x31},
+	{0x0D,0x08,0x01,0x10,0x40},
+	{0x0C,0x0A,0x01,0x20,0x34},
+	{0x0C,0x09,0x01,0x08,0x32},
+	{0x0B,0x08,0x02,0x08,0x21},
+	{0x0C,0x08,0x01,0x08,0x30},
+	{0x0A,0x08,0x02,0x04,0x11},
+	{0x0B,0x0A,0x01,0x10,0x28},
+	{0x09,0x08,0x02,0x02,0x01},
+	{0x0B,0x09,0x01,0x08,0x24},
+	{0x0B,0x08,0x01,0x04,0x20},
+	{0x0A,0x08,0x01,0x02,0x10},
+	{0x09,0x08,0x01,0x01,0x00}
+};
 #endif
 
 
-- 
1.7.3.4


^ permalink raw reply related

* Re: [Xen-devel] [PATCH] drivers/video/xen-fbfront.c: add missing cleanup code
From: Florian Tobias Schandinat @ 2012-04-28 23:47 UTC (permalink / raw)
  To: Konrad Rzeszutek Wilk
  Cc: Julia Lawall, Jeremy Fitzhardinge, xen-devel, linux-kernel,
	kernel-janitors, linux-fbdev, virtualization
In-Reply-To: <20120426214222.GA11289@phenom.dumpdata.com>

Hi Konrad,

On 04/26/2012 09:42 PM, Konrad Rzeszutek Wilk wrote:
> On Sun, Apr 22, 2012 at 11:57:40AM +0200, Julia Lawall wrote:
>> From: Julia Lawall <Julia.Lawall@lip6.fr>
>>
>> The operations in the subsequent error-handling code appear to be also
>> useful here.
> 
> How about doing it this way?

Looks good to me.

> Florian, are you OK me carrying this patch in my tree for Linus or would
> you prefer to do it?

Yes, I'm okay with you carrying this patch. Feel free to add
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>


Best regards,

Florian Tobias Schandinat

> 
> commit a833fb9973b47cb30d1086e73f20b62a425bcd85
> Author: Julia Lawall <Julia.Lawall@lip6.fr>
> Date:   Sun Apr 22 11:57:40 2012 +0200
> 
>     drivers/video/xen-fbfront.c: add missing cleanup code
>     
>     The operations in the subsequent error-handling code appear to be also
>     useful here.
>     
>     Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
>     [v1: Collapse some of the error handling functions]
>     Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> 
> diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
> index cb4529c..aa42160 100644
> --- a/drivers/video/xen-fbfront.c
> +++ b/drivers/video/xen-fbfront.c
> @@ -458,26 +458,31 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
>  	xenfb_init_shared_page(info, fb_info);
>  
>  	ret = xenfb_connect_backend(dev, info);
> -	if (ret < 0)
> -		goto error;
> +	if (ret < 0) {
> +		xenbus_dev_fatal(dev, ret, "xenfb_connect_backend");
> +		goto error_fb;
> +	}
>  
>  	ret = register_framebuffer(fb_info);
>  	if (ret) {
> -		fb_deferred_io_cleanup(fb_info);
> -		fb_dealloc_cmap(&fb_info->cmap);
> -		framebuffer_release(fb_info);
>  		xenbus_dev_fatal(dev, ret, "register_framebuffer");
> -		goto error;
> +		goto error_fb;
>  	}
>  	info->fb_info = fb_info;
>  
>  	xenfb_make_preferred_console();
>  	return 0;
>  
> - error_nomem:
> -	ret = -ENOMEM;
> -	xenbus_dev_fatal(dev, ret, "allocating device memory");
> - error:
> +error_fb:
> +	fb_deferred_io_cleanup(fb_info);
> +	fb_dealloc_cmap(&fb_info->cmap);
> +	framebuffer_release(fb_info);
> +error_nomem:
> +	if (!ret) {
> +		ret = -ENOMEM;
> +		xenbus_dev_fatal(dev, ret, "allocating device memory");
> +	}
> +error:
>  	xenfb_remove(dev);
>  	return ret;
>  }
> 
> 
> 
>>
>> Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
>>
>> ---
>> Not tested.
>>
>>  drivers/video/xen-fbfront.c |    7 ++++++-
>>  1 file changed, 6 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
>> index cb4529c..b0bd59c 100644
>> --- a/drivers/video/xen-fbfront.c
>> +++ b/drivers/video/xen-fbfront.c
>> @@ -458,8 +458,13 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
>>  	xenfb_init_shared_page(info, fb_info);
>>  
>>  	ret = xenfb_connect_backend(dev, info);
>> -	if (ret < 0)
>> +	if (ret < 0) {
>> +		fb_deferred_io_cleanup(fb_info);
>> +		fb_dealloc_cmap(&fb_info->cmap);
>> +		framebuffer_release(fb_info);
>> +		xenbus_dev_fatal(dev, ret, "xenfb_connect_backend");
>>  		goto error;
>> +	}
>>  
>>  	ret = register_framebuffer(fb_info);
>>  	if (ret) {
>>
>>
>> _______________________________________________
>> Xen-devel mailing list
>> Xen-devel@lists.xen.org
>> http://lists.xen.org/xen-devel
> 


^ permalink raw reply

* [PATCH 4/4] video: auo_k190x: add driver for AUO-K1901 variant
From: Heiko Stübner @ 2012-04-28 10:22 UTC (permalink / raw)
  To: linux-fbdev

This controller not only supports higher resolutions than the K1900
but concurrent updates as well. This results in a generally higher
display speed.

Signed-off-by: Heiko Stübner <heiko@sntech.de>
---
 drivers/video/Kconfig       |    8 ++
 drivers/video/Makefile      |    1 +
 drivers/video/auo_k1901fb.c |  251 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 260 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/auo_k1901fb.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 148d83c..d304204 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2407,6 +2407,14 @@ config FB_AUO_K1900
 	  This controller can drive Sipix epaper displays but can only do
 	  serial updates, reducing the number of possible frames per second.
 
+config FB_AUO_K1901
+	tristate "AUO-K1901 EPD controller support"
+	depends on FB && FB_AUO_K190X
+	help
+	  This driver implements support for the AUO K1901 epd-controller.
+	  This controller can drive Sipix epaper displays and supports
+	  concurrent updates, making higher frames per second possible.
+
 config FB_JZ4740
 	tristate "JZ4740 LCD framebuffer support"
 	depends on FB && MACH_JZ4740
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e84df73..ee8dafb 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_FB_METRONOME)        += metronomefb.o
 obj-$(CONFIG_FB_BROADSHEET)       += broadsheetfb.o
 obj-$(CONFIG_FB_AUO_K190X)	  += auo_k190x.o
 obj-$(CONFIG_FB_AUO_K1900)	  += auo_k1900fb.o
+obj-$(CONFIG_FB_AUO_K1901)	  += auo_k1901fb.o
 obj-$(CONFIG_FB_S1D13XXX)	  += s1d13xxxfb.o
 obj-$(CONFIG_FB_SH7760)		  += sh7760fb.o
 obj-$(CONFIG_FB_IMX)              += imxfb.o
diff --git a/drivers/video/auo_k1901fb.c b/drivers/video/auo_k1901fb.c
new file mode 100644
index 0000000..1c054c1
--- /dev/null
+++ b/drivers/video/auo_k1901fb.c
@@ -0,0 +1,251 @@
+/*
+ * auok190xfb.c -- FB driver for AUO-K1901 controllers
+ *
+ * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on broadsheetfb.c
+ *
+ * Copyright (C) 2008, Jaya Kumar
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
+ *
+ * This driver is written to be used with the AUO-K1901 display controller.
+ *
+ * It is intended to be architecture independent. A board specific driver
+ * must be used to perform all the physical IO interactions.
+ *
+ * The controller supports different update modes:
+ * mode0+1 16 step gray (4bit)
+ * mode2+3 4 step gray (2bit)
+ * mode4+5 2 step gray (1bit)
+ * - mode4 is described as "without LUT"
+ * mode7 automatic selection of update mode
+ *
+ * The most interesting difference to the K1900 is the ability to do screen
+ * updates in an asynchronous fashion. Where the K1900 needs to wait for the
+ * current update to complete, the K1901 can process later updates already.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/pm_runtime.h>
+
+#include <video/auo_k190xfb.h>
+
+#include "auo_k190x.h"
+
+/*
+ * AUO-K1901 specific commands
+ */
+
+#define AUOK1901_CMD_LUT_INTERFACE	0x0005
+#define AUOK1901_CMD_DMA_START		0x1001
+#define AUOK1901_CMD_CURSOR_START	0x1007
+#define AUOK1901_CMD_CURSOR_STOP	AUOK190X_CMD_DATA_STOP
+#define AUOK1901_CMD_DDMA_START		0x1009
+
+#define AUOK1901_INIT_GATE_PULSE_LOW	(0 << 14)
+#define AUOK1901_INIT_GATE_PULSE_HIGH	(1 << 14)
+#define AUOK1901_INIT_SINGLE_GATE	(0 << 13)
+#define AUOK1901_INIT_DOUBLE_GATE	(1 << 13)
+
+/* Bits to pixels
+ *   Mode	15-12	11-8	7-4	3-0
+ *   format2	2	T	1	T
+ *   format3	1	T	2	T
+ *   format4	T	2	T	1
+ *   format5	T	1	T	2
+ *
+ *   halftone modes:
+ *   format6	2	2	1	1
+ *   format7	1	1	2	2
+ */
+#define AUOK1901_INIT_FORMAT2		(1 << 7)
+#define AUOK1901_INIT_FORMAT3		((1 << 7) | (1 << 6))
+#define AUOK1901_INIT_FORMAT4		(1 << 8)
+#define AUOK1901_INIT_FORMAT5		((1 << 8) | (1 << 6))
+#define AUOK1901_INIT_FORMAT6		((1 << 8) | (1 << 7))
+#define AUOK1901_INIT_FORMAT7		((1 << 8) | (1 << 7) | (1 << 6))
+
+/* res[4] to bit 10
+ * res[3-0] to bits 5-2
+ */
+#define AUOK1901_INIT_RESOLUTION(_res)	(((_res & (1 << 4)) << 6) \
+					 | ((_res & 0xf) << 2))
+
+/*
+ * portrait / landscape orientation in AUOK1901_CMD_DMA_START
+ */
+#define AUOK1901_DMA_ROTATE90(_rot)		((_rot & 1) << 13)
+
+/*
+ * equivalent to 1 << 11, needs the ~ to have same rotation like K1900
+ */
+#define AUOK1901_DDMA_ROTATE180(_rot)		((~_rot & 2) << 10)
+
+static void auok1901_init(struct auok190xfb_par *par)
+{
+	struct auok190x_board *board = par->board;
+	u16 init_param = 0;
+
+	init_param |= AUOK190X_INIT_INVERSE_WHITE;
+	init_param |= AUOK190X_INIT_FORMAT0;
+	init_param |= AUOK1901_INIT_RESOLUTION(par->resolution);
+	init_param |= AUOK190X_INIT_SHIFT_LEFT;
+
+	auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param);
+
+	/* let the controller finish */
+	board->wait_for_rdy(par);
+}
+
+static void auok1901_update_region(struct auok190xfb_par *par, int mode,
+						u16 y1, u16 y2)
+{
+	struct device *dev = par->info->device;
+	unsigned char *buf = (unsigned char *)par->info->screen_base;
+	int xres = par->info->var.xres;
+	u16 args[5];
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&(par->io_lock));
+
+	/* y1 and y2 must be a multiple of 2 so drop the lowest bit */
+	y1 &= 0xfffe;
+	y2 &= 0xfffe;
+
+	dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n",
+		1, y1+1, xres, y2-y1, mode);
+
+	/* K1901: first transfer the region data */
+	args[0] = AUOK1901_DMA_ROTATE90(par->rotation) | 1;
+	args[1] = y1 + 1;
+	args[2] = xres;
+	args[3] = y2 - y1;
+	buf += y1 * xres;
+	auok190x_send_cmdargs_pixels_nowait(par, AUOK1901_CMD_DMA_START, 4,
+					    args, ((y2 - y1) * xres)/2,
+					    (u16 *) buf);
+	auok190x_send_command_nowait(par, AUOK190X_CMD_DATA_STOP);
+
+	/* K1901: second tell the controller to update the region with mode */
+	args[0] = mode | AUOK1901_DDMA_ROTATE180(par->rotation);
+	args[1] = 1;
+	args[2] = y1 + 1;
+	args[3] = xres;
+	args[4] = y2 - y1;
+	auok190x_send_cmdargs_nowait(par, AUOK1901_CMD_DDMA_START, 5, args);
+
+	par->update_cnt++;
+
+	mutex_unlock(&(par->io_lock));
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+}
+
+static void auok1901fb_dpy_update_pages(struct auok190xfb_par *par,
+						u16 y1, u16 y2)
+{
+	int mode;
+
+	if (par->update_mode < 0) {
+		mode = AUOK190X_UPDATE_MODE(1);
+		par->last_mode = -1;
+	} else {
+		mode = AUOK190X_UPDATE_MODE(par->update_mode);
+		par->last_mode = par->update_mode;
+	}
+
+	if (par->flash)
+		mode |= AUOK190X_UPDATE_NONFLASH;
+
+	auok1901_update_region(par, mode, y1, y2);
+}
+
+static void auok1901fb_dpy_update(struct auok190xfb_par *par)
+{
+	int mode;
+
+	/* When doing full updates, wait for the controller to be ready
+	 * This will hopefully catch some hangs of the K1901
+	 */
+	par->board->wait_for_rdy(par);
+
+	if (par->update_mode < 0) {
+		mode = AUOK190X_UPDATE_MODE(0);
+		par->last_mode = -1;
+	} else {
+		mode = AUOK190X_UPDATE_MODE(par->update_mode);
+		par->last_mode = par->update_mode;
+	}
+
+	if (par->flash)
+		mode |= AUOK190X_UPDATE_NONFLASH;
+
+	auok1901_update_region(par, mode, 0, par->info->var.yres);
+	par->update_cnt = 0;
+}
+
+static bool auok1901fb_need_refresh(struct auok190xfb_par *par)
+{
+	return (par->update_cnt > 10);
+}
+
+static int __devinit auok1901fb_probe(struct platform_device *pdev)
+{
+	struct auok190x_init_data init;
+	struct auok190x_board *board;
+
+	/* pick up board specific routines */
+	board = pdev->dev.platform_data;
+	if (!board)
+		return -EINVAL;
+
+	/* fill temporary init struct for common init */
+	init.id = "auo_k1901fb";
+	init.board = board;
+	init.update_partial = auok1901fb_dpy_update_pages;
+	init.update_all = auok1901fb_dpy_update;
+	init.need_refresh = auok1901fb_need_refresh;
+	init.init = auok1901_init;
+
+	return auok190x_common_probe(pdev, &init);
+}
+
+static int __devexit auok1901fb_remove(struct platform_device *pdev)
+{
+	return auok190x_common_remove(pdev);
+}
+
+static struct platform_driver auok1901fb_driver = {
+	.probe	= auok1901fb_probe,
+	.remove = __devexit_p(auok1901fb_remove),
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "auo_k1901fb",
+		.pm = &auok190x_pm,
+	},
+};
+module_platform_driver(auok1901fb_driver);
+
+MODULE_DESCRIPTION("framebuffer driver for the AUO-K1901 EPD controller");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_LICENSE("GPL");
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 3/4] video: auo_k190x: add driver for AUO-K1900 variant
From: Heiko Stübner @ 2012-04-28 10:21 UTC (permalink / raw)
  To: linux-fbdev

This controller only supports smaller resolutions and only serial
updates, i.e. it has to wait for an update to finish before
starting another one.

Signed-off-by: Heiko Stübner <heiko@sntech.de>
---
 drivers/video/Kconfig       |    8 ++
 drivers/video/Makefile      |    1 +
 drivers/video/auo_k1900fb.c |  198 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 207 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/auo_k1900fb.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 31c029b..148d83c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2399,6 +2399,14 @@ config FB_AUO_K190X
 	  controller drivers. You will also have to enable the driver
 	  for the controller type used in your device.
 
+config FB_AUO_K1900
+	tristate "AUO-K1900 EPD controller support"
+	depends on FB && FB_AUO_K190X
+	help
+	  This driver implements support for the AUO K1900 epd-controller.
+	  This controller can drive Sipix epaper displays but can only do
+	  serial updates, reducing the number of possible frames per second.
+
 config FB_JZ4740
 	tristate "JZ4740 LCD framebuffer support"
 	depends on FB && MACH_JZ4740
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index d5406f2..e84df73 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -119,6 +119,7 @@ obj-$(CONFIG_FB_MAXINE)		  += maxinefb.o
 obj-$(CONFIG_FB_METRONOME)        += metronomefb.o
 obj-$(CONFIG_FB_BROADSHEET)       += broadsheetfb.o
 obj-$(CONFIG_FB_AUO_K190X)	  += auo_k190x.o
+obj-$(CONFIG_FB_AUO_K1900)	  += auo_k1900fb.o
 obj-$(CONFIG_FB_S1D13XXX)	  += s1d13xxxfb.o
 obj-$(CONFIG_FB_SH7760)		  += sh7760fb.o
 obj-$(CONFIG_FB_IMX)              += imxfb.o
diff --git a/drivers/video/auo_k1900fb.c b/drivers/video/auo_k1900fb.c
new file mode 100644
index 0000000..c36cf96
--- /dev/null
+++ b/drivers/video/auo_k1900fb.c
@@ -0,0 +1,198 @@
+/*
+ * auok190xfb.c -- FB driver for AUO-K1900 controllers
+ *
+ * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on broadsheetfb.c
+ *
+ * Copyright (C) 2008, Jaya Kumar
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
+ *
+ * This driver is written to be used with the AUO-K1900 display controller.
+ *
+ * It is intended to be architecture independent. A board specific driver
+ * must be used to perform all the physical IO interactions.
+ *
+ * The controller supports different update modes:
+ * mode0+1 16 step gray (4bit)
+ * mode2 4 step gray (2bit) - FIXME: add strange refresh
+ * mode3 2 step gray (1bit) - FIXME: add strange refresh
+ * mode4 handwriting mode (strange behaviour)
+ * mode5 automatic selection of update mode
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/pm_runtime.h>
+
+#include <video/auo_k190xfb.h>
+
+#include "auo_k190x.h"
+
+/*
+ * AUO-K1900 specific commands
+ */
+
+#define AUOK1900_CMD_PARTIALDISP	0x1001
+#define AUOK1900_CMD_ROTATION		0x1006
+#define AUOK1900_CMD_LUT_STOP		0x1009
+
+#define AUOK1900_INIT_TEMP_AVERAGE	(1 << 13)
+#define AUOK1900_INIT_ROTATE(_x)	((_x & 0x3) << 10)
+#define AUOK1900_INIT_RESOLUTION(_res)	((_res & 0x7) << 2)
+
+static void auok1900_init(struct auok190xfb_par *par)
+{
+	struct auok190x_board *board = par->board;
+	u16 init_param = 0;
+
+	init_param |= AUOK1900_INIT_TEMP_AVERAGE;
+	init_param |= AUOK1900_INIT_ROTATE(par->rotation);
+	init_param |= AUOK190X_INIT_INVERSE_WHITE;
+	init_param |= AUOK190X_INIT_FORMAT0;
+	init_param |= AUOK1900_INIT_RESOLUTION(par->resolution);
+	init_param |= AUOK190X_INIT_SHIFT_RIGHT;
+
+	auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param);
+
+	/* let the controller finish */
+	board->wait_for_rdy(par);
+}
+
+static void auok1900_update_region(struct auok190xfb_par *par, int mode,
+						u16 y1, u16 y2)
+{
+	struct device *dev = par->info->device;
+	unsigned char *buf = (unsigned char *)par->info->screen_base;
+	int xres = par->info->var.xres;
+	u16 args[4];
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&(par->io_lock));
+
+	/* y1 and y2 must be a multiple of 2 so drop the lowest bit */
+	y1 &= 0xfffe;
+	y2 &= 0xfffe;
+
+	dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n",
+		1, y1+1, xres, y2-y1, mode);
+
+	/* to FIX handle different partial update modes */
+	args[0] = mode | 1;
+	args[1] = y1 + 1;
+	args[2] = xres;
+	args[3] = y2 - y1;
+	buf += y1 * xres;
+	auok190x_send_cmdargs_pixels(par, AUOK1900_CMD_PARTIALDISP, 4, args,
+				     ((y2 - y1) * xres)/2, (u16 *) buf);
+	auok190x_send_command(par, AUOK190X_CMD_DATA_STOP);
+
+	par->update_cnt++;
+
+	mutex_unlock(&(par->io_lock));
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+}
+
+static void auok1900fb_dpy_update_pages(struct auok190xfb_par *par,
+						u16 y1, u16 y2)
+{
+	int mode;
+
+	if (par->update_mode < 0) {
+		mode = AUOK190X_UPDATE_MODE(1);
+		par->last_mode = -1;
+	} else {
+		mode = AUOK190X_UPDATE_MODE(par->update_mode);
+		par->last_mode = par->update_mode;
+	}
+
+	if (par->flash)
+		mode |= AUOK190X_UPDATE_NONFLASH;
+
+	auok1900_update_region(par, mode, y1, y2);
+}
+
+static void auok1900fb_dpy_update(struct auok190xfb_par *par)
+{
+	int mode;
+
+	if (par->update_mode < 0) {
+		mode = AUOK190X_UPDATE_MODE(0);
+		par->last_mode = -1;
+	} else {
+		mode = AUOK190X_UPDATE_MODE(par->update_mode);
+		par->last_mode = par->update_mode;
+	}
+
+	if (par->flash)
+		mode |= AUOK190X_UPDATE_NONFLASH;
+
+	auok1900_update_region(par, mode, 0, par->info->var.yres);
+	par->update_cnt = 0;
+}
+
+static bool auok1900fb_need_refresh(struct auok190xfb_par *par)
+{
+	return (par->update_cnt > 10);
+}
+
+static int __devinit auok1900fb_probe(struct platform_device *pdev)
+{
+	struct auok190x_init_data init;
+	struct auok190x_board *board;
+
+	/* pick up board specific routines */
+	board = pdev->dev.platform_data;
+	if (!board)
+		return -EINVAL;
+
+	/* fill temporary init struct for common init */
+	init.id = "auo_k1900fb";
+	init.board = board;
+	init.update_partial = auok1900fb_dpy_update_pages;
+	init.update_all = auok1900fb_dpy_update;
+	init.need_refresh = auok1900fb_need_refresh;
+	init.init = auok1900_init;
+
+	return auok190x_common_probe(pdev, &init);
+}
+
+static int __devexit auok1900fb_remove(struct platform_device *pdev)
+{
+	return auok190x_common_remove(pdev);
+}
+
+static struct platform_driver auok1900fb_driver = {
+	.probe	= auok1900fb_probe,
+	.remove = __devexit_p(auok1900fb_remove),
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "auo_k1900fb",
+		.pm = &auok190x_pm,
+	},
+};
+module_platform_driver(auok1900fb_driver);
+
+MODULE_DESCRIPTION("framebuffer driver for the AUO-K1900 EPD controller");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_LICENSE("GPL");
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH v2 2/4] video: auo_k190x: add code shared by controller drivers
From: Heiko Stübner @ 2012-04-28 10:20 UTC (permalink / raw)
  To: linux-fbdev

The AUO-K190X controllers share a very similar set of commands and
can therefore also share most of the driver code.

Signed-off-by: Heiko Stübner <heiko@sntech.de>
---
changes since v1: fix Kconfig (depends instead of select)
and catch possible runtime-pm mismatch.

 drivers/video/Kconfig       |   17 +
 drivers/video/Makefile      |    1 +
 drivers/video/auo_k190x.c   | 1046 +++++++++++++++++++++++++++++++++++++++++++
 drivers/video/auo_k190x.h   |  129 ++++++
 include/video/auo_k190xfb.h |  106 +++++
 5 files changed, 1299 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/auo_k190x.c
 create mode 100644 drivers/video/auo_k190x.h
 create mode 100644 include/video/auo_k190xfb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a290be5..31c029b 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2382,6 +2382,23 @@ config FB_BROADSHEET
 	  and could also have been called by other names when coupled with
 	  a bridge adapter.
 
+config FB_AUO_K190X
+	tristate "AUO-K190X EPD controller support"
+	depends on FB
+	select FB_SYS_FILLRECT
+	select FB_SYS_COPYAREA
+	select FB_SYS_IMAGEBLIT
+	select FB_SYS_FOPS
+	select FB_DEFERRED_IO
+	help
+	  Provides support for epaper controllers from the K190X series
+	  of AUO. These controllers can be used to drive epaper displays
+	  from Sipix.
+
+	  This option enables the common support, shared by the individual
+	  controller drivers. You will also have to enable the driver
+	  for the controller type used in your device.
+
 config FB_JZ4740
 	tristate "JZ4740 LCD framebuffer support"
 	depends on FB && MACH_JZ4740
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 9356add..d5406f2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_FB_PMAGB_B)	  += pmagb-b-fb.o
 obj-$(CONFIG_FB_MAXINE)		  += maxinefb.o
 obj-$(CONFIG_FB_METRONOME)        += metronomefb.o
 obj-$(CONFIG_FB_BROADSHEET)       += broadsheetfb.o
+obj-$(CONFIG_FB_AUO_K190X)	  += auo_k190x.o
 obj-$(CONFIG_FB_S1D13XXX)	  += s1d13xxxfb.o
 obj-$(CONFIG_FB_SH7760)		  += sh7760fb.o
 obj-$(CONFIG_FB_IMX)              += imxfb.o
diff --git a/drivers/video/auo_k190x.c b/drivers/video/auo_k190x.c
new file mode 100644
index 0000000..94ba7e7
--- /dev/null
+++ b/drivers/video/auo_k190x.c
@@ -0,0 +1,1046 @@
+/*
+ * Common code for AUO-K190X framebuffer drivers
+ *
+ * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/auo_k190xfb.h>
+
+#include "auo_k190x.h"
+
+struct panel_info {
+	int w;
+	int h;
+};
+
+/* table of panel specific parameters to be indexed into by the board drivers */
+static struct panel_info panel_table[] = {
+	/* standard 6" */
+	[AUOK190X_RESOLUTION_800_600] = {
+		.w = 800,
+		.h = 600,
+	},
+	/* standard 9" */
+	[AUOK190X_RESOLUTION_1024_768] = {
+		.w = 1024,
+		.h = 768,
+	},
+};
+
+/*
+ * private I80 interface to the board driver
+ */
+
+static void auok190x_issue_data(struct auok190xfb_par *par, u16 data)
+{
+	par->board->set_ctl(par, AUOK190X_I80_WR, 0);
+	par->board->set_hdb(par, data);
+	par->board->set_ctl(par, AUOK190X_I80_WR, 1);
+}
+
+static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data)
+{
+	par->board->set_ctl(par, AUOK190X_I80_DC, 0);
+	auok190x_issue_data(par, data);
+	par->board->set_ctl(par, AUOK190X_I80_DC, 1);
+}
+
+static int auok190x_issue_pixels(struct auok190xfb_par *par, int size,
+				 u16 *data)
+{
+	struct device *dev = par->info->device;
+	int i;
+	u16 tmp;
+
+	if (size & 3) {
+		dev_err(dev, "issue_pixels: size %d must be a multiple of 4\n",
+			size);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < (size >> 1); i++) {
+		par->board->set_ctl(par, AUOK190X_I80_WR, 0);
+
+		/* simple reduction of 8bit staticgray to 4bit gray
+		 * combines 4 * 4bit pixel values into a 16bit value
+		 */
+		tmp  = (data[2*i] & 0xF0) >> 4;
+		tmp |= (data[2*i] & 0xF000) >> 8;
+		tmp |= (data[2*i+1] & 0xF0) << 4;
+		tmp |= (data[2*i+1] & 0xF000);
+
+		par->board->set_hdb(par, tmp);
+		par->board->set_ctl(par, AUOK190X_I80_WR, 1);
+	}
+
+	return 0;
+}
+
+static u16 auok190x_read_data(struct auok190xfb_par *par)
+{
+	u16 data;
+
+	par->board->set_ctl(par, AUOK190X_I80_OE, 0);
+	data = par->board->get_hdb(par);
+	par->board->set_ctl(par, AUOK190X_I80_OE, 1);
+
+	return data;
+}
+
+/*
+ * Command interface for the controller drivers
+ */
+
+void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data)
+{
+	par->board->set_ctl(par, AUOK190X_I80_CS, 0);
+	auok190x_issue_cmd(par, data);
+	par->board->set_ctl(par, AUOK190X_I80_CS, 1);
+}
+EXPORT_SYMBOL_GPL(auok190x_send_command_nowait);
+
+void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd,
+				  int argc, u16 *argv)
+{
+	int i;
+
+	par->board->set_ctl(par, AUOK190X_I80_CS, 0);
+	auok190x_issue_cmd(par, cmd);
+
+	for (i = 0; i < argc; i++)
+		auok190x_issue_data(par, argv[i]);
+	par->board->set_ctl(par, AUOK190X_I80_CS, 1);
+}
+EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_nowait);
+
+int auok190x_send_command(struct auok190xfb_par *par, u16 data)
+{
+	int ret;
+
+	ret = par->board->wait_for_rdy(par);
+	if (ret)
+		return ret;
+
+	auok190x_send_command_nowait(par, data);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(auok190x_send_command);
+
+int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd,
+			   int argc, u16 *argv)
+{
+	int ret;
+
+	ret = par->board->wait_for_rdy(par);
+	if (ret)
+		return ret;
+
+	auok190x_send_cmdargs_nowait(par, cmd, argc, argv);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(auok190x_send_cmdargs);
+
+int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd,
+			   int argc, u16 *argv)
+{
+	int i, ret;
+
+	ret = par->board->wait_for_rdy(par);
+	if (ret)
+		return ret;
+
+	par->board->set_ctl(par, AUOK190X_I80_CS, 0);
+	auok190x_issue_cmd(par, cmd);
+
+	for (i = 0; i < argc; i++)
+		argv[i] = auok190x_read_data(par);
+	par->board->set_ctl(par, AUOK190X_I80_CS, 1);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(auok190x_read_cmdargs);
+
+void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, u16 cmd,
+				  int argc, u16 *argv, int size, u16 *data)
+{
+	int i;
+
+	par->board->set_ctl(par, AUOK190X_I80_CS, 0);
+
+	auok190x_issue_cmd(par, cmd);
+
+	for (i = 0; i < argc; i++)
+		auok190x_issue_data(par, argv[i]);
+
+	auok190x_issue_pixels(par, size, data);
+
+	par->board->set_ctl(par, AUOK190X_I80_CS, 1);
+}
+EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels_nowait);
+
+int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd,
+				  int argc, u16 *argv, int size, u16 *data)
+{
+	int ret;
+
+	ret = par->board->wait_for_rdy(par);
+	if (ret)
+		return ret;
+
+	auok190x_send_cmdargs_pixels_nowait(par, cmd, argc, argv, size, data);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels);
+
+/*
+ * fbdefio callbacks - common on both controllers.
+ */
+
+static void auok190xfb_dpy_first_io(struct fb_info *info)
+{
+	/* tell runtime-pm that we wish to use the device in a short time */
+	pm_runtime_get(info->device);
+}
+
+/* this is called back from the deferred io workqueue */
+static void auok190xfb_dpy_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct auok190xfb_par *par = info->par;
+	u16 yres = info->var.yres;
+	u16 xres = info->var.xres;
+	u16 y1 = 0, h = 0;
+	int prev_index = -1;
+	struct page *cur;
+	int h_inc;
+	int threshold;
+
+	if (!list_empty(pagelist))
+		/* the device resume should've been requested through first_io,
+		 * if the resume did not finish until now, wait for it.
+		 */
+		pm_runtime_barrier(info->device);
+	else
+		/* We reached this via the fsync or some other way.
+		 * In either case the first_io function did not run,
+		 * so we runtime_resume the device here synchronously.
+		 */
+		pm_runtime_get_sync(info->device);
+
+	/* Do a full screen update every n updates to prevent
+	 * excessive darkening of the Sipix display.
+	 * If we do this, there is no need to walk the pages.
+	 */
+	if (par->need_refresh(par)) {
+		par->update_all(par);
+		goto out;
+	}
+
+	/* height increment is fixed per page */
+	h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
+
+	/* calculate number of pages from pixel height */
+	threshold = par->consecutive_threshold / h_inc;
+	if (threshold < 1)
+		threshold = 1;
+
+	/* walk the written page list and swizzle the data */
+	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+		if (prev_index < 0) {
+			/* just starting so assign first page */
+			y1 = (cur->index << PAGE_SHIFT) / xres;
+			h = h_inc;
+		} else if ((cur->index - prev_index) <= threshold) {
+			/* page is within our threshold for single updates */
+			h += h_inc * (cur->index - prev_index);
+		} else {
+			/* page not consecutive, issue previous update first */
+			par->update_partial(par, y1, y1 + h);
+
+			/* start over with our non consecutive page */
+			y1 = (cur->index << PAGE_SHIFT) / xres;
+			h = h_inc;
+		}
+		prev_index = cur->index;
+	}
+
+	/* if we still have any pages to update we do so now */
+	if (h >= yres)
+		/* its a full screen update, just do it */
+		par->update_all(par);
+	else
+		par->update_partial(par, y1, min((u16) (y1 + h), yres));
+
+out:
+	pm_runtime_mark_last_busy(info->device);
+	pm_runtime_put_autosuspend(info->device);
+}
+
+/*
+ * framebuffer operations
+ */
+
+/*
+ * this is the slow path from userspace. they can seek and write to
+ * the fb. it's inefficient to do anything less than a full screen draw
+ */
+static ssize_t auok190xfb_write(struct fb_info *info, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct auok190xfb_par *par = info->par;
+	unsigned long p = *ppos;
+	void *dst;
+	int err = 0;
+	unsigned long total_size;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return -EPERM;
+
+	total_size = info->fix.smem_len;
+
+	if (p > total_size)
+		return -EFBIG;
+
+	if (count > total_size) {
+		err = -EFBIG;
+		count = total_size;
+	}
+
+	if (count + p > total_size) {
+		if (!err)
+			err = -ENOSPC;
+
+		count = total_size - p;
+	}
+
+	dst = (void *)(info->screen_base + p);
+
+	if (copy_from_user(dst, buf, count))
+		err = -EFAULT;
+
+	if  (!err)
+		*ppos += count;
+
+	par->update_all(par);
+
+	return (err) ? err : count;
+}
+
+static void auok190xfb_fillrect(struct fb_info *info,
+				   const struct fb_fillrect *rect)
+{
+	struct auok190xfb_par *par = info->par;
+
+	sys_fillrect(info, rect);
+
+	par->update_all(par);
+}
+
+static void auok190xfb_copyarea(struct fb_info *info,
+				   const struct fb_copyarea *area)
+{
+	struct auok190xfb_par *par = info->par;
+
+	sys_copyarea(info, area);
+
+	par->update_all(par);
+}
+
+static void auok190xfb_imageblit(struct fb_info *info,
+				const struct fb_image *image)
+{
+	struct auok190xfb_par *par = info->par;
+
+	sys_imageblit(info, image);
+
+	par->update_all(par);
+}
+
+static int auok190xfb_check_var(struct fb_var_screeninfo *var,
+				   struct fb_info *info)
+{
+	if (info->var.xres != var->xres || info->var.yres != var->yres ||
+	    info->var.xres_virtual != var->xres_virtual ||
+	    info->var.yres_virtual != var->yres_virtual) {
+		pr_info("%s: Resolution not supported: X%u x Y%u\n",
+			 __func__, var->xres, var->yres);
+		return -EINVAL;
+	}
+
+	/*
+	 *  Memory limit
+	 */
+
+	if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) {
+		pr_info("%s: Memory Limit requested yres_virtual = %u\n",
+			 __func__, var->yres_virtual);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static struct fb_ops auok190xfb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read	= fb_sys_read,
+	.fb_write	= auok190xfb_write,
+	.fb_fillrect	= auok190xfb_fillrect,
+	.fb_copyarea	= auok190xfb_copyarea,
+	.fb_imageblit	= auok190xfb_imageblit,
+	.fb_check_var	= auok190xfb_check_var,
+};
+
+/*
+ * Controller-functions common to both K1900 and K1901
+ */
+
+static int auok190x_read_temperature(struct auok190xfb_par *par)
+{
+	struct device *dev = par->info->device;
+	u16 data[4];
+	int temp;
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&(par->io_lock));
+
+	auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data);
+
+	mutex_unlock(&(par->io_lock));
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	/* sanitize and split of half-degrees for now */
+	temp = ((data[0] & AUOK190X_VERSION_TEMP_MASK) >> 1);
+
+	/* handle positive and negative temperatures */
+	if (temp >= 201)
+		return (255 - temp + 1) * (-1);
+	else
+		return temp;
+}
+
+static void auok190x_identify(struct auok190xfb_par *par)
+{
+	struct device *dev = par->info->device;
+	u16 data[4];
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&(par->io_lock));
+
+	auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data);
+
+	mutex_unlock(&(par->io_lock));
+
+	par->epd_type = data[1] & AUOK190X_VERSION_TEMP_MASK;
+
+	par->panel_size_int = AUOK190X_VERSION_SIZE_INT(data[2]);
+	par->panel_size_float = AUOK190X_VERSION_SIZE_FLOAT(data[2]);
+	par->panel_model = AUOK190X_VERSION_MODEL(data[2]);
+
+	par->tcon_version = AUOK190X_VERSION_TCON(data[3]);
+	par->lut_version = AUOK190X_VERSION_LUT(data[3]);
+
+	dev_dbg(dev, "panel %d.%din, model 0x%x, EPD 0x%x TCON-rev 0x%x, LUT-rev 0x%x",
+		par->panel_size_int, par->panel_size_float, par->panel_model,
+		par->epd_type, par->tcon_version, par->lut_version);
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+}
+
+/*
+ * Sysfs functions
+ */
+
+static ssize_t update_mode_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct auok190xfb_par *par = info->par;
+
+	return sprintf(buf, "%d\n", par->update_mode);
+}
+
+static ssize_t update_mode_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct auok190xfb_par *par = info->par;
+	int mode, ret;
+
+	ret = kstrtoint(buf, 10, &mode);
+	if (ret)
+		return ret;
+
+	par->update_mode = mode;
+
+	/* if we enter a better mode, do a full update */
+	if (par->last_mode > 1 && mode < par->last_mode)
+		par->update_all(par);
+
+	return count;
+}
+
+static ssize_t flash_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct auok190xfb_par *par = info->par;
+
+	return sprintf(buf, "%d\n", par->flash);
+}
+
+static ssize_t flash_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct auok190xfb_par *par = info->par;
+	int flash, ret;
+
+	ret = kstrtoint(buf, 10, &flash);
+	if (ret)
+		return ret;
+
+	if (flash > 0)
+		par->flash = 1;
+	else
+		par->flash = 0;
+
+	return count;
+}
+
+static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct auok190xfb_par *par = info->par;
+	int temp;
+
+	temp = auok190x_read_temperature(par);
+	return sprintf(buf, "%d\n", temp);
+}
+
+static DEVICE_ATTR(update_mode, 0644, update_mode_show, update_mode_store);
+static DEVICE_ATTR(flash, 0644, flash_show, flash_store);
+static DEVICE_ATTR(temp, 0644, temp_show, NULL);
+
+static struct attribute *auok190x_attributes[] = {
+	&dev_attr_update_mode.attr,
+	&dev_attr_flash.attr,
+	&dev_attr_temp.attr,
+	NULL
+};
+
+static const struct attribute_group auok190x_attr_group = {
+	.attrs		= auok190x_attributes,
+};
+
+static int auok190x_power(struct auok190xfb_par *par, bool on)
+{
+	struct auok190x_board *board = par->board;
+	int ret;
+
+	if (on) {
+		/* We should maintain POWER up for at least 80ms before set
+		 * RST_N and SLP_N to high (TCON spec 20100803_v35 p59)
+		 */
+		ret = regulator_enable(par->regulator);
+		if (ret)
+			return ret;
+
+		msleep(200);
+		gpio_set_value(board->gpio_nrst, 1);
+		gpio_set_value(board->gpio_nsleep, 1);
+		msleep(200);
+	} else {
+		regulator_disable(par->regulator);
+		gpio_set_value(board->gpio_nrst, 0);
+		gpio_set_value(board->gpio_nsleep, 0);
+	}
+
+	return 0;
+}
+
+/*
+ * Recovery - powercycle the controller
+ */
+
+static void auok190x_recover(struct auok190xfb_par *par)
+{
+	auok190x_power(par, 0);
+	msleep(100);
+	auok190x_power(par, 1);
+
+	par->init(par);
+
+	/* wait for init to complete */
+	par->board->wait_for_rdy(par);
+}
+
+/*
+ * Power-management
+ */
+
+#ifdef CONFIG_PM
+static int auok190x_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct auok190xfb_par *par = info->par;
+	struct auok190x_board *board = par->board;
+	u16 standby_param;
+
+	/* take and keep the lock until we are resumed, as the controller
+	 * will never reach the non-busy state when in standby mode
+	 */
+	mutex_lock(&(par->io_lock));
+
+	if (par->standby) {
+		dev_warn(dev, "already in standby, runtime-pm pairing mismatch\n");
+		mutex_unlock(&(par->io_lock));
+		return 0;
+	}
+
+	/* according to runtime_pm.txt runtime_suspend only means, that the
+	 * device will not process data and will not communicate with the CPU
+	 * As we hold the lock, this stays true even without standby
+	 */
+	if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
+		dev_dbg(dev, "runtime suspend without standby\n");
+		goto finish;
+	} else if (board->quirks & AUOK190X_QUIRK_STANDBYPARAM) {
+		/* for some TCON versions STANDBY expects a parameter (0) but
+		 * it seems the real tcon version has to be determined yet.
+		 */
+		dev_dbg(dev, "runtime suspend with additional empty param\n");
+		standby_param = 0;
+		auok190x_send_cmdargs(par, AUOK190X_CMD_STANDBY, 1,
+				      &standby_param);
+	} else {
+		dev_dbg(dev, "runtime suspend without param\n");
+		auok190x_send_command(par, AUOK190X_CMD_STANDBY);
+	}
+
+	msleep(64);
+
+finish:
+	par->standby = 1;
+
+	return 0;
+}
+
+static int auok190x_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct auok190xfb_par *par = info->par;
+	struct auok190x_board *board = par->board;
+
+	if (!par->standby) {
+		dev_warn(dev, "not in standby, runtime-pm pairing mismatch\n");
+		return 0;
+	}
+
+	if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
+		dev_dbg(dev, "runtime resume without standby\n");
+	} else {
+		/* when in standby, controller is always busy
+		 * and only accepts the wakeup command
+		 */
+		dev_dbg(dev, "runtime resume from standby\n");
+		auok190x_send_command_nowait(par, AUOK190X_CMD_WAKEUP);
+
+		msleep(160);
+
+		/* wait for the controller to be ready and release the lock */
+		board->wait_for_rdy(par);
+	}
+
+	par->standby = 0;
+
+	mutex_unlock(&(par->io_lock));
+
+	return 0;
+}
+
+static int auok190x_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct auok190xfb_par *par = info->par;
+	struct auok190x_board *board = par->board;
+	int ret;
+
+	dev_dbg(dev, "suspend\n");
+	if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
+		/* suspend via powering off the ic */
+		dev_dbg(dev, "suspend with broken standby\n");
+
+		auok190x_power(par, 0);
+	} else {
+		dev_dbg(dev, "suspend using sleep\n");
+
+		/* the sleep state can only be entered from the standby state.
+		 * pm_runtime_get_noresume gets called before the suspend call.
+		 * So the devices usage count is >0 but it is not necessarily
+		 * active.
+		 */
+		if (!pm_runtime_status_suspended(dev)) {
+			ret = auok190x_runtime_suspend(dev);
+			if (ret < 0) {
+				dev_err(dev, "auok190x_runtime_suspend failed with %d\n",
+					ret);
+				return ret;
+			}
+			par->manual_standby = 1;
+		}
+
+		gpio_direction_output(board->gpio_nsleep, 0);
+	}
+
+	msleep(100);
+
+	return 0;
+}
+
+static int auok190x_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct auok190xfb_par *par = info->par;
+	struct auok190x_board *board = par->board;
+
+	dev_dbg(dev, "resume\n");
+	if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) {
+		dev_dbg(dev, "resume with broken standby\n");
+
+		auok190x_power(par, 1);
+
+		par->init(par);
+	} else {
+		dev_dbg(dev, "resume from sleep\n");
+
+		/* device should be in runtime suspend when we were suspended
+		 * and pm_runtime_put_sync gets called after this function.
+		 * So there is no need to touch the standby mode here at all.
+		 */
+		gpio_direction_output(board->gpio_nsleep, 1);
+		msleep(100);
+
+		/* an additional init call seems to be necessary after sleep */
+		auok190x_runtime_resume(dev);
+		par->init(par);
+
+		/* if we were runtime-suspended before, suspend again*/
+		if (!par->manual_standby)
+			auok190x_runtime_suspend(dev);
+		else
+			par->manual_standby = 0;
+	}
+
+	return 0;
+}
+#endif
+
+const struct dev_pm_ops auok190x_pm = {
+	SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume,
+			   NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(auok190x_suspend, auok190x_resume)
+};
+EXPORT_SYMBOL_GPL(auok190x_pm);
+
+/*
+ * Common probe and remove code
+ */
+
+int __devinit auok190x_common_probe(struct platform_device *pdev,
+				    struct auok190x_init_data *init)
+{
+	struct auok190x_board *board = init->board;
+	struct auok190xfb_par *par;
+	struct fb_info *info;
+	struct panel_info *panel;
+	int videomemorysize, ret;
+	unsigned char *videomemory;
+
+	/* check board contents */
+	if (!board->init || !board->cleanup || !board->wait_for_rdy
+	    || !board->set_ctl || !board->set_hdb || !board->get_hdb
+	    || !board->setup_irq)
+		return -EINVAL;
+
+	info = framebuffer_alloc(sizeof(struct auok190xfb_par), &pdev->dev);
+	if (!info)
+		return -ENOMEM;
+
+	par = info->par;
+	par->info = info;
+	par->board = board;
+	par->recover = auok190x_recover;
+	par->update_partial = init->update_partial;
+	par->update_all = init->update_all;
+	par->need_refresh = init->need_refresh;
+	par->init = init->init;
+
+	/* init update modes */
+	par->update_cnt = 0;
+	par->update_mode = -1;
+	par->last_mode = -1;
+	par->flash = 0;
+
+	par->regulator = regulator_get(info->device, "vdd");
+	if (IS_ERR(par->regulator)) {
+		ret = PTR_ERR(par->regulator);
+		dev_err(info->device, "Failed to get regulator: %d\n", ret);
+		goto err_reg;
+	}
+
+	ret = board->init(par);
+	if (ret) {
+		dev_err(info->device, "board init failed, %d\n", ret);
+		goto err_board;
+	}
+
+	ret = gpio_request(board->gpio_nsleep, "AUOK190x sleep");
+	if (ret) {
+		dev_err(info->device, "could not request sleep gpio, %d\n",
+			ret);
+		goto err_gpio1;
+	}
+
+	ret = gpio_direction_output(board->gpio_nsleep, 0);
+	if (ret) {
+		dev_err(info->device, "could not set sleep gpio, %d\n", ret);
+		goto err_gpio2;
+	}
+
+	ret = gpio_request(board->gpio_nrst, "AUOK190x reset");
+	if (ret) {
+		dev_err(info->device, "could not request reset gpio, %d\n",
+			ret);
+		goto err_gpio2;
+	}
+
+	ret = gpio_direction_output(board->gpio_nrst, 0);
+	if (ret) {
+		dev_err(info->device, "could not set reset gpio, %d\n", ret);
+		goto err_gpio3;
+	}
+
+	ret = auok190x_power(par, 1);
+	if (ret) {
+		dev_err(info->device, "could not power on the device, %d\n",
+			ret);
+		goto err_gpio3;
+	}
+
+	mutex_init(&par->io_lock);
+
+	init_waitqueue_head(&par->waitq);
+
+	ret = par->board->setup_irq(par->info);
+	if (ret) {
+		dev_err(info->device, "could not setup ready-irq, %d\n", ret);
+		goto err_irq;
+	}
+
+	/* wait for init to complete */
+	par->board->wait_for_rdy(par);
+
+	/*
+	 * From here on the controller can talk to us
+	 */
+
+	/* initialise fix, var, resolution and rotation */
+
+	strlcpy(info->fix.id, init->id, 16);
+	info->fix.type = FB_TYPE_PACKED_PIXELS;
+	info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+	info->fix.xpanstep = 0;
+	info->fix.ypanstep = 0;
+	info->fix.ywrapstep = 0;
+	info->fix.accel = FB_ACCEL_NONE;
+
+	info->var.bits_per_pixel = 8;
+	info->var.grayscale = 1;
+	info->var.red.length = 8;
+	info->var.green.length = 8;
+	info->var.blue.length = 8;
+
+	panel = &panel_table[board->resolution];
+
+	/* if 90 degree rotation, switch width and height */
+	if (board->rotation & 1) {
+		info->var.xres = panel->h;
+		info->var.yres = panel->w;
+		info->var.xres_virtual = panel->h;
+		info->var.yres_virtual = panel->w;
+		info->fix.line_length = panel->h;
+	} else {
+		info->var.xres = panel->w;
+		info->var.yres = panel->h;
+		info->var.xres_virtual = panel->w;
+		info->var.yres_virtual = panel->h;
+		info->fix.line_length = panel->w;
+	}
+
+	par->resolution = board->resolution;
+	par->rotation = board->rotation;
+
+	/* videomemory handling */
+
+	videomemorysize = roundup((panel->w * panel->h), PAGE_SIZE);
+	videomemory = vmalloc(videomemorysize);
+	if (!videomemory) {
+		ret = -ENOMEM;
+		goto err_irq;
+	}
+
+	memset(videomemory, 0, videomemorysize);
+	info->screen_base = (char *)videomemory;
+	info->fix.smem_len = videomemorysize;
+
+	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
+	info->fbops = &auok190xfb_ops;
+
+	/* deferred io init */
+
+	info->fbdefio = devm_kzalloc(info->device,
+				     sizeof(struct fb_deferred_io),
+				     GFP_KERNEL);
+	if (!info->fbdefio) {
+		dev_err(info->device, "Failed to allocate memory\n");
+		ret = -ENOMEM;
+		goto err_defio;
+	}
+
+	dev_dbg(info->device, "targetting %d frames per second\n", board->fps);
+	info->fbdefio->delay = HZ / board->fps;
+	info->fbdefio->first_io = auok190xfb_dpy_first_io,
+	info->fbdefio->deferred_io = auok190xfb_dpy_deferred_io,
+	fb_deferred_io_init(info);
+
+	/* color map */
+
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret < 0) {
+		dev_err(info->device, "Failed to allocate colormap\n");
+		goto err_cmap;
+	}
+
+	/* controller init */
+
+	par->consecutive_threshold = 100;
+	par->init(par);
+	auok190x_identify(par);
+
+	platform_set_drvdata(pdev, info);
+
+	ret = register_framebuffer(info);
+	if (ret < 0)
+		goto err_regfb;
+
+	ret = sysfs_create_group(&info->device->kobj, &auok190x_attr_group);
+	if (ret)
+		goto err_sysfs;
+
+	dev_info(info->device, "fb%d: %dx%d using %dK of video memory\n",
+		 info->node, info->var.xres, info->var.yres,
+		 videomemorysize >> 10);
+
+	/* increase autosuspend_delay when we use alternative methods
+	 * for runtime_pm
+	 */
+	par->autosuspend_delay = (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN)
+					? 1000 : 200;
+
+	pm_runtime_set_active(info->device);
+	pm_runtime_enable(info->device);
+	pm_runtime_set_autosuspend_delay(info->device, par->autosuspend_delay);
+	pm_runtime_use_autosuspend(info->device);
+
+	return 0;
+
+err_sysfs:
+	unregister_framebuffer(info);
+err_regfb:
+	fb_dealloc_cmap(&info->cmap);
+err_cmap:
+	fb_deferred_io_cleanup(info);
+	kfree(info->fbdefio);
+err_defio:
+	vfree((void *)info->screen_base);
+err_irq:
+	auok190x_power(par, 0);
+err_gpio3:
+	gpio_free(board->gpio_nrst);
+err_gpio2:
+	gpio_free(board->gpio_nsleep);
+err_gpio1:
+	board->cleanup(par);
+err_board:
+	regulator_put(par->regulator);
+err_reg:
+	framebuffer_release(info);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(auok190x_common_probe);
+
+int  __devexit auok190x_common_remove(struct platform_device *pdev)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct auok190xfb_par *par = info->par;
+	struct auok190x_board *board = par->board;
+
+	pm_runtime_disable(info->device);
+
+	sysfs_remove_group(&info->device->kobj, &auok190x_attr_group);
+
+	unregister_framebuffer(info);
+
+	fb_dealloc_cmap(&info->cmap);
+
+	fb_deferred_io_cleanup(info);
+	kfree(info->fbdefio);
+
+	vfree((void *)info->screen_base);
+
+	auok190x_power(par, 0);
+
+	gpio_free(board->gpio_nrst);
+	gpio_free(board->gpio_nsleep);
+
+	board->cleanup(par);
+
+	regulator_put(par->regulator);
+
+	framebuffer_release(info);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(auok190x_common_remove);
+
+MODULE_DESCRIPTION("Common code for AUO-K190X controllers");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/auo_k190x.h b/drivers/video/auo_k190x.h
new file mode 100644
index 0000000..e35af1f
--- /dev/null
+++ b/drivers/video/auo_k190x.h
@@ -0,0 +1,129 @@
+/*
+ * Private common definitions for AUO-K190X framebuffer drivers
+ *
+ * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * I80 interface specific defines
+ */
+
+#define AUOK190X_I80_CS			0x01
+#define AUOK190X_I80_DC			0x02
+#define AUOK190X_I80_WR			0x03
+#define AUOK190X_I80_OE			0x04
+
+/*
+ * AUOK190x commands, common to both controllers
+ */
+
+#define AUOK190X_CMD_INIT		0x0000
+#define AUOK190X_CMD_STANDBY		0x0001
+#define AUOK190X_CMD_WAKEUP		0x0002
+#define AUOK190X_CMD_TCON_RESET		0x0003
+#define AUOK190X_CMD_DATA_STOP		0x1002
+#define AUOK190X_CMD_LUT_START		0x1003
+#define AUOK190X_CMD_DISP_REFRESH	0x1004
+#define AUOK190X_CMD_DISP_RESET		0x1005
+#define AUOK190X_CMD_PRE_DISPLAY_START	0x100D
+#define AUOK190X_CMD_PRE_DISPLAY_STOP	0x100F
+#define AUOK190X_CMD_FLASH_W		0x2000
+#define AUOK190X_CMD_FLASH_E		0x2001
+#define AUOK190X_CMD_FLASH_STS		0x2002
+#define AUOK190X_CMD_FRAMERATE		0x3000
+#define AUOK190X_CMD_READ_VERSION	0x4000
+#define AUOK190X_CMD_READ_STATUS	0x4001
+#define AUOK190X_CMD_READ_LUT		0x4003
+#define AUOK190X_CMD_DRIVERTIMING	0x5000
+#define AUOK190X_CMD_LBALANCE		0x5001
+#define AUOK190X_CMD_AGINGMODE		0x6000
+#define AUOK190X_CMD_AGINGEXIT		0x6001
+
+/*
+ * Common settings for AUOK190X_CMD_INIT
+ */
+
+#define AUOK190X_INIT_DATA_FILTER	(0 << 12)
+#define AUOK190X_INIT_DATA_BYPASS	(1 << 12)
+#define AUOK190X_INIT_INVERSE_WHITE	(0 << 9)
+#define AUOK190X_INIT_INVERSE_BLACK	(1 << 9)
+#define AUOK190X_INIT_SCAN_DOWN		(0 << 1)
+#define AUOK190X_INIT_SCAN_UP		(1 << 1)
+#define AUOK190X_INIT_SHIFT_LEFT	(0 << 0)
+#define AUOK190X_INIT_SHIFT_RIGHT	(1 << 0)
+
+/* Common bits to pixels
+ *   Mode	15-12	11-8	7-4	3-0
+ *   format0	4	3	2	1
+ *   format1	3	4	1	2
+ */
+
+#define AUOK190X_INIT_FORMAT0		0
+#define AUOK190X_INIT_FORMAT1		(1 << 6)
+
+/*
+ * settings for AUOK190X_CMD_RESET
+ */
+
+#define AUOK190X_RESET_TCON		(0 << 0)
+#define AUOK190X_RESET_NORMAL		(1 << 0)
+#define AUOK190X_RESET_PON		(1 << 1)
+
+/*
+ * AUOK190X_CMD_VERSION
+ */
+
+#define AUOK190X_VERSION_TEMP_MASK		(0x1ff)
+#define AUOK190X_VERSION_EPD_MASK		(0xff)
+#define AUOK190X_VERSION_SIZE_INT(_val)		((_val & 0xfc00) >> 10)
+#define AUOK190X_VERSION_SIZE_FLOAT(_val)	((_val & 0x3c0) >> 6)
+#define AUOK190X_VERSION_MODEL(_val)		(_val & 0x3f)
+#define AUOK190X_VERSION_LUT(_val)		(_val & 0xff)
+#define AUOK190X_VERSION_TCON(_val)		((_val & 0xff00) >> 8)
+
+/*
+ * update modes for CMD_PARTIALDISP on K1900 and CMD_DDMA on K1901
+ */
+
+#define AUOK190X_UPDATE_MODE(_res)		((_res & 0x7) << 12)
+#define AUOK190X_UPDATE_NONFLASH		(1 << 15)
+
+/*
+ * track panel specific parameters for common init
+ */
+
+struct auok190x_init_data {
+	char *id;
+	struct auok190x_board *board;
+
+	void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2);
+	void (*update_all)(struct auok190xfb_par *par);
+	bool (*need_refresh)(struct auok190xfb_par *par);
+	void (*init)(struct auok190xfb_par *par);
+};
+
+
+extern void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data);
+extern int auok190x_send_command(struct auok190xfb_par *par, u16 data);
+extern void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd,
+					 int argc, u16 *argv);
+extern int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd,
+				  int argc, u16 *argv);
+extern void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par,
+						u16 cmd, int argc, u16 *argv,
+						int size, u16 *data);
+extern int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd,
+					int argc, u16 *argv, int size,
+					u16 *data);
+extern int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd,
+				  int argc, u16 *argv);
+
+extern int auok190x_common_probe(struct platform_device *pdev,
+				 struct auok190x_init_data *init);
+extern int auok190x_common_remove(struct platform_device *pdev);
+
+extern const struct dev_pm_ops auok190x_pm;
diff --git a/include/video/auo_k190xfb.h b/include/video/auo_k190xfb.h
new file mode 100644
index 0000000..609efe8
--- /dev/null
+++ b/include/video/auo_k190xfb.h
@@ -0,0 +1,106 @@
+/*
+ * Definitions for AUO-K190X framebuffer drivers
+ *
+ * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_VIDEO_AUO_K190XFB_H_
+#define _LINUX_VIDEO_AUO_K190XFB_H_
+
+/* Controller standby command needs a param */
+#define AUOK190X_QUIRK_STANDBYPARAM	(1 << 0)
+
+/* Controller standby is completely broken */
+#define AUOK190X_QUIRK_STANDBYBROKEN	(1 << 1)
+
+/*
+ * Resolutions for the displays
+ */
+#define AUOK190X_RESOLUTION_800_600		0
+#define AUOK190X_RESOLUTION_1024_768		1
+
+/*
+ * struct used by auok190x. board specific stuff comes from *board
+ */
+struct auok190xfb_par {
+	struct fb_info *info;
+	struct auok190x_board *board;
+
+	struct regulator *regulator;
+
+	struct mutex io_lock;
+	struct delayed_work work;
+	wait_queue_head_t waitq;
+	int resolution;
+	int rotation;
+	int consecutive_threshold;
+	int update_cnt;
+
+	/* panel and controller informations */
+	int epd_type;
+	int panel_size_int;
+	int panel_size_float;
+	int panel_model;
+	int tcon_version;
+	int lut_version;
+
+	/* individual controller callbacks */
+	void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2);
+	void (*update_all)(struct auok190xfb_par *par);
+	bool (*need_refresh)(struct auok190xfb_par *par);
+	void (*init)(struct auok190xfb_par *par);
+	void (*recover)(struct auok190xfb_par *par);
+
+	int update_mode; /* mode to use for updates */
+	int last_mode; /* update mode last used */
+	int flash;
+
+	/* power management */
+	int autosuspend_delay;
+	bool standby;
+	bool manual_standby;
+};
+
+/**
+ * Board specific platform-data
+ * @init:		initialize the controller interface
+ * @cleanup:		cleanup the controller interface
+ * @wait_for_rdy:	wait until the controller is not busy anymore
+ * @set_ctl:		change an interface control
+ * @set_hdb:		write a value to the data register
+ * @get_hdb:		read a value from the data register
+ * @setup_irq:		method to setup the irq handling on the busy gpio
+ * @gpio_nsleep:	sleep gpio
+ * @gpio_nrst:		reset gpio
+ * @gpio_nbusy:		busy gpio
+ * @resolution:		one of the AUOK190X_RESOLUTION constants
+ * @rotation:		rotation of the framebuffer
+ * @quirks:		controller quirks to honor
+ * @fps:		frames per second for defio
+ */
+struct auok190x_board {
+	int (*init)(struct auok190xfb_par *);
+	void (*cleanup)(struct auok190xfb_par *);
+	int (*wait_for_rdy)(struct auok190xfb_par *);
+
+	void (*set_ctl)(struct auok190xfb_par *, unsigned char, u8);
+	void (*set_hdb)(struct auok190xfb_par *, u16);
+	u16 (*get_hdb)(struct auok190xfb_par *);
+
+	int (*setup_irq)(struct fb_info *);
+
+	int gpio_nsleep;
+	int gpio_nrst;
+	int gpio_nbusy;
+
+	int resolution;
+	int rotation;
+	int quirks;
+	int fps;
+};
+
+#endif
-- 
1.7.5.4


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox