linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] eink_apollofb: new driver for Apollo eInk controller.
@ 2008-07-07 13:09 Yauhen Kharuzhy
  0 siblings, 0 replies; 13+ messages in thread
From: Yauhen Kharuzhy @ 2008-07-07 13:09 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Antonino Daplas

Add a new driver for eInk Apollo controller. This
controller is used in various bookreaders with eInk
displays.

The eink_apollofb driver supports many features of
Apollo chip, in difference with the hecubafb driver:
2-bit color depth, partial picture loading, flash
read/write.

The driver uses platform_device framework for
platform-specific functions (set values of control
lines, read/write data from/to bus etc.) and can be
used on any platform.

This driver have been developed for the OpenInkpot
project (http://openinkpot.org/).

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/video/Kconfig         |   17 +
 drivers/video/Makefile        |    1 +
 drivers/video/eink_apollofb.c | 1046 +++++++++++++++++++++++++++++++++++++++++
 include/linux/eink_apollofb.h |   60 +++
 4 files changed, 1124 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/eink_apollofb.c
 create mode 100644 include/linux/eink_apollofb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index e0c5f96..ad23464 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -718,6 +718,23 @@ config FB_N411
          This enables support for the Apollo display controller in its
          Hecuba form using the n411 devkit.
 
+config FB_EINK_APOLLO
+       tristate "Apollo eInk display controller support"
+       depends on EXPERIMENTAL && FB && MMU
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       select FB_SYS_FOPS
+       select FB_DEFERRED_IO
+       help
+         This enables support for Apollo eInk display controller.
+	 Includes support of deferred IO and 2-bit grayscale mode.
+
+	 Hardware-specific functions have been moved to platform data.
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called eink_apollofb.
+
 config FB_HGA
 	tristate "Hercules mono graphics support"
 	depends on FB && X86
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 04bca35..2530f0c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_FB_ATARI)            += atafb.o c2p.o atafb_mfb.o \
                                      atafb_iplan2p2.o atafb_iplan2p4.o atafb_iplan2p8.o
 obj-$(CONFIG_FB_MAC)              += macfb.o
 obj-$(CONFIG_FB_HECUBA)           += hecubafb.o
+obj-$(CONFIG_FB_EINK_APOLLO)      += eink_apollofb.o
 obj-$(CONFIG_FB_HGA)              += hgafb.o
 obj-$(CONFIG_FB_XVR500)           += sunxvr500.o
 obj-$(CONFIG_FB_XVR2500)          += sunxvr2500.o
diff --git a/drivers/video/eink_apollofb.c b/drivers/video/eink_apollofb.c
new file mode 100644
index 0000000..290b68f
--- /dev/null
+++ b/drivers/video/eink_apollofb.c
@@ -0,0 +1,1046 @@
+/*
+ * linux/drivers/video/apollofb.c -- FB driver for lBook/Jinke eReader V3
+ *
+ * Copyright (C) 2007, Yauhen Kharuzhy
+ * This driver is part of openinkpot.org project
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * This driver based on hecubafb driver code.
+ *
+ */
+
+#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/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/eink_apollofb.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/arch/regs-gpio.h>
+#include <asm/hardware.h>
+
+/* Display specific information */
+#define DPY_W 600
+#define DPY_H 800
+
+#define is_portrait(var) (!(var.rotate % 180))
+
+struct apollofb_options {
+	unsigned int manual_refresh:1;
+	unsigned int partial_update:1;
+	unsigned int use_sleep_mode:1;
+};
+
+struct apollofb_par {
+	struct fb_info *info;
+	struct mutex lock;
+	struct delayed_work deferred_work;
+	struct cdev cdev;
+	struct apollofb_options options;
+	int current_mode;
+	struct eink_apollo_operations *ops;
+	int standby;
+};
+
+static struct fb_fix_screeninfo apollofb_fix __devinitdata = {
+	.id =		"apollofb",
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_STATIC_PSEUDOCOLOR,
+	.xpanstep =	0,
+	.ypanstep =	0,
+	.ywrapstep =	0,
+	.line_length =	DPY_W / (8 / 8),
+	.accel =	FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo apollofb_var __devinitdata = {
+	.xres		= DPY_W,
+	.yres		= DPY_H,
+	.xres_virtual	= DPY_W,
+	.yres_virtual	= DPY_H,
+	.red		= {0, 2, 0},
+	.blue		= {0, 2, 0},
+	.green		= {0, 2, 0},
+	.grayscale	= 1,
+	.bits_per_pixel	= 8,
+	.nonstd		= 1,
+};
+
+static inline int apollo_wait_for_ack_value(struct apollofb_par *par,
+		unsigned int value)
+{
+	unsigned long timeout = jiffies + 2 * HZ;
+
+	while ((par->ops->get_ctl_pin(H_ACK) != value) &&
+			time_before(jiffies, timeout))
+		schedule();
+
+	if (par->ops->get_ctl_pin(H_ACK) != value) {
+		printk(KERN_ERR "%s: Wait for H_ACK == %u, timeout\n",
+				__func__, value);
+		return 1;
+	}
+
+	return 0;
+}
+
+#define apollo_wait_for_ack(par)	apollo_wait_for_ack_value(par, 0)
+#define apollo_wait_for_ack_clear(par)	apollo_wait_for_ack_value(par, 1)
+
+static void apollo_send_data(struct apollofb_par *par, unsigned char data)
+{
+	par->ops->write_value(data);
+	par->ops->set_ctl_pin(H_DS, 0);
+	apollo_wait_for_ack(par);
+	par->ops->set_ctl_pin(H_DS, 1);
+	apollo_wait_for_ack_clear(par);
+}
+
+
+static void apollo_send_command(struct apollofb_par *par, unsigned char cmd)
+{
+	par->ops->set_ctl_pin(H_CD, 1);
+	apollo_send_data(par, cmd);
+	par->ops->set_ctl_pin(H_CD, 0);
+}
+
+static unsigned char apollo_read_data(struct apollofb_par *par)
+{
+	unsigned char res;
+
+	par->ops->set_ctl_pin(H_RW, 1);
+	par->ops->set_ctl_pin(H_DS, 0);
+	apollo_wait_for_ack(par);
+	res = par->ops->read_value();
+	par->ops->set_ctl_pin(H_DS, 1);
+	apollo_wait_for_ack_clear(par);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	return res;
+}
+
+static unsigned char apollo_get_status(struct apollofb_par *par)
+{
+	unsigned char res;
+
+	apollo_send_command(par, APOLLO_GET_STATUS);
+	res = apollo_read_data(par);
+	return res;
+}
+
+static void apollo_set_sleep_mode(struct apollofb_par *par)
+{
+	apollo_send_command(par, APOLLO_SLEEP_MODE);
+	par->current_mode = APOLLO_STATUS_MODE_SLEEP;
+}
+
+static void apollo_set_normal_mode(struct apollofb_par *par)
+{
+	par->ops->set_ctl_pin(H_CD, 0);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	apollo_send_command(par, APOLLO_NORMAL_MODE);
+	apollo_send_command(par, APOLLO_ORIENTATION);
+	apollo_send_data(par, ((par->info->var.rotate + 90) % 360) / 90);
+
+	par->current_mode = APOLLO_STATUS_MODE_NORMAL;
+}
+
+static void apollo_wakeup(struct apollofb_par *par)
+{
+	udelay(600); /* in case we were just powered off */
+	par->ops->set_ctl_pin(H_WUP, 1);
+	udelay(100);
+	par->ops->set_ctl_pin(H_DS, 0);
+	apollo_wait_for_ack(par);
+	par->ops->set_ctl_pin(H_WUP, 0);
+	par->ops->set_ctl_pin(H_DS, 1);
+	apollo_wait_for_ack_clear(par);
+}
+
+
+/* main apollofb functions */
+
+static void apollofb_dpy_update(struct apollofb_par *par)
+{
+	unsigned char *buf = (unsigned char __force *)par->info->screen_base;
+	int count = par->info->fix.smem_len;
+	int bpp = par->info->var.green.length;
+	unsigned char tmp, mask;
+	unsigned int i, k;
+	unsigned int pixels_in_byte = 8 / bpp;
+
+	cancel_delayed_work(&par->deferred_work);
+
+	mask = 0;
+	for (i = 0; i < bpp; i++)
+		mask = (mask << 1) | 1;
+
+	mutex_lock(&par->lock);
+
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (par->options.manual_refresh)
+		apollo_send_command(par, APOLLO_MANUAL_REFRESH);
+
+	apollo_send_command(par, APOLLO_LOAD_PICTURE);
+
+	k = 0;
+	tmp = 0;
+	for (i = 0; i < count; i++) {
+		tmp = (tmp << bpp) | (buf[i] & mask);
+		k++;
+		if (k % pixels_in_byte == 0)
+			apollo_send_data(par, tmp);
+	}
+
+	apollo_send_command(par, APOLLO_STOP_LOADING);
+	apollo_send_command(par, APOLLO_DISPLAY_PICTURE);
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+
+	mutex_unlock(&par->lock);
+}
+
+/*
+ * x1 must be less than x2, y1 < y2
+ */
+static void apollofb_apollo_update_part(struct apollofb_par *par,
+		unsigned int x1, unsigned int y1,
+		unsigned int x2, unsigned int y2)
+{
+	int i, j, k;
+	struct fb_info *info = par->info;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int bpp = info->var.green.length;
+	unsigned int pixels_in_byte = 8 / bpp;
+	unsigned char *buf = (unsigned char __force *)info->screen_base;
+	unsigned char tmp, mask;
+
+	y1 -= y1 % 4;
+
+	if ((y2 + 1) % 4)
+		y2 += 4 - ((y2 + 1) % 4);
+
+	x1 -= x1 % 4;
+
+	if ((x2 + 1) % 4)
+		x2 += 4 - ((x2 + 1) % 4);
+
+	mask = 0;
+	for (i = 0; i < bpp; i++)
+		mask = (mask << 1) | 1;
+
+	mutex_lock(&par->lock);
+
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (par->options.manual_refresh)
+		apollo_send_command(par, APOLLO_MANUAL_REFRESH);
+
+	apollo_send_command(par, APOLLO_LOAD_PARTIAL_PICTURE);
+	apollo_send_data(par, (x1 >> 8) & 0xff);
+	apollo_send_data(par, x1 & 0xff);
+	apollo_send_data(par, (y1 >> 8) & 0xff);
+	apollo_send_data(par, y1 & 0xff);
+	apollo_send_data(par, (x2 >> 8) & 0xff);
+	apollo_send_data(par, x2 & 0xff);
+	apollo_send_data(par, (y2 >> 8) & 0xff);
+	apollo_send_data(par, y2 & 0xff);
+
+	k = 0;
+	tmp = 0;
+	for (i = y1; i <= y2; i++)
+		for (j = x1; j <= x2; j++) {
+			tmp = (tmp << bpp) | (buf[i * width + j] & mask);
+			k++;
+			if (k % pixels_in_byte == 0)
+				apollo_send_data(par, tmp);
+		}
+
+	apollo_send_command(par, APOLLO_STOP_LOADING);
+	apollo_send_command(par, APOLLO_DISPLAY_PARTIAL_PICTURE);
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+
+	mutex_unlock(&par->lock);
+}
+
+/* this is called back from the deferred io workqueue */
+static void apollofb_dpy_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+
+	struct apollofb_par *par = info->par;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int height = is_portrait(info->var) ? info->var.yres :
+							info->var.xres;
+	unsigned long int start_page = -1, end_page = -1;
+	unsigned int y1 = 0, y2 = 0;
+	struct page *cur;
+
+
+	if (par->options.partial_update) {
+		list_for_each_entry(cur, pagelist, lru) {
+			if (start_page == -1) {
+				start_page = cur->index;
+				end_page = cur->index;
+				continue;
+			}
+
+			if (cur->index == end_page + 1) {
+				end_page++;
+			} else {
+				y1 = start_page * PAGE_SIZE / width;
+				y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
+				if (y2 >= height)
+					y2 = height - 1;
+
+				apollofb_apollo_update_part(par,
+						0, y1,
+						width - 1, y2);
+
+				start_page = cur->index;
+				end_page = cur->index;
+			}
+		}
+
+		y1 = start_page * PAGE_SIZE / width;
+		y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
+		if (y2 >= height)
+			y2 = height - 1;
+
+		apollofb_apollo_update_part(par, 0, y1,	width - 1, y2);
+	} else {
+		apollofb_dpy_update(par);
+	}
+}
+
+static void apollofb_deferred_work(struct work_struct *work)
+{
+	struct apollofb_par *par = container_of(work, struct apollofb_par,
+						deferred_work.work);
+
+	apollofb_dpy_update(par);
+}
+
+static void apollofb_fillrect(struct fb_info *info,
+				   const struct fb_fillrect *rect)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_fillrect(info, rect);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+static void apollofb_copyarea(struct fb_info *info,
+				   const struct fb_copyarea *area)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_copyarea(info, area);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+static void apollofb_imageblit(struct fb_info *info,
+				const struct fb_image *image)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_imageblit(info, image);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+int apollofb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	return 0;
+}
+
+/*
+ * 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 apollofb_write(struct fb_info *info, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long p;
+	int err = -EINVAL;
+	struct apollofb_par *par;
+	unsigned int xres;
+	unsigned int fbmemlength;
+
+	p = *ppos;
+	par = info->par;
+	xres = info->var.xres;
+	fbmemlength = (xres * info->var.yres)/8 * info->var.bits_per_pixel;
+
+	if (p > fbmemlength)
+		return -ENOSPC;
+
+	err = 0;
+	if ((count + p) > fbmemlength) {
+		count = fbmemlength - p;
+		err = -ENOSPC;
+	}
+
+	if (count) {
+		char *base_addr;
+
+		base_addr = (char __force *)info->screen_base;
+		count -= copy_from_user(base_addr + p, buf, count);
+		*ppos += count;
+		err = -EFAULT;
+	}
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+
+	if (count)
+		return count;
+
+	return err;
+}
+
+static int apollofb_sync(struct fb_info *info)
+{
+	return 0;
+}
+
+static int apollofb_check_var(struct fb_var_screeninfo *var,
+			      struct fb_info *info)
+{
+	switch (var->bits_per_pixel) {
+	case 1:
+		var->red.length = 1;
+		var->red.offset = 0;
+		var->green.length = 1;
+		var->green.offset = 0;
+		var->blue.length = 1;
+		var->blue.offset = 0;
+		var->transp.length = 0;
+		var->transp.offset = 0;
+		break;
+	default:
+		var->bits_per_pixel = 8;
+		var->grayscale = 1;
+		var->red.length = 2;
+		var->red.offset = 0;
+		var->green.length = 2;
+		var->green.offset = 0;
+		var->blue.length = 2;
+		var->blue.offset = 0;
+		var->transp.length = 0;
+		var->transp.offset = 0;
+		break;
+	}
+
+	var->xres = DPY_W;
+	var->xres_virtual = DPY_W;
+	var->yres = DPY_H;
+	var->yres_virtual = DPY_H;
+
+	if (var->rotate % 90)
+		var->rotate -= var->rotate % 90;
+
+	return 0;
+}
+
+static int apollofb_set_par(struct fb_info *info)
+{
+	struct apollofb_par *par = info->par;
+
+	switch (info->var.bits_per_pixel) {
+	case 1:
+		info->fix.visual = FB_VISUAL_MONO01;
+		apollo_send_command(par, APOLLO_SET_DEPTH);
+		apollo_send_data(par, 0x00);
+		break;
+	default:
+		info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+		apollo_send_command(par, APOLLO_SET_DEPTH);
+		apollo_send_data(par, 0x02);
+		break;
+	}
+
+	mutex_lock(&par->lock);
+	apollo_send_command(par, APOLLO_ORIENTATION);
+	apollo_send_data(par, ((info->var.rotate + 90) % 360) / 90);
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+
+static ssize_t apollofb_wf_read(struct file *f, char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	unsigned char data;
+	char __user *p = buf;
+	int i;
+	struct apollofb_par *par = f->private_data;
+
+	mutex_lock(&par->lock);
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (*f_pos > APOLLO_WAVEFORMS_FLASH_SIZE - 1)
+		return 0;
+
+	if (*f_pos + count > APOLLO_WAVEFORMS_FLASH_SIZE)
+		count = APOLLO_WAVEFORMS_FLASH_SIZE - *f_pos;
+
+	for (i = *f_pos; i < *f_pos + count; i++) {
+		apollo_send_command(par, APOLLO_READ_FROM_FLASH);
+
+		apollo_send_data(par, (i >> 16) & 0xff);
+		apollo_send_data(par, (i >> 8) & 0xff);
+		apollo_send_data(par, i & 0xff);
+
+		data = apollo_read_data(par);
+
+		if (copy_to_user(p, &data, 1))
+			return -EFAULT;
+
+		p++;
+	}
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	*f_pos += count;
+	return count;
+}
+
+static ssize_t apollofb_wf_write(struct file *f, const char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	unsigned char data;
+	const char __user *p = buf;
+	int i;
+	struct apollofb_par *par = f->private_data;
+
+	mutex_lock(&par->lock);
+
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (*f_pos > APOLLO_WAVEFORMS_FLASH_SIZE - 1)
+		return 0;
+
+	if (*f_pos + count > APOLLO_WAVEFORMS_FLASH_SIZE)
+		count = APOLLO_WAVEFORMS_FLASH_SIZE - *f_pos;
+
+	for (i = *f_pos; i < *f_pos + count; i++) {
+		if (copy_to_user(&data, p, 1))
+			return -EFAULT;
+
+		apollo_send_command(par, APOLLO_WRITE_TO_FLASH);
+		apollo_send_data(par, (i >> 16) & 0xff);
+		apollo_send_data(par, (i >> 8) & 0xff);
+		apollo_send_data(par, i & 0xff);
+
+		apollo_send_data(par, data);
+
+		p++;
+	}
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	*f_pos += count;
+	return count;
+}
+
+static int apollofb_wf_open(struct inode *i, struct file *f)
+{
+	struct apollofb_par *par;
+
+	par = container_of(i->i_cdev, struct apollofb_par, cdev);
+
+	f->private_data = par;
+
+	return 0;
+}
+
+static ssize_t apollofb_temperature_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char temp;
+
+	mutex_lock(&par->lock);
+
+	apollo_send_command(par, APOLLO_READ_TEMPERATURE);
+
+	temp = apollo_read_data(par);
+
+	mutex_unlock(&par->lock);
+
+	sprintf(buf, "%d\n", temp);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_manual_refresh_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	sprintf(buf, "%d\n", par->options.manual_refresh);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_manual_refresh_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if ((count == size) && (state <= 1)) {
+		ret = count;
+		par->options.manual_refresh = state;
+	}
+
+	return ret;
+}
+
+static ssize_t apollofb_use_sleep_mode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	sprintf(buf, "%d\n", par->options.use_sleep_mode);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_use_sleep_mode_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if ((count == size) && (state <= 1)) {
+		ret = count;
+		par->options.use_sleep_mode = state;
+
+		mutex_lock(&par->lock);
+
+		if (state)
+			apollo_set_sleep_mode(par);
+		else
+			apollo_set_normal_mode(par);
+
+		mutex_unlock(&par->lock);
+
+	}
+
+	return ret;
+}
+
+static ssize_t apollofb_partial_update_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	sprintf(buf, "%d\n", par->options.partial_update);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_partial_update_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if ((count == size) && (state <= 1)) {
+		ret = count;
+		par->options.partial_update = state;
+	}
+
+	return ret;
+}
+
+static ssize_t apollofb_defio_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+
+	sprintf(buf, "%lu\n", info->fbdefio->delay * 1000 / HZ);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_defio_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	state = state * HZ / 1000;
+
+	if (!state)
+		state = 1;
+
+	if (count == size) {
+		ret = count;
+		info->fbdefio->delay = state;
+	}
+
+	return ret;
+}
+
+DEVICE_ATTR(manual_refresh, 0666,
+		apollofb_manual_refresh_show, apollofb_manual_refresh_store);
+DEVICE_ATTR(partial_update, 0666,
+		apollofb_partial_update_show, apollofb_partial_update_store);
+DEVICE_ATTR(defio_delay, 0666,
+		apollofb_defio_delay_show, apollofb_defio_delay_store);
+DEVICE_ATTR(use_sleep_mode, 0666,
+		apollofb_use_sleep_mode_show, apollofb_use_sleep_mode_store);
+
+static struct file_operations apollofb_wf_fops = {
+	.owner = THIS_MODULE,
+	.open = apollofb_wf_open,
+	.read = apollofb_wf_read,
+	.write = apollofb_wf_write,
+};
+
+
+static struct fb_ops apollofb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read        = fb_sys_read,
+	.fb_write	= apollofb_write,
+	.fb_fillrect	= apollofb_fillrect,
+	.fb_copyarea	= apollofb_copyarea,
+	.fb_imageblit	= apollofb_imageblit,
+	.fb_cursor	= apollofb_cursor,
+	.fb_sync	= apollofb_sync,
+	.fb_check_var	= apollofb_check_var,
+	.fb_set_par	= apollofb_set_par,
+};
+
+static struct fb_deferred_io apollofb_defio = {
+	.delay		= HZ / 2,
+	.deferred_io	= apollofb_dpy_deferred_io,
+};
+
+DEVICE_ATTR(temperature, 0444, apollofb_temperature_show, NULL);
+
+
+static int __devinit apollofb_setup_chrdev(struct apollofb_par *par)
+{
+	int res = 0;
+	struct cdev *cdev = &par->cdev;
+	dev_t devno;
+
+	res = alloc_chrdev_region(&devno, 0, 1, "apollo");
+	if (res)
+		goto err_alloc_chrdev_region;
+
+
+	cdev_init(cdev, &apollofb_wf_fops);
+
+	res = cdev_add(cdev, devno, 1);
+	if (res)
+		goto err_cdev_add;
+
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(devno, 1);
+err_alloc_chrdev_region:
+
+	return res;
+}
+
+static void apollofb_remove_chrdev(struct apollofb_par *par)
+{
+	cdev_del(&par->cdev);
+	unregister_chrdev_region(par->cdev.dev, 1);
+}
+
+static u16 red4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+static u16 green4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+static u16 blue4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+
+static const struct fb_cmap eink_apollofb_4_colors = {
+	    .len = 4, .red = red4, .green = green4, .blue = blue4
+};
+
+static int __devinit apollofb_probe(struct platform_device *dev)
+{
+	struct fb_info *info;
+	int retval = -ENOMEM;
+	int videomemorysize;
+	unsigned char *videomemory;
+	struct apollofb_par *par;
+	struct eink_apollofb_platdata *pdata = dev->dev.platform_data;
+
+	videomemorysize = (DPY_W * DPY_H)/8 * apollofb_var.bits_per_pixel;
+
+	videomemory = vmalloc(videomemorysize);
+	if (!videomemory)
+		return retval;
+
+	memset(videomemory, 0xFF, videomemorysize);
+
+	info = framebuffer_alloc(sizeof(struct apollofb_par), &dev->dev);
+	if (!info)
+		goto err;
+
+	if (!pdata->ops.set_ctl_pin ||
+			!pdata->ops.get_ctl_pin ||
+			!pdata->ops.read_value ||
+			!pdata->ops.write_value) {
+		retval = -EINVAL;
+		dev_err(&dev->dev,
+				"Invalid platform data: missing operations\n");
+		goto err1;
+	}
+
+	info->screen_base = (char __iomem *) videomemory;
+	info->fbops = &apollofb_ops;
+
+	info->var = apollofb_var;
+	info->fix = apollofb_fix;
+	info->fix.smem_len = videomemorysize;
+	par = info->par;
+	par->info = info;
+	mutex_init(&par->lock);
+	INIT_DELAYED_WORK(&par->deferred_work, apollofb_deferred_work);
+	par->options.manual_refresh = 0;
+	par->options.partial_update = 1;
+	par->options.use_sleep_mode = 0;
+	par->ops = &pdata->ops;
+
+	info->flags = FBINFO_FLAG_DEFAULT;
+
+	if (pdata->defio_delay)
+		apollofb_defio.delay = pdata->defio_delay;
+	info->fbdefio = &apollofb_defio;
+	fb_deferred_io_init(info);
+
+	fb_alloc_cmap(&info->cmap, 4, 0);
+	fb_copy_cmap(&eink_apollofb_4_colors, &info->cmap);
+
+	if (par->ops->initialize)
+		par->ops->initialize();
+
+	par->ops->set_ctl_pin(H_CD, 0);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	mutex_lock(&par->lock);
+	apollo_set_normal_mode(par);
+	apollo_send_command(par, APOLLO_SET_DEPTH);
+	apollo_send_data(par, 0x02);
+	apollo_send_command(par, APOLLO_ERASE_DISPLAY);
+	apollo_send_data(par, 0x01);
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	retval = register_framebuffer(info);
+	if (retval < 0)
+		goto err1;
+	platform_set_drvdata(dev, info);
+
+	printk(KERN_INFO
+	       "fb%d: eInk Apollo frame buffer device,"
+	       "using %dK of video memory (%p)\n",
+	       info->node, videomemorysize >> 10, videomemory);
+
+
+	retval = apollofb_setup_chrdev(par);
+	if (retval)
+		goto err2;
+
+	retval = device_create_file(info->dev, &dev_attr_temperature);
+	if (retval)
+		goto err_devattr_temperature;
+
+	retval = device_create_file(info->dev, &dev_attr_manual_refresh);
+	if (retval)
+		goto err_devattr_manref;
+
+	retval = device_create_file(info->dev, &dev_attr_partial_update);
+	if (retval)
+		goto err_devattr_partupd;
+
+	retval = device_create_file(info->dev, &dev_attr_defio_delay);
+	if (retval)
+		goto err_devattr_defio_delay;
+
+	retval = device_create_file(info->dev, &dev_attr_use_sleep_mode);
+	if (retval)
+		goto err_devattr_use_sleep_mode;
+
+	return 0;
+
+
+	device_remove_file(info->dev, &dev_attr_use_sleep_mode);
+err_devattr_use_sleep_mode:
+	device_remove_file(info->dev, &dev_attr_defio_delay);
+err_devattr_defio_delay:
+	device_remove_file(info->dev, &dev_attr_partial_update);
+err_devattr_partupd:
+	device_remove_file(info->dev, &dev_attr_manual_refresh);
+err_devattr_manref:
+	device_remove_file(info->dev, &dev_attr_temperature);
+err_devattr_temperature:
+	apollofb_remove_chrdev(par);
+err2:
+	unregister_framebuffer(info);
+err1:
+	framebuffer_release(info);
+err:
+	vfree(videomemory);
+	return retval;
+}
+
+static int __devexit apollofb_remove(struct platform_device *dev)
+{
+	struct fb_info *info = platform_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	if (info) {
+		fb_deferred_io_cleanup(info);
+		cancel_delayed_work(&par->deferred_work);
+		flush_scheduled_work();
+
+		device_remove_file(info->dev, &dev_attr_use_sleep_mode);
+		device_remove_file(info->dev, &dev_attr_manual_refresh);
+		device_remove_file(info->dev, &dev_attr_partial_update);
+		device_remove_file(info->dev, &dev_attr_defio_delay);
+		device_remove_file(info->dev, &dev_attr_temperature);
+		unregister_framebuffer(info);
+		vfree((void __force *)info->screen_base);
+		apollofb_remove_chrdev(info->par);
+		framebuffer_release(info);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int apollofb_suspend(struct platform_device *pdev, pm_message_t message)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct apollofb_par *par = info->par;
+
+	mutex_lock(&par->lock);
+	apollo_send_command(par, APOLLO_STANDBY_MODE);
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+
+static int apollofb_resume(struct platform_device *pdev)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct apollofb_par *par = info->par;
+
+	mutex_lock(&par->lock);
+	apollo_wakeup(par);
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+#endif
+
+
+static struct platform_driver apollofb_driver = {
+	.probe	= apollofb_probe,
+	.remove = apollofb_remove,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "eink-apollo",
+	},
+#ifdef CONFIG_PM
+	.suspend = apollofb_suspend,
+	.resume = apollofb_resume,
+#endif
+};
+
+static int __init apollofb_init(void)
+{
+	int ret = 0;
+
+	ret = platform_driver_register(&apollofb_driver);
+
+	return ret;
+}
+
+static void __exit apollofb_exit(void)
+{
+	platform_driver_unregister(&apollofb_driver);
+}
+
+
+module_init(apollofb_init);
+module_exit(apollofb_exit);
+
+MODULE_DESCRIPTION("fbdev driver for Apollo eInk display controller");
+MODULE_AUTHOR("Yauhen Kharuzhy");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/eink_apollofb.h b/include/linux/eink_apollofb.h
new file mode 100644
index 0000000..36c1c3c
--- /dev/null
+++ b/include/linux/eink_apollofb.h
@@ -0,0 +1,60 @@
+
+enum eink_apollo_controls {
+	H_CD = 0,
+	H_RW = 1,
+	H_DS = 2,
+	H_ACK = 3,
+	H_WUP = 4,
+	H_NRST = 5,
+};
+
+struct eink_apollo_operations {
+	int (*initialize)(void);
+	void (*set_ctl_pin)(unsigned int pin, unsigned char val);
+	int (*get_ctl_pin)(unsigned int pin);
+	void (*write_value)(unsigned char val);
+	unsigned char (*read_value)(void);
+};
+
+/* Apollo controller commands */
+#define APOLLO_WRITE_TO_FLASH		0x01
+#define APOLLO_READ_FROM_FLASH		0x02
+#define APOLLO_WRITE_REGISTER		0x10
+#define APOLLO_READ_REGISTER		0x11
+#define APOLLO_READ_TEMPERATURE		0x21
+#define APOLLO_LOAD_PICTURE		0xA0
+#define APOLLO_STOP_LOADING		0xA1
+#define APOLLO_DISPLAY_PICTURE		0xA2
+#define APOLLO_ERASE_DISPLAY		0xA3
+#define APOLLO_INIT_DISPLAY		0xA4
+#define APOLLO_RESTORE_IMAGE		0xA5
+#define APOLLO_GET_STATUS		0xAA
+#define APOLLO_LOAD_PARTIAL_PICTURE	0xB0
+#define APOLLO_DISPLAY_PARTIAL_PICTURE	0xB1
+#define APOLLO_VERSION_NUMBER		0xE0
+#define APOLLO_DISPLAY_SIZE		0xE2
+#define APOLLO_RESET			0xEE
+#define APOLLO_NORMAL_MODE		0xF0
+#define APOLLO_SLEEP_MODE		0xF1
+#define APOLLO_STANDBY_MODE		0xF2
+#define APOLLO_SET_DEPTH		0xF3
+#define APOLLO_ORIENTATION		0xF5
+#define APOLLO_POSITIVE_PICTURE		0xF7
+#define APOLLO_NEGATIVE_PICTURE		0xF8
+#define APOLLO_AUTO_REFRESH		0xF9
+#define APOLLO_CANCEL_AUTO_REFRESH	0xFA
+#define APOLLO_SET_REFRESH_TIMER	0xFB
+#define APOLLO_MANUAL_REFRESH		0xFC
+#define APOLLO_READ_REFRESH_TIMER	0xFD
+
+#define APOLLO_WAVEFORMS_FLASH_SIZE	(1024 * 1024 * 2)
+
+#define APOLLO_STATUS_MODE_MASK		(1 << 0)
+#define APOLLO_STATUS_MODE_SLEEP	0x01
+#define APOLLO_STATUS_MODE_NORMAL	0x00
+
+struct eink_apollofb_platdata {
+	struct eink_apollo_operations ops;
+	unsigned long defio_delay;
+};
+
-- 
1.5.6


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH] FB deffered io: keep pagelist sorted
@ 2008-07-11 14:50 Yauhen Kharuzhy
  2008-07-11 14:50 ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
  2008-07-11 14:54 ` [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
  0 siblings, 2 replies; 13+ messages in thread
From: Yauhen Kharuzhy @ 2008-07-11 14:50 UTC (permalink / raw)
  To: linux-fbdev-devel

eInk display drivers will do less work if list of
changed pages will be sorted.

Apollo controller has 'Partial Update' feature ---
updating only a part of a image, which can be done in
small time.

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/video/fb_defio.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 24843fd..5517e51 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -74,6 +74,7 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
 {
 	struct fb_info *info = vma->vm_private_data;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct page *cur;
 
 	/* this is a callback we get when userspace first tries to
 	write to the page. we schedule a workqueue. that workqueue
@@ -83,7 +84,11 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
 
 	/* protect against the workqueue changing the page list */
 	mutex_lock(&fbdefio->lock);
-	list_add(&page->lru, &fbdefio->pagelist);
+	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+		if (cur->index > page->index)
+			break;
+	}
+	list_add(&page->lru, cur->lru.prev);
 	mutex_unlock(&fbdefio->lock);
 
 	/* come back after delay to process the deferred IO */
-- 
1.5.6


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-11 14:50 [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
@ 2008-07-11 14:50 ` Yauhen Kharuzhy
  2008-07-11 14:54 ` [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
  1 sibling, 0 replies; 13+ messages in thread
From: Yauhen Kharuzhy @ 2008-07-11 14:50 UTC (permalink / raw)
  To: linux-fbdev-devel

Add a new driver for eInk Apollo controller. This
controller is used in various bookreaders with eInk
displays.

The eink_apollofb driver supports many features of
Apollo chip, in difference with the hecubafb driver:
2-bit color depth, partial picture loading, flash
read/write.

The driver uses platform_device framework for
platform-specific functions (set values of control
lines, read/write data from/to bus etc.) and can be
used on any platform.

This driver has been developed for the OpenInkpot
project (http://openinkpot.org/).

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/video/Kconfig         |   17 +
 drivers/video/Makefile        |    1 +
 drivers/video/eink_apollofb.c |  969 +++++++++++++++++++++++++++++++++++++++++
 include/linux/eink_apollofb.h |   60 +++
 4 files changed, 1047 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/eink_apollofb.c
 create mode 100644 include/linux/eink_apollofb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index e0c5f96..ad23464 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -718,6 +718,23 @@ config FB_N411
          This enables support for the Apollo display controller in its
          Hecuba form using the n411 devkit.
 
+config FB_EINK_APOLLO
+       tristate "Apollo eInk display controller support"
+       depends on EXPERIMENTAL && FB && MMU
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       select FB_SYS_FOPS
+       select FB_DEFERRED_IO
+       help
+         This enables support for Apollo eInk display controller.
+	 Includes support of deferred IO and 2-bit grayscale mode.
+
+	 Hardware-specific functions have been moved to platform data.
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called eink_apollofb.
+
 config FB_HGA
 	tristate "Hercules mono graphics support"
 	depends on FB && X86
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 04bca35..2530f0c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_FB_ATARI)            += atafb.o c2p.o atafb_mfb.o \
                                      atafb_iplan2p2.o atafb_iplan2p4.o atafb_iplan2p8.o
 obj-$(CONFIG_FB_MAC)              += macfb.o
 obj-$(CONFIG_FB_HECUBA)           += hecubafb.o
+obj-$(CONFIG_FB_EINK_APOLLO)      += eink_apollofb.o
 obj-$(CONFIG_FB_HGA)              += hgafb.o
 obj-$(CONFIG_FB_XVR500)           += sunxvr500.o
 obj-$(CONFIG_FB_XVR2500)          += sunxvr2500.o
diff --git a/drivers/video/eink_apollofb.c b/drivers/video/eink_apollofb.c
new file mode 100644
index 0000000..e96a7f7
--- /dev/null
+++ b/drivers/video/eink_apollofb.c
@@ -0,0 +1,969 @@
+/*
+ * linux/drivers/video/apollofb.c -- FB driver for lBook/Jinke eReader V3
+ *
+ * Copyright (C) 2007, Yauhen Kharuzhy
+ * This driver is part of openinkpot.org project
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * This driver based on hecubafb driver code.
+ *
+ */
+
+#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/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/eink_apollofb.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/arch/regs-gpio.h>
+#include <asm/hardware.h>
+
+/* Display specific information */
+#define DPY_W 600
+#define DPY_H 800
+
+#define is_portrait(var) (!(var.rotate % 180))
+
+struct apollofb_options {
+	unsigned int manual_refresh:1;
+	unsigned int use_sleep_mode:1;
+};
+
+struct apollofb_par {
+	struct fb_info *info;
+	struct mutex lock;
+	struct delayed_work deferred_work;
+	struct cdev cdev;
+	struct apollofb_options options;
+	int current_mode;
+	struct eink_apollo_operations *ops;
+	int standby;
+};
+
+static struct fb_fix_screeninfo apollofb_fix __devinitdata = {
+	.id =		"apollofb",
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_STATIC_PSEUDOCOLOR,
+	.xpanstep =	0,
+	.ypanstep =	0,
+	.ywrapstep =	0,
+	.line_length =	DPY_W / (8 / 8),
+	.accel =	FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo apollofb_var __devinitdata = {
+	.xres		= DPY_W,
+	.yres		= DPY_H,
+	.xres_virtual	= DPY_W,
+	.yres_virtual	= DPY_H,
+	.red		= {0, 2, 0},
+	.blue		= {0, 2, 0},
+	.green		= {0, 2, 0},
+	.grayscale	= 1,
+	.bits_per_pixel	= 8,
+	.nonstd		= 1,
+};
+
+static inline int apollo_wait_for_ack_value(struct apollofb_par *par,
+		unsigned int value)
+{
+	unsigned long timeout = jiffies + 2 * HZ;
+
+	while ((par->ops->get_ctl_pin(H_ACK) != value) &&
+			time_before(jiffies, timeout))
+		schedule();
+
+	if (par->ops->get_ctl_pin(H_ACK) != value) {
+		printk(KERN_ERR "%s: Wait for H_ACK == %u, timeout\n",
+				__func__, value);
+		return 1;
+	}
+
+	return 0;
+}
+
+#define apollo_wait_for_ack(par)	apollo_wait_for_ack_value(par, 0)
+#define apollo_wait_for_ack_clear(par)	apollo_wait_for_ack_value(par, 1)
+
+static int apollo_send_data(struct apollofb_par *par, unsigned char data)
+{
+	int res = 0;;
+
+	par->ops->write_value(data);
+	par->ops->set_ctl_pin(H_DS, 0);
+	res = apollo_wait_for_ack(par);
+	par->ops->set_ctl_pin(H_DS, 1);
+	if (!res)
+		apollo_wait_for_ack_clear(par);
+	return res;
+}
+
+
+static int apollo_send_command(struct apollofb_par *par, unsigned char cmd)
+{
+	int res;
+
+	par->ops->set_ctl_pin(H_CD, 1);
+	res = apollo_send_data(par, cmd);
+	par->ops->set_ctl_pin(H_CD, 0);
+	return res;
+}
+
+static unsigned char apollo_read_data(struct apollofb_par *par)
+{
+	unsigned char res;
+
+	par->ops->set_ctl_pin(H_RW, 1);
+	par->ops->set_ctl_pin(H_DS, 0);
+	apollo_wait_for_ack(par);
+	res = par->ops->read_value();
+	par->ops->set_ctl_pin(H_DS, 1);
+	apollo_wait_for_ack_clear(par);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	return res;
+}
+
+static void apollo_set_sleep_mode(struct apollofb_par *par)
+{
+	apollo_send_command(par, APOLLO_SLEEP_MODE);
+	par->current_mode = APOLLO_STATUS_MODE_SLEEP;
+}
+
+static void apollo_set_normal_mode(struct apollofb_par *par)
+{
+	par->ops->set_ctl_pin(H_CD, 0);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	apollo_send_command(par, APOLLO_NORMAL_MODE);
+	apollo_send_command(par, APOLLO_ORIENTATION);
+	apollo_send_data(par, ((par->info->var.rotate + 90) % 360) / 90);
+
+	par->current_mode = APOLLO_STATUS_MODE_NORMAL;
+}
+
+static void apollo_wakeup(struct apollofb_par *par)
+{
+	udelay(600); /* in case we were just powered off */
+	par->ops->set_ctl_pin(H_WUP, 1);
+	udelay(100);
+	par->ops->set_ctl_pin(H_DS, 0);
+	apollo_wait_for_ack(par);
+	par->ops->set_ctl_pin(H_WUP, 0);
+	par->ops->set_ctl_pin(H_DS, 1);
+	apollo_wait_for_ack_clear(par);
+}
+
+static void apollofb_apollo_update_part(struct apollofb_par *par,
+		unsigned int x1, unsigned int y1,
+		unsigned int x2, unsigned int y2)
+{
+	int i, j, k;
+	struct fb_info *info = par->info;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int bpp = info->var.green.length;
+	unsigned int pixels_in_byte = 8 / bpp;
+	unsigned char *buf = (unsigned char __force *)info->screen_base;
+	unsigned char tmp, mask;
+
+	y1 -= y1 % 4;
+
+	if ((y2 + 1) % 4)
+		y2 += 4 - ((y2 + 1) % 4);
+
+	x1 -= x1 % 4;
+
+	if ((x2 + 1) % 4)
+		x2 += 4 - ((x2 + 1) % 4);
+
+	mask = 0;
+	for (i = 0; i < bpp; i++)
+		mask = (mask << 1) | 1;
+
+	mutex_lock(&par->lock);
+
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (par->options.manual_refresh)
+		apollo_send_command(par, APOLLO_MANUAL_REFRESH);
+
+	apollo_send_command(par, APOLLO_LOAD_PARTIAL_PICTURE);
+	apollo_send_data(par, (x1 >> 8) & 0xff);
+	apollo_send_data(par, x1 & 0xff);
+	apollo_send_data(par, (y1 >> 8) & 0xff);
+	apollo_send_data(par, y1 & 0xff);
+	apollo_send_data(par, (x2 >> 8) & 0xff);
+	apollo_send_data(par, x2 & 0xff);
+	apollo_send_data(par, (y2 >> 8) & 0xff);
+	apollo_send_data(par, y2 & 0xff);
+
+	k = 0;
+	tmp = 0;
+	for (i = y1; i <= y2; i++)
+		for (j = x1; j <= x2; j++) {
+			tmp = (tmp << bpp) | (buf[i * width + j] & mask);
+			k++;
+			if (k % pixels_in_byte == 0)
+				apollo_send_data(par, tmp);
+		}
+
+	apollo_send_command(par, APOLLO_STOP_LOADING);
+	apollo_send_command(par, APOLLO_DISPLAY_PARTIAL_PICTURE);
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+
+	mutex_unlock(&par->lock);
+}
+
+/* this is called back from the deferred io workqueue */
+static void apollofb_dpy_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+
+	struct apollofb_par *par = info->par;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int height = is_portrait(info->var) ? info->var.yres :
+							info->var.xres;
+	unsigned long int start_page = -1, end_page = -1;
+	unsigned int y1 = 0, y2 = 0;
+	struct page *cur;
+
+
+	list_for_each_entry(cur, pagelist, lru) {
+		if (start_page == -1) {
+			start_page = cur->index;
+			end_page = cur->index;
+			continue;
+		}
+
+		if (cur->index == end_page + 1) {
+			end_page++;
+		} else {
+			y1 = start_page * PAGE_SIZE / width;
+			y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
+			if (y2 >= height)
+				y2 = height - 1;
+
+			apollofb_apollo_update_part(par, 0, y1, width - 1, y2);
+
+			start_page = cur->index;
+			end_page = cur->index;
+		}
+	}
+
+	y1 = start_page * PAGE_SIZE / width;
+	y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
+	if (y2 >= height)
+		y2 = height - 1;
+
+	apollofb_apollo_update_part(par, 0, y1,	width - 1, y2);
+}
+
+static void apollofb_deferred_work(struct work_struct *work)
+{
+	struct apollofb_par *par = container_of(work, struct apollofb_par,
+			deferred_work.work);
+	struct fb_info *info = par->info;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int height = is_portrait(info->var) ? info->var.yres :
+							info->var.xres;
+
+	apollofb_apollo_update_part(par, 0, 0, width - 1, height - 1);
+}
+
+static void apollofb_fillrect(struct fb_info *info,
+				   const struct fb_fillrect *rect)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_fillrect(info, rect);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+static void apollofb_copyarea(struct fb_info *info,
+				   const struct fb_copyarea *area)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_copyarea(info, area);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+static void apollofb_imageblit(struct fb_info *info,
+				const struct fb_image *image)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_imageblit(info, image);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+int apollofb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	return 0;
+}
+
+/*
+ * 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 apollofb_write(struct fb_info *info, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long p;
+	int err = -EINVAL;
+	struct apollofb_par *par;
+	unsigned int xres;
+	unsigned int fbmemlength;
+
+	p = *ppos;
+	par = info->par;
+	xres = info->var.xres;
+	fbmemlength = (xres * info->var.yres)/8 * info->var.bits_per_pixel;
+
+	if (p > fbmemlength)
+		return -ENOSPC;
+
+	err = 0;
+	if ((count + p) > fbmemlength) {
+		count = fbmemlength - p;
+		err = -ENOSPC;
+	}
+
+	if (count) {
+		char *base_addr;
+
+		base_addr = (char __force *)info->screen_base;
+		count -= copy_from_user(base_addr + p, buf, count);
+		*ppos += count;
+		err = -EFAULT;
+	}
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+
+	if (count)
+		return count;
+
+	return err;
+}
+
+static int apollofb_sync(struct fb_info *info)
+{
+	return 0;
+}
+
+static int apollofb_check_var(struct fb_var_screeninfo *var,
+			      struct fb_info *info)
+{
+	switch (var->bits_per_pixel) {
+	case 1:
+		var->red.length = 1;
+		var->red.offset = 0;
+		var->green.length = 1;
+		var->green.offset = 0;
+		var->blue.length = 1;
+		var->blue.offset = 0;
+		var->transp.length = 0;
+		var->transp.offset = 0;
+		break;
+	default:
+		var->bits_per_pixel = 8;
+		var->grayscale = 1;
+		var->red.length = 2;
+		var->red.offset = 0;
+		var->green.length = 2;
+		var->green.offset = 0;
+		var->blue.length = 2;
+		var->blue.offset = 0;
+		var->transp.length = 0;
+		var->transp.offset = 0;
+		break;
+	}
+
+	var->xres = DPY_W;
+	var->xres_virtual = DPY_W;
+	var->yres = DPY_H;
+	var->yres_virtual = DPY_H;
+
+	if (var->rotate % 90)
+		var->rotate -= var->rotate % 90;
+
+	return 0;
+}
+
+static int apollofb_set_par(struct fb_info *info)
+{
+	struct apollofb_par *par = info->par;
+
+	switch (info->var.bits_per_pixel) {
+	case 1:
+		info->fix.visual = FB_VISUAL_MONO01;
+		apollo_send_command(par, APOLLO_SET_DEPTH);
+		apollo_send_data(par, 0x00);
+		break;
+	default:
+		info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+		apollo_send_command(par, APOLLO_SET_DEPTH);
+		apollo_send_data(par, 0x02);
+		break;
+	}
+
+	mutex_lock(&par->lock);
+	apollo_send_command(par, APOLLO_ORIENTATION);
+	apollo_send_data(par, ((info->var.rotate + 90) % 360) / 90);
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+
+static ssize_t apollofb_wf_read(struct file *f, char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	unsigned char data;
+	char __user *p = buf;
+	int i;
+	struct apollofb_par *par = f->private_data;
+
+	mutex_lock(&par->lock);
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (*f_pos > APOLLO_WAVEFORMS_FLASH_SIZE - 1)
+		return 0;
+
+	if (*f_pos + count > APOLLO_WAVEFORMS_FLASH_SIZE)
+		count = APOLLO_WAVEFORMS_FLASH_SIZE - *f_pos;
+
+	for (i = *f_pos; i < *f_pos + count; i++) {
+		apollo_send_command(par, APOLLO_READ_FROM_FLASH);
+
+		apollo_send_data(par, (i >> 16) & 0xff);
+		apollo_send_data(par, (i >> 8) & 0xff);
+		apollo_send_data(par, i & 0xff);
+
+		data = apollo_read_data(par);
+
+		if (copy_to_user(p, &data, 1))
+			return -EFAULT;
+
+		p++;
+	}
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	*f_pos += count;
+	return count;
+}
+
+static ssize_t apollofb_wf_write(struct file *f, const char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	unsigned char data;
+	const char __user *p = buf;
+	int i;
+	struct apollofb_par *par = f->private_data;
+
+	mutex_lock(&par->lock);
+
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (*f_pos > APOLLO_WAVEFORMS_FLASH_SIZE - 1)
+		return 0;
+
+	if (*f_pos + count > APOLLO_WAVEFORMS_FLASH_SIZE)
+		count = APOLLO_WAVEFORMS_FLASH_SIZE - *f_pos;
+
+	for (i = *f_pos; i < *f_pos + count; i++) {
+		if (copy_to_user(&data, p, 1))
+			return -EFAULT;
+
+		apollo_send_command(par, APOLLO_WRITE_TO_FLASH);
+		apollo_send_data(par, (i >> 16) & 0xff);
+		apollo_send_data(par, (i >> 8) & 0xff);
+		apollo_send_data(par, i & 0xff);
+
+		apollo_send_data(par, data);
+
+		p++;
+	}
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	*f_pos += count;
+	return count;
+}
+
+static int apollofb_wf_open(struct inode *i, struct file *f)
+{
+	struct apollofb_par *par;
+
+	par = container_of(i->i_cdev, struct apollofb_par, cdev);
+
+	f->private_data = par;
+
+	return 0;
+}
+
+static ssize_t apollofb_temperature_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char temp;
+
+	mutex_lock(&par->lock);
+
+	apollo_send_command(par, APOLLO_READ_TEMPERATURE);
+
+	temp = apollo_read_data(par);
+
+	mutex_unlock(&par->lock);
+
+	sprintf(buf, "%d\n", temp);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_manual_refresh_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	sprintf(buf, "%d\n", par->options.manual_refresh);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_manual_refresh_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if ((count == size) && (state <= 1)) {
+		ret = count;
+		par->options.manual_refresh = state;
+	}
+
+	return ret;
+}
+
+static ssize_t apollofb_use_sleep_mode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	sprintf(buf, "%d\n", par->options.use_sleep_mode);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_use_sleep_mode_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if ((count == size) && (state <= 1)) {
+		ret = count;
+		par->options.use_sleep_mode = state;
+
+		mutex_lock(&par->lock);
+
+		if (state)
+			apollo_set_sleep_mode(par);
+		else
+			apollo_set_normal_mode(par);
+
+		mutex_unlock(&par->lock);
+
+	}
+
+	return ret;
+}
+
+static ssize_t apollofb_defio_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+
+	sprintf(buf, "%lu\n", info->fbdefio->delay * 1000 / HZ);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_defio_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	state = state * HZ / 1000;
+
+	if (!state)
+		state = 1;
+
+	if (count == size) {
+		ret = count;
+		info->fbdefio->delay = state;
+	}
+
+	return ret;
+}
+
+DEVICE_ATTR(manual_refresh, 0666,
+		apollofb_manual_refresh_show, apollofb_manual_refresh_store);
+DEVICE_ATTR(defio_delay, 0666,
+		apollofb_defio_delay_show, apollofb_defio_delay_store);
+DEVICE_ATTR(use_sleep_mode, 0666,
+		apollofb_use_sleep_mode_show, apollofb_use_sleep_mode_store);
+
+static struct file_operations apollofb_wf_fops = {
+	.owner = THIS_MODULE,
+	.open = apollofb_wf_open,
+	.read = apollofb_wf_read,
+	.write = apollofb_wf_write,
+};
+
+
+static struct fb_ops apollofb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read        = fb_sys_read,
+	.fb_write	= apollofb_write,
+	.fb_fillrect	= apollofb_fillrect,
+	.fb_copyarea	= apollofb_copyarea,
+	.fb_imageblit	= apollofb_imageblit,
+	.fb_cursor	= apollofb_cursor,
+	.fb_sync	= apollofb_sync,
+	.fb_check_var	= apollofb_check_var,
+	.fb_set_par	= apollofb_set_par,
+};
+
+static struct fb_deferred_io apollofb_defio = {
+	.delay		= HZ / 2,
+	.deferred_io	= apollofb_dpy_deferred_io,
+};
+
+DEVICE_ATTR(temperature, 0444, apollofb_temperature_show, NULL);
+
+
+static int __devinit apollofb_setup_chrdev(struct apollofb_par *par)
+{
+	int res = 0;
+	struct cdev *cdev = &par->cdev;
+	dev_t devno;
+
+	res = alloc_chrdev_region(&devno, 0, 1, "apollo");
+	if (res)
+		goto err_alloc_chrdev_region;
+
+
+	cdev_init(cdev, &apollofb_wf_fops);
+
+	res = cdev_add(cdev, devno, 1);
+	if (res)
+		goto err_cdev_add;
+
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(devno, 1);
+err_alloc_chrdev_region:
+
+	return res;
+}
+
+static void apollofb_remove_chrdev(struct apollofb_par *par)
+{
+	cdev_del(&par->cdev);
+	unregister_chrdev_region(par->cdev.dev, 1);
+}
+
+static u16 red4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+static u16 green4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+static u16 blue4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+
+static const struct fb_cmap eink_apollofb_4_colors = {
+	    .len = 4, .red = red4, .green = green4, .blue = blue4
+};
+
+static int __devinit apollofb_probe(struct platform_device *dev)
+{
+	struct fb_info *info;
+	int retval = -ENOMEM;
+	int videomemorysize;
+	unsigned char *videomemory;
+	struct apollofb_par *par;
+	struct eink_apollofb_platdata *pdata = dev->dev.platform_data;
+	unsigned char apollo_display_size;
+
+	videomemorysize = (DPY_W * DPY_H)/8 * apollofb_var.bits_per_pixel;
+
+	videomemory = vmalloc(videomemorysize);
+	if (!videomemory)
+		return retval;
+
+	memset(videomemory, 0xFF, videomemorysize);
+
+	info = framebuffer_alloc(sizeof(struct apollofb_par), &dev->dev);
+	if (!info)
+		goto err;
+
+	if (!pdata->ops.set_ctl_pin ||
+			!pdata->ops.get_ctl_pin ||
+			!pdata->ops.read_value ||
+			!pdata->ops.write_value) {
+		retval = -EINVAL;
+		dev_err(&dev->dev,
+				"Invalid platform data: missing operations\n");
+		goto err1;
+	}
+
+	info->screen_base = (char __iomem *) videomemory;
+	info->fbops = &apollofb_ops;
+
+	info->var = apollofb_var;
+	info->fix = apollofb_fix;
+	info->fix.smem_len = videomemorysize;
+	par = info->par;
+	par->info = info;
+	mutex_init(&par->lock);
+	INIT_DELAYED_WORK(&par->deferred_work, apollofb_deferred_work);
+	par->options.manual_refresh = 0;
+	par->options.use_sleep_mode = 0;
+	par->ops = &pdata->ops;
+
+	info->flags = FBINFO_FLAG_DEFAULT;
+
+	if (pdata->defio_delay)
+		apollofb_defio.delay = pdata->defio_delay;
+	info->fbdefio = &apollofb_defio;
+	fb_deferred_io_init(info);
+
+	fb_alloc_cmap(&info->cmap, 4, 0);
+	fb_copy_cmap(&eink_apollofb_4_colors, &info->cmap);
+
+	if (par->ops->initialize)
+		par->ops->initialize();
+
+	par->ops->set_ctl_pin(H_CD, 0);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	mutex_lock(&par->lock);
+	if (apollo_send_command(par, APOLLO_DISPLAY_SIZE)) {
+		dev_err(&dev->dev, "Apollo controller is not detected.\n");
+		mutex_unlock(&par->lock);
+		goto err1;
+	}
+
+	apollo_display_size = apollo_read_data(par);
+	if (apollo_display_size != 0x22) {
+		dev_err(&dev->dev, "Unknown or missing eInk controller, "
+				"display size byte is 0x%02x\n",
+				apollo_display_size);
+		mutex_unlock(&par->lock);
+		goto err1;
+	}
+
+	apollo_set_normal_mode(par);
+	apollo_send_command(par, APOLLO_SET_DEPTH);
+	apollo_send_data(par, 0x02);
+	apollo_send_command(par, APOLLO_ERASE_DISPLAY);
+	apollo_send_data(par, 0x01);
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	retval = register_framebuffer(info);
+	if (retval < 0)
+		goto err1;
+	platform_set_drvdata(dev, info);
+
+	printk(KERN_INFO
+	       "fb%d: eInk Apollo frame buffer device,"
+	       "using %dK of video memory (%p)\n",
+	       info->node, videomemorysize >> 10, videomemory);
+
+
+	retval = apollofb_setup_chrdev(par);
+	if (retval)
+		goto err2;
+
+	retval = device_create_file(info->dev, &dev_attr_temperature);
+	if (retval)
+		goto err_devattr_temperature;
+
+	retval = device_create_file(info->dev, &dev_attr_manual_refresh);
+	if (retval)
+		goto err_devattr_manref;
+
+	retval = device_create_file(info->dev, &dev_attr_defio_delay);
+	if (retval)
+		goto err_devattr_defio_delay;
+
+	retval = device_create_file(info->dev, &dev_attr_use_sleep_mode);
+	if (retval)
+		goto err_devattr_use_sleep_mode;
+
+	return 0;
+
+
+	device_remove_file(info->dev, &dev_attr_use_sleep_mode);
+err_devattr_use_sleep_mode:
+	device_remove_file(info->dev, &dev_attr_defio_delay);
+err_devattr_defio_delay:
+	device_remove_file(info->dev, &dev_attr_manual_refresh);
+err_devattr_manref:
+	device_remove_file(info->dev, &dev_attr_temperature);
+err_devattr_temperature:
+	apollofb_remove_chrdev(par);
+err2:
+	unregister_framebuffer(info);
+err1:
+	framebuffer_release(info);
+err:
+	vfree(videomemory);
+	return retval;
+}
+
+static int __devexit apollofb_remove(struct platform_device *dev)
+{
+	struct fb_info *info = platform_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	if (info) {
+		fb_deferred_io_cleanup(info);
+		cancel_delayed_work(&par->deferred_work);
+		flush_scheduled_work();
+
+		device_remove_file(info->dev, &dev_attr_use_sleep_mode);
+		device_remove_file(info->dev, &dev_attr_manual_refresh);
+		device_remove_file(info->dev, &dev_attr_defio_delay);
+		device_remove_file(info->dev, &dev_attr_temperature);
+		unregister_framebuffer(info);
+		vfree((void __force *)info->screen_base);
+		apollofb_remove_chrdev(info->par);
+		framebuffer_release(info);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int apollofb_suspend(struct platform_device *pdev, pm_message_t message)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct apollofb_par *par = info->par;
+
+	mutex_lock(&par->lock);
+	apollo_send_command(par, APOLLO_STANDBY_MODE);
+	par->current_mode = APOLLO_STATUS_MODE_SLEEP;
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+
+static int apollofb_resume(struct platform_device *pdev)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct apollofb_par *par = info->par;
+
+	mutex_lock(&par->lock);
+	apollo_wakeup(par);
+	if (!par->options.use_sleep_mode)
+		apollo_set_normal_mode(par);
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+#endif
+
+
+static struct platform_driver apollofb_driver = {
+	.probe	= apollofb_probe,
+	.remove = apollofb_remove,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "eink-apollo",
+	},
+#ifdef CONFIG_PM
+	.suspend = apollofb_suspend,
+	.resume = apollofb_resume,
+#endif
+};
+
+static int __init apollofb_init(void)
+{
+	int ret = 0;
+
+	ret = platform_driver_register(&apollofb_driver);
+
+	return ret;
+}
+
+static void __exit apollofb_exit(void)
+{
+	platform_driver_unregister(&apollofb_driver);
+}
+
+
+module_init(apollofb_init);
+module_exit(apollofb_exit);
+
+MODULE_DESCRIPTION("fbdev driver for Apollo eInk display controller");
+MODULE_AUTHOR("Yauhen Kharuzhy");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/eink_apollofb.h b/include/linux/eink_apollofb.h
new file mode 100644
index 0000000..36c1c3c
--- /dev/null
+++ b/include/linux/eink_apollofb.h
@@ -0,0 +1,60 @@
+
+enum eink_apollo_controls {
+	H_CD = 0,
+	H_RW = 1,
+	H_DS = 2,
+	H_ACK = 3,
+	H_WUP = 4,
+	H_NRST = 5,
+};
+
+struct eink_apollo_operations {
+	int (*initialize)(void);
+	void (*set_ctl_pin)(unsigned int pin, unsigned char val);
+	int (*get_ctl_pin)(unsigned int pin);
+	void (*write_value)(unsigned char val);
+	unsigned char (*read_value)(void);
+};
+
+/* Apollo controller commands */
+#define APOLLO_WRITE_TO_FLASH		0x01
+#define APOLLO_READ_FROM_FLASH		0x02
+#define APOLLO_WRITE_REGISTER		0x10
+#define APOLLO_READ_REGISTER		0x11
+#define APOLLO_READ_TEMPERATURE		0x21
+#define APOLLO_LOAD_PICTURE		0xA0
+#define APOLLO_STOP_LOADING		0xA1
+#define APOLLO_DISPLAY_PICTURE		0xA2
+#define APOLLO_ERASE_DISPLAY		0xA3
+#define APOLLO_INIT_DISPLAY		0xA4
+#define APOLLO_RESTORE_IMAGE		0xA5
+#define APOLLO_GET_STATUS		0xAA
+#define APOLLO_LOAD_PARTIAL_PICTURE	0xB0
+#define APOLLO_DISPLAY_PARTIAL_PICTURE	0xB1
+#define APOLLO_VERSION_NUMBER		0xE0
+#define APOLLO_DISPLAY_SIZE		0xE2
+#define APOLLO_RESET			0xEE
+#define APOLLO_NORMAL_MODE		0xF0
+#define APOLLO_SLEEP_MODE		0xF1
+#define APOLLO_STANDBY_MODE		0xF2
+#define APOLLO_SET_DEPTH		0xF3
+#define APOLLO_ORIENTATION		0xF5
+#define APOLLO_POSITIVE_PICTURE		0xF7
+#define APOLLO_NEGATIVE_PICTURE		0xF8
+#define APOLLO_AUTO_REFRESH		0xF9
+#define APOLLO_CANCEL_AUTO_REFRESH	0xFA
+#define APOLLO_SET_REFRESH_TIMER	0xFB
+#define APOLLO_MANUAL_REFRESH		0xFC
+#define APOLLO_READ_REFRESH_TIMER	0xFD
+
+#define APOLLO_WAVEFORMS_FLASH_SIZE	(1024 * 1024 * 2)
+
+#define APOLLO_STATUS_MODE_MASK		(1 << 0)
+#define APOLLO_STATUS_MODE_SLEEP	0x01
+#define APOLLO_STATUS_MODE_NORMAL	0x00
+
+struct eink_apollofb_platdata {
+	struct eink_apollo_operations ops;
+	unsigned long defio_delay;
+};
+
-- 
1.5.6


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH] FB deffered io: keep pagelist sorted
  2008-07-11 14:50 [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
  2008-07-11 14:50 ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
@ 2008-07-11 14:54 ` Yauhen Kharuzhy
  2008-07-11 14:54   ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
  2008-07-13  1:56   ` [PATCH] FB deffered io: keep pagelist sorted Andrew Morton
  1 sibling, 2 replies; 13+ messages in thread
From: Yauhen Kharuzhy @ 2008-07-11 14:54 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Andrew Morton, Ingo Molnar

eInk display drivers will do less work if list of
changed pages will be sorted.

Apollo controller has 'Partial Update' feature ---
updating only a part of a image, which can be done in
small time.

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/video/fb_defio.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 24843fd..5517e51 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -74,6 +74,7 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
 {
 	struct fb_info *info = vma->vm_private_data;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct page *cur;
 
 	/* this is a callback we get when userspace first tries to
 	write to the page. we schedule a workqueue. that workqueue
@@ -83,7 +84,11 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
 
 	/* protect against the workqueue changing the page list */
 	mutex_lock(&fbdefio->lock);
-	list_add(&page->lru, &fbdefio->pagelist);
+	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+		if (cur->index > page->index)
+			break;
+	}
+	list_add(&page->lru, cur->lru.prev);
 	mutex_unlock(&fbdefio->lock);
 
 	/* come back after delay to process the deferred IO */
-- 
1.5.6


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-11 14:54 ` [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
@ 2008-07-11 14:54   ` Yauhen Kharuzhy
  2008-07-13  2:20     ` Andrew Morton
  2008-09-24  0:01     ` Andrew Morton
  2008-07-13  1:56   ` [PATCH] FB deffered io: keep pagelist sorted Andrew Morton
  1 sibling, 2 replies; 13+ messages in thread
From: Yauhen Kharuzhy @ 2008-07-11 14:54 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Andrew Morton, Ingo Molnar

Add a new driver for eInk Apollo controller. This
controller is used in various bookreaders with eInk
displays.

The eink_apollofb driver supports many features of
Apollo chip, in difference with the hecubafb driver:
2-bit color depth, partial picture loading, flash
read/write.

The driver uses platform_device framework for
platform-specific functions (set values of control
lines, read/write data from/to bus etc.) and can be
used on any platform.

This driver has been developed for the OpenInkpot
project (http://openinkpot.org/).

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/video/Kconfig         |   17 +
 drivers/video/Makefile        |    1 +
 drivers/video/eink_apollofb.c |  969 +++++++++++++++++++++++++++++++++++++++++
 include/linux/eink_apollofb.h |   60 +++
 4 files changed, 1047 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/eink_apollofb.c
 create mode 100644 include/linux/eink_apollofb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index e0c5f96..ad23464 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -718,6 +718,23 @@ config FB_N411
          This enables support for the Apollo display controller in its
          Hecuba form using the n411 devkit.
 
+config FB_EINK_APOLLO
+       tristate "Apollo eInk display controller support"
+       depends on EXPERIMENTAL && FB && MMU
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       select FB_SYS_FOPS
+       select FB_DEFERRED_IO
+       help
+         This enables support for Apollo eInk display controller.
+	 Includes support of deferred IO and 2-bit grayscale mode.
+
+	 Hardware-specific functions have been moved to platform data.
+
+	 To compile this driver as a module, choose M here: the
+	 module will be called eink_apollofb.
+
 config FB_HGA
 	tristate "Hercules mono graphics support"
 	depends on FB && X86
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 04bca35..2530f0c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_FB_ATARI)            += atafb.o c2p.o atafb_mfb.o \
                                      atafb_iplan2p2.o atafb_iplan2p4.o atafb_iplan2p8.o
 obj-$(CONFIG_FB_MAC)              += macfb.o
 obj-$(CONFIG_FB_HECUBA)           += hecubafb.o
+obj-$(CONFIG_FB_EINK_APOLLO)      += eink_apollofb.o
 obj-$(CONFIG_FB_HGA)              += hgafb.o
 obj-$(CONFIG_FB_XVR500)           += sunxvr500.o
 obj-$(CONFIG_FB_XVR2500)          += sunxvr2500.o
diff --git a/drivers/video/eink_apollofb.c b/drivers/video/eink_apollofb.c
new file mode 100644
index 0000000..e96a7f7
--- /dev/null
+++ b/drivers/video/eink_apollofb.c
@@ -0,0 +1,969 @@
+/*
+ * linux/drivers/video/apollofb.c -- FB driver for lBook/Jinke eReader V3
+ *
+ * Copyright (C) 2007, Yauhen Kharuzhy
+ * This driver is part of openinkpot.org project
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * This driver based on hecubafb driver code.
+ *
+ */
+
+#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/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/eink_apollofb.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/arch/regs-gpio.h>
+#include <asm/hardware.h>
+
+/* Display specific information */
+#define DPY_W 600
+#define DPY_H 800
+
+#define is_portrait(var) (!(var.rotate % 180))
+
+struct apollofb_options {
+	unsigned int manual_refresh:1;
+	unsigned int use_sleep_mode:1;
+};
+
+struct apollofb_par {
+	struct fb_info *info;
+	struct mutex lock;
+	struct delayed_work deferred_work;
+	struct cdev cdev;
+	struct apollofb_options options;
+	int current_mode;
+	struct eink_apollo_operations *ops;
+	int standby;
+};
+
+static struct fb_fix_screeninfo apollofb_fix __devinitdata = {
+	.id =		"apollofb",
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_STATIC_PSEUDOCOLOR,
+	.xpanstep =	0,
+	.ypanstep =	0,
+	.ywrapstep =	0,
+	.line_length =	DPY_W / (8 / 8),
+	.accel =	FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo apollofb_var __devinitdata = {
+	.xres		= DPY_W,
+	.yres		= DPY_H,
+	.xres_virtual	= DPY_W,
+	.yres_virtual	= DPY_H,
+	.red		= {0, 2, 0},
+	.blue		= {0, 2, 0},
+	.green		= {0, 2, 0},
+	.grayscale	= 1,
+	.bits_per_pixel	= 8,
+	.nonstd		= 1,
+};
+
+static inline int apollo_wait_for_ack_value(struct apollofb_par *par,
+		unsigned int value)
+{
+	unsigned long timeout = jiffies + 2 * HZ;
+
+	while ((par->ops->get_ctl_pin(H_ACK) != value) &&
+			time_before(jiffies, timeout))
+		schedule();
+
+	if (par->ops->get_ctl_pin(H_ACK) != value) {
+		printk(KERN_ERR "%s: Wait for H_ACK == %u, timeout\n",
+				__func__, value);
+		return 1;
+	}
+
+	return 0;
+}
+
+#define apollo_wait_for_ack(par)	apollo_wait_for_ack_value(par, 0)
+#define apollo_wait_for_ack_clear(par)	apollo_wait_for_ack_value(par, 1)
+
+static int apollo_send_data(struct apollofb_par *par, unsigned char data)
+{
+	int res = 0;;
+
+	par->ops->write_value(data);
+	par->ops->set_ctl_pin(H_DS, 0);
+	res = apollo_wait_for_ack(par);
+	par->ops->set_ctl_pin(H_DS, 1);
+	if (!res)
+		apollo_wait_for_ack_clear(par);
+	return res;
+}
+
+
+static int apollo_send_command(struct apollofb_par *par, unsigned char cmd)
+{
+	int res;
+
+	par->ops->set_ctl_pin(H_CD, 1);
+	res = apollo_send_data(par, cmd);
+	par->ops->set_ctl_pin(H_CD, 0);
+	return res;
+}
+
+static unsigned char apollo_read_data(struct apollofb_par *par)
+{
+	unsigned char res;
+
+	par->ops->set_ctl_pin(H_RW, 1);
+	par->ops->set_ctl_pin(H_DS, 0);
+	apollo_wait_for_ack(par);
+	res = par->ops->read_value();
+	par->ops->set_ctl_pin(H_DS, 1);
+	apollo_wait_for_ack_clear(par);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	return res;
+}
+
+static void apollo_set_sleep_mode(struct apollofb_par *par)
+{
+	apollo_send_command(par, APOLLO_SLEEP_MODE);
+	par->current_mode = APOLLO_STATUS_MODE_SLEEP;
+}
+
+static void apollo_set_normal_mode(struct apollofb_par *par)
+{
+	par->ops->set_ctl_pin(H_CD, 0);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	apollo_send_command(par, APOLLO_NORMAL_MODE);
+	apollo_send_command(par, APOLLO_ORIENTATION);
+	apollo_send_data(par, ((par->info->var.rotate + 90) % 360) / 90);
+
+	par->current_mode = APOLLO_STATUS_MODE_NORMAL;
+}
+
+static void apollo_wakeup(struct apollofb_par *par)
+{
+	udelay(600); /* in case we were just powered off */
+	par->ops->set_ctl_pin(H_WUP, 1);
+	udelay(100);
+	par->ops->set_ctl_pin(H_DS, 0);
+	apollo_wait_for_ack(par);
+	par->ops->set_ctl_pin(H_WUP, 0);
+	par->ops->set_ctl_pin(H_DS, 1);
+	apollo_wait_for_ack_clear(par);
+}
+
+static void apollofb_apollo_update_part(struct apollofb_par *par,
+		unsigned int x1, unsigned int y1,
+		unsigned int x2, unsigned int y2)
+{
+	int i, j, k;
+	struct fb_info *info = par->info;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int bpp = info->var.green.length;
+	unsigned int pixels_in_byte = 8 / bpp;
+	unsigned char *buf = (unsigned char __force *)info->screen_base;
+	unsigned char tmp, mask;
+
+	y1 -= y1 % 4;
+
+	if ((y2 + 1) % 4)
+		y2 += 4 - ((y2 + 1) % 4);
+
+	x1 -= x1 % 4;
+
+	if ((x2 + 1) % 4)
+		x2 += 4 - ((x2 + 1) % 4);
+
+	mask = 0;
+	for (i = 0; i < bpp; i++)
+		mask = (mask << 1) | 1;
+
+	mutex_lock(&par->lock);
+
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (par->options.manual_refresh)
+		apollo_send_command(par, APOLLO_MANUAL_REFRESH);
+
+	apollo_send_command(par, APOLLO_LOAD_PARTIAL_PICTURE);
+	apollo_send_data(par, (x1 >> 8) & 0xff);
+	apollo_send_data(par, x1 & 0xff);
+	apollo_send_data(par, (y1 >> 8) & 0xff);
+	apollo_send_data(par, y1 & 0xff);
+	apollo_send_data(par, (x2 >> 8) & 0xff);
+	apollo_send_data(par, x2 & 0xff);
+	apollo_send_data(par, (y2 >> 8) & 0xff);
+	apollo_send_data(par, y2 & 0xff);
+
+	k = 0;
+	tmp = 0;
+	for (i = y1; i <= y2; i++)
+		for (j = x1; j <= x2; j++) {
+			tmp = (tmp << bpp) | (buf[i * width + j] & mask);
+			k++;
+			if (k % pixels_in_byte == 0)
+				apollo_send_data(par, tmp);
+		}
+
+	apollo_send_command(par, APOLLO_STOP_LOADING);
+	apollo_send_command(par, APOLLO_DISPLAY_PARTIAL_PICTURE);
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+
+	mutex_unlock(&par->lock);
+}
+
+/* this is called back from the deferred io workqueue */
+static void apollofb_dpy_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+
+	struct apollofb_par *par = info->par;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int height = is_portrait(info->var) ? info->var.yres :
+							info->var.xres;
+	unsigned long int start_page = -1, end_page = -1;
+	unsigned int y1 = 0, y2 = 0;
+	struct page *cur;
+
+
+	list_for_each_entry(cur, pagelist, lru) {
+		if (start_page == -1) {
+			start_page = cur->index;
+			end_page = cur->index;
+			continue;
+		}
+
+		if (cur->index == end_page + 1) {
+			end_page++;
+		} else {
+			y1 = start_page * PAGE_SIZE / width;
+			y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
+			if (y2 >= height)
+				y2 = height - 1;
+
+			apollofb_apollo_update_part(par, 0, y1, width - 1, y2);
+
+			start_page = cur->index;
+			end_page = cur->index;
+		}
+	}
+
+	y1 = start_page * PAGE_SIZE / width;
+	y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
+	if (y2 >= height)
+		y2 = height - 1;
+
+	apollofb_apollo_update_part(par, 0, y1,	width - 1, y2);
+}
+
+static void apollofb_deferred_work(struct work_struct *work)
+{
+	struct apollofb_par *par = container_of(work, struct apollofb_par,
+			deferred_work.work);
+	struct fb_info *info = par->info;
+	unsigned int width = is_portrait(info->var) ? info->var.xres :
+							info->var.yres;
+	unsigned int height = is_portrait(info->var) ? info->var.yres :
+							info->var.xres;
+
+	apollofb_apollo_update_part(par, 0, 0, width - 1, height - 1);
+}
+
+static void apollofb_fillrect(struct fb_info *info,
+				   const struct fb_fillrect *rect)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_fillrect(info, rect);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+static void apollofb_copyarea(struct fb_info *info,
+				   const struct fb_copyarea *area)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_copyarea(info, area);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+static void apollofb_imageblit(struct fb_info *info,
+				const struct fb_image *image)
+{
+	struct apollofb_par *par = info->par;
+
+	sys_imageblit(info, image);
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+}
+
+int apollofb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	return 0;
+}
+
+/*
+ * 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 apollofb_write(struct fb_info *info, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long p;
+	int err = -EINVAL;
+	struct apollofb_par *par;
+	unsigned int xres;
+	unsigned int fbmemlength;
+
+	p = *ppos;
+	par = info->par;
+	xres = info->var.xres;
+	fbmemlength = (xres * info->var.yres)/8 * info->var.bits_per_pixel;
+
+	if (p > fbmemlength)
+		return -ENOSPC;
+
+	err = 0;
+	if ((count + p) > fbmemlength) {
+		count = fbmemlength - p;
+		err = -ENOSPC;
+	}
+
+	if (count) {
+		char *base_addr;
+
+		base_addr = (char __force *)info->screen_base;
+		count -= copy_from_user(base_addr + p, buf, count);
+		*ppos += count;
+		err = -EFAULT;
+	}
+
+	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
+
+	if (count)
+		return count;
+
+	return err;
+}
+
+static int apollofb_sync(struct fb_info *info)
+{
+	return 0;
+}
+
+static int apollofb_check_var(struct fb_var_screeninfo *var,
+			      struct fb_info *info)
+{
+	switch (var->bits_per_pixel) {
+	case 1:
+		var->red.length = 1;
+		var->red.offset = 0;
+		var->green.length = 1;
+		var->green.offset = 0;
+		var->blue.length = 1;
+		var->blue.offset = 0;
+		var->transp.length = 0;
+		var->transp.offset = 0;
+		break;
+	default:
+		var->bits_per_pixel = 8;
+		var->grayscale = 1;
+		var->red.length = 2;
+		var->red.offset = 0;
+		var->green.length = 2;
+		var->green.offset = 0;
+		var->blue.length = 2;
+		var->blue.offset = 0;
+		var->transp.length = 0;
+		var->transp.offset = 0;
+		break;
+	}
+
+	var->xres = DPY_W;
+	var->xres_virtual = DPY_W;
+	var->yres = DPY_H;
+	var->yres_virtual = DPY_H;
+
+	if (var->rotate % 90)
+		var->rotate -= var->rotate % 90;
+
+	return 0;
+}
+
+static int apollofb_set_par(struct fb_info *info)
+{
+	struct apollofb_par *par = info->par;
+
+	switch (info->var.bits_per_pixel) {
+	case 1:
+		info->fix.visual = FB_VISUAL_MONO01;
+		apollo_send_command(par, APOLLO_SET_DEPTH);
+		apollo_send_data(par, 0x00);
+		break;
+	default:
+		info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+		apollo_send_command(par, APOLLO_SET_DEPTH);
+		apollo_send_data(par, 0x02);
+		break;
+	}
+
+	mutex_lock(&par->lock);
+	apollo_send_command(par, APOLLO_ORIENTATION);
+	apollo_send_data(par, ((info->var.rotate + 90) % 360) / 90);
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+
+static ssize_t apollofb_wf_read(struct file *f, char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	unsigned char data;
+	char __user *p = buf;
+	int i;
+	struct apollofb_par *par = f->private_data;
+
+	mutex_lock(&par->lock);
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (*f_pos > APOLLO_WAVEFORMS_FLASH_SIZE - 1)
+		return 0;
+
+	if (*f_pos + count > APOLLO_WAVEFORMS_FLASH_SIZE)
+		count = APOLLO_WAVEFORMS_FLASH_SIZE - *f_pos;
+
+	for (i = *f_pos; i < *f_pos + count; i++) {
+		apollo_send_command(par, APOLLO_READ_FROM_FLASH);
+
+		apollo_send_data(par, (i >> 16) & 0xff);
+		apollo_send_data(par, (i >> 8) & 0xff);
+		apollo_send_data(par, i & 0xff);
+
+		data = apollo_read_data(par);
+
+		if (copy_to_user(p, &data, 1))
+			return -EFAULT;
+
+		p++;
+	}
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	*f_pos += count;
+	return count;
+}
+
+static ssize_t apollofb_wf_write(struct file *f, const char __user *buf,
+				size_t count, loff_t *f_pos)
+{
+	unsigned char data;
+	const char __user *p = buf;
+	int i;
+	struct apollofb_par *par = f->private_data;
+
+	mutex_lock(&par->lock);
+
+	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
+		apollo_set_normal_mode(par);
+
+	if (*f_pos > APOLLO_WAVEFORMS_FLASH_SIZE - 1)
+		return 0;
+
+	if (*f_pos + count > APOLLO_WAVEFORMS_FLASH_SIZE)
+		count = APOLLO_WAVEFORMS_FLASH_SIZE - *f_pos;
+
+	for (i = *f_pos; i < *f_pos + count; i++) {
+		if (copy_to_user(&data, p, 1))
+			return -EFAULT;
+
+		apollo_send_command(par, APOLLO_WRITE_TO_FLASH);
+		apollo_send_data(par, (i >> 16) & 0xff);
+		apollo_send_data(par, (i >> 8) & 0xff);
+		apollo_send_data(par, i & 0xff);
+
+		apollo_send_data(par, data);
+
+		p++;
+	}
+
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	*f_pos += count;
+	return count;
+}
+
+static int apollofb_wf_open(struct inode *i, struct file *f)
+{
+	struct apollofb_par *par;
+
+	par = container_of(i->i_cdev, struct apollofb_par, cdev);
+
+	f->private_data = par;
+
+	return 0;
+}
+
+static ssize_t apollofb_temperature_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char temp;
+
+	mutex_lock(&par->lock);
+
+	apollo_send_command(par, APOLLO_READ_TEMPERATURE);
+
+	temp = apollo_read_data(par);
+
+	mutex_unlock(&par->lock);
+
+	sprintf(buf, "%d\n", temp);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_manual_refresh_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	sprintf(buf, "%d\n", par->options.manual_refresh);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_manual_refresh_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if ((count == size) && (state <= 1)) {
+		ret = count;
+		par->options.manual_refresh = state;
+	}
+
+	return ret;
+}
+
+static ssize_t apollofb_use_sleep_mode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	sprintf(buf, "%d\n", par->options.use_sleep_mode);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_use_sleep_mode_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	if ((count == size) && (state <= 1)) {
+		ret = count;
+		par->options.use_sleep_mode = state;
+
+		mutex_lock(&par->lock);
+
+		if (state)
+			apollo_set_sleep_mode(par);
+		else
+			apollo_set_normal_mode(par);
+
+		mutex_unlock(&par->lock);
+
+	}
+
+	return ret;
+}
+
+static ssize_t apollofb_defio_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+
+	sprintf(buf, "%lu\n", info->fbdefio->delay * 1000 / HZ);
+	return strlen(buf) + 1;
+}
+
+static ssize_t apollofb_defio_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	char *after;
+	unsigned long state = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+	ssize_t ret = -EINVAL;
+
+	if (*after && isspace(*after))
+		count++;
+
+	state = state * HZ / 1000;
+
+	if (!state)
+		state = 1;
+
+	if (count == size) {
+		ret = count;
+		info->fbdefio->delay = state;
+	}
+
+	return ret;
+}
+
+DEVICE_ATTR(manual_refresh, 0666,
+		apollofb_manual_refresh_show, apollofb_manual_refresh_store);
+DEVICE_ATTR(defio_delay, 0666,
+		apollofb_defio_delay_show, apollofb_defio_delay_store);
+DEVICE_ATTR(use_sleep_mode, 0666,
+		apollofb_use_sleep_mode_show, apollofb_use_sleep_mode_store);
+
+static struct file_operations apollofb_wf_fops = {
+	.owner = THIS_MODULE,
+	.open = apollofb_wf_open,
+	.read = apollofb_wf_read,
+	.write = apollofb_wf_write,
+};
+
+
+static struct fb_ops apollofb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read        = fb_sys_read,
+	.fb_write	= apollofb_write,
+	.fb_fillrect	= apollofb_fillrect,
+	.fb_copyarea	= apollofb_copyarea,
+	.fb_imageblit	= apollofb_imageblit,
+	.fb_cursor	= apollofb_cursor,
+	.fb_sync	= apollofb_sync,
+	.fb_check_var	= apollofb_check_var,
+	.fb_set_par	= apollofb_set_par,
+};
+
+static struct fb_deferred_io apollofb_defio = {
+	.delay		= HZ / 2,
+	.deferred_io	= apollofb_dpy_deferred_io,
+};
+
+DEVICE_ATTR(temperature, 0444, apollofb_temperature_show, NULL);
+
+
+static int __devinit apollofb_setup_chrdev(struct apollofb_par *par)
+{
+	int res = 0;
+	struct cdev *cdev = &par->cdev;
+	dev_t devno;
+
+	res = alloc_chrdev_region(&devno, 0, 1, "apollo");
+	if (res)
+		goto err_alloc_chrdev_region;
+
+
+	cdev_init(cdev, &apollofb_wf_fops);
+
+	res = cdev_add(cdev, devno, 1);
+	if (res)
+		goto err_cdev_add;
+
+	return 0;
+
+err_cdev_add:
+	unregister_chrdev_region(devno, 1);
+err_alloc_chrdev_region:
+
+	return res;
+}
+
+static void apollofb_remove_chrdev(struct apollofb_par *par)
+{
+	cdev_del(&par->cdev);
+	unregister_chrdev_region(par->cdev.dev, 1);
+}
+
+static u16 red4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+static u16 green4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+static u16 blue4[] __read_mostly = {
+    0x0000, 0x5555, 0xaaaa, 0xffff
+};
+
+static const struct fb_cmap eink_apollofb_4_colors = {
+	    .len = 4, .red = red4, .green = green4, .blue = blue4
+};
+
+static int __devinit apollofb_probe(struct platform_device *dev)
+{
+	struct fb_info *info;
+	int retval = -ENOMEM;
+	int videomemorysize;
+	unsigned char *videomemory;
+	struct apollofb_par *par;
+	struct eink_apollofb_platdata *pdata = dev->dev.platform_data;
+	unsigned char apollo_display_size;
+
+	videomemorysize = (DPY_W * DPY_H)/8 * apollofb_var.bits_per_pixel;
+
+	videomemory = vmalloc(videomemorysize);
+	if (!videomemory)
+		return retval;
+
+	memset(videomemory, 0xFF, videomemorysize);
+
+	info = framebuffer_alloc(sizeof(struct apollofb_par), &dev->dev);
+	if (!info)
+		goto err;
+
+	if (!pdata->ops.set_ctl_pin ||
+			!pdata->ops.get_ctl_pin ||
+			!pdata->ops.read_value ||
+			!pdata->ops.write_value) {
+		retval = -EINVAL;
+		dev_err(&dev->dev,
+				"Invalid platform data: missing operations\n");
+		goto err1;
+	}
+
+	info->screen_base = (char __iomem *) videomemory;
+	info->fbops = &apollofb_ops;
+
+	info->var = apollofb_var;
+	info->fix = apollofb_fix;
+	info->fix.smem_len = videomemorysize;
+	par = info->par;
+	par->info = info;
+	mutex_init(&par->lock);
+	INIT_DELAYED_WORK(&par->deferred_work, apollofb_deferred_work);
+	par->options.manual_refresh = 0;
+	par->options.use_sleep_mode = 0;
+	par->ops = &pdata->ops;
+
+	info->flags = FBINFO_FLAG_DEFAULT;
+
+	if (pdata->defio_delay)
+		apollofb_defio.delay = pdata->defio_delay;
+	info->fbdefio = &apollofb_defio;
+	fb_deferred_io_init(info);
+
+	fb_alloc_cmap(&info->cmap, 4, 0);
+	fb_copy_cmap(&eink_apollofb_4_colors, &info->cmap);
+
+	if (par->ops->initialize)
+		par->ops->initialize();
+
+	par->ops->set_ctl_pin(H_CD, 0);
+	par->ops->set_ctl_pin(H_RW, 0);
+
+	mutex_lock(&par->lock);
+	if (apollo_send_command(par, APOLLO_DISPLAY_SIZE)) {
+		dev_err(&dev->dev, "Apollo controller is not detected.\n");
+		mutex_unlock(&par->lock);
+		goto err1;
+	}
+
+	apollo_display_size = apollo_read_data(par);
+	if (apollo_display_size != 0x22) {
+		dev_err(&dev->dev, "Unknown or missing eInk controller, "
+				"display size byte is 0x%02x\n",
+				apollo_display_size);
+		mutex_unlock(&par->lock);
+		goto err1;
+	}
+
+	apollo_set_normal_mode(par);
+	apollo_send_command(par, APOLLO_SET_DEPTH);
+	apollo_send_data(par, 0x02);
+	apollo_send_command(par, APOLLO_ERASE_DISPLAY);
+	apollo_send_data(par, 0x01);
+	if (par->options.use_sleep_mode)
+		apollo_set_sleep_mode(par);
+	mutex_unlock(&par->lock);
+
+	retval = register_framebuffer(info);
+	if (retval < 0)
+		goto err1;
+	platform_set_drvdata(dev, info);
+
+	printk(KERN_INFO
+	       "fb%d: eInk Apollo frame buffer device,"
+	       "using %dK of video memory (%p)\n",
+	       info->node, videomemorysize >> 10, videomemory);
+
+
+	retval = apollofb_setup_chrdev(par);
+	if (retval)
+		goto err2;
+
+	retval = device_create_file(info->dev, &dev_attr_temperature);
+	if (retval)
+		goto err_devattr_temperature;
+
+	retval = device_create_file(info->dev, &dev_attr_manual_refresh);
+	if (retval)
+		goto err_devattr_manref;
+
+	retval = device_create_file(info->dev, &dev_attr_defio_delay);
+	if (retval)
+		goto err_devattr_defio_delay;
+
+	retval = device_create_file(info->dev, &dev_attr_use_sleep_mode);
+	if (retval)
+		goto err_devattr_use_sleep_mode;
+
+	return 0;
+
+
+	device_remove_file(info->dev, &dev_attr_use_sleep_mode);
+err_devattr_use_sleep_mode:
+	device_remove_file(info->dev, &dev_attr_defio_delay);
+err_devattr_defio_delay:
+	device_remove_file(info->dev, &dev_attr_manual_refresh);
+err_devattr_manref:
+	device_remove_file(info->dev, &dev_attr_temperature);
+err_devattr_temperature:
+	apollofb_remove_chrdev(par);
+err2:
+	unregister_framebuffer(info);
+err1:
+	framebuffer_release(info);
+err:
+	vfree(videomemory);
+	return retval;
+}
+
+static int __devexit apollofb_remove(struct platform_device *dev)
+{
+	struct fb_info *info = platform_get_drvdata(dev);
+	struct apollofb_par *par = info->par;
+
+	if (info) {
+		fb_deferred_io_cleanup(info);
+		cancel_delayed_work(&par->deferred_work);
+		flush_scheduled_work();
+
+		device_remove_file(info->dev, &dev_attr_use_sleep_mode);
+		device_remove_file(info->dev, &dev_attr_manual_refresh);
+		device_remove_file(info->dev, &dev_attr_defio_delay);
+		device_remove_file(info->dev, &dev_attr_temperature);
+		unregister_framebuffer(info);
+		vfree((void __force *)info->screen_base);
+		apollofb_remove_chrdev(info->par);
+		framebuffer_release(info);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int apollofb_suspend(struct platform_device *pdev, pm_message_t message)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct apollofb_par *par = info->par;
+
+	mutex_lock(&par->lock);
+	apollo_send_command(par, APOLLO_STANDBY_MODE);
+	par->current_mode = APOLLO_STATUS_MODE_SLEEP;
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+
+static int apollofb_resume(struct platform_device *pdev)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct apollofb_par *par = info->par;
+
+	mutex_lock(&par->lock);
+	apollo_wakeup(par);
+	if (!par->options.use_sleep_mode)
+		apollo_set_normal_mode(par);
+	mutex_unlock(&par->lock);
+
+	return 0;
+}
+#endif
+
+
+static struct platform_driver apollofb_driver = {
+	.probe	= apollofb_probe,
+	.remove = apollofb_remove,
+	.driver	= {
+		.owner	= THIS_MODULE,
+		.name	= "eink-apollo",
+	},
+#ifdef CONFIG_PM
+	.suspend = apollofb_suspend,
+	.resume = apollofb_resume,
+#endif
+};
+
+static int __init apollofb_init(void)
+{
+	int ret = 0;
+
+	ret = platform_driver_register(&apollofb_driver);
+
+	return ret;
+}
+
+static void __exit apollofb_exit(void)
+{
+	platform_driver_unregister(&apollofb_driver);
+}
+
+
+module_init(apollofb_init);
+module_exit(apollofb_exit);
+
+MODULE_DESCRIPTION("fbdev driver for Apollo eInk display controller");
+MODULE_AUTHOR("Yauhen Kharuzhy");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/eink_apollofb.h b/include/linux/eink_apollofb.h
new file mode 100644
index 0000000..36c1c3c
--- /dev/null
+++ b/include/linux/eink_apollofb.h
@@ -0,0 +1,60 @@
+
+enum eink_apollo_controls {
+	H_CD = 0,
+	H_RW = 1,
+	H_DS = 2,
+	H_ACK = 3,
+	H_WUP = 4,
+	H_NRST = 5,
+};
+
+struct eink_apollo_operations {
+	int (*initialize)(void);
+	void (*set_ctl_pin)(unsigned int pin, unsigned char val);
+	int (*get_ctl_pin)(unsigned int pin);
+	void (*write_value)(unsigned char val);
+	unsigned char (*read_value)(void);
+};
+
+/* Apollo controller commands */
+#define APOLLO_WRITE_TO_FLASH		0x01
+#define APOLLO_READ_FROM_FLASH		0x02
+#define APOLLO_WRITE_REGISTER		0x10
+#define APOLLO_READ_REGISTER		0x11
+#define APOLLO_READ_TEMPERATURE		0x21
+#define APOLLO_LOAD_PICTURE		0xA0
+#define APOLLO_STOP_LOADING		0xA1
+#define APOLLO_DISPLAY_PICTURE		0xA2
+#define APOLLO_ERASE_DISPLAY		0xA3
+#define APOLLO_INIT_DISPLAY		0xA4
+#define APOLLO_RESTORE_IMAGE		0xA5
+#define APOLLO_GET_STATUS		0xAA
+#define APOLLO_LOAD_PARTIAL_PICTURE	0xB0
+#define APOLLO_DISPLAY_PARTIAL_PICTURE	0xB1
+#define APOLLO_VERSION_NUMBER		0xE0
+#define APOLLO_DISPLAY_SIZE		0xE2
+#define APOLLO_RESET			0xEE
+#define APOLLO_NORMAL_MODE		0xF0
+#define APOLLO_SLEEP_MODE		0xF1
+#define APOLLO_STANDBY_MODE		0xF2
+#define APOLLO_SET_DEPTH		0xF3
+#define APOLLO_ORIENTATION		0xF5
+#define APOLLO_POSITIVE_PICTURE		0xF7
+#define APOLLO_NEGATIVE_PICTURE		0xF8
+#define APOLLO_AUTO_REFRESH		0xF9
+#define APOLLO_CANCEL_AUTO_REFRESH	0xFA
+#define APOLLO_SET_REFRESH_TIMER	0xFB
+#define APOLLO_MANUAL_REFRESH		0xFC
+#define APOLLO_READ_REFRESH_TIMER	0xFD
+
+#define APOLLO_WAVEFORMS_FLASH_SIZE	(1024 * 1024 * 2)
+
+#define APOLLO_STATUS_MODE_MASK		(1 << 0)
+#define APOLLO_STATUS_MODE_SLEEP	0x01
+#define APOLLO_STATUS_MODE_NORMAL	0x00
+
+struct eink_apollofb_platdata {
+	struct eink_apollo_operations ops;
+	unsigned long defio_delay;
+};
+
-- 
1.5.6


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] FB deffered io: keep pagelist sorted
  2008-07-11 14:54 ` [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
  2008-07-11 14:54   ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
@ 2008-07-13  1:56   ` Andrew Morton
  1 sibling, 0 replies; 13+ messages in thread
From: Andrew Morton @ 2008-07-13  1:56 UTC (permalink / raw)
  To: Yauhen Kharuzhy; +Cc: Jaya Kumar, Ingo Molnar, linux-fbdev-devel

On Fri, 11 Jul 2008 17:54:30 +0300 Yauhen Kharuzhy <jekhor@gmail.com> wrote:

> eInk display drivers will do less work if list of
> changed pages will be sorted.
> 
> Apollo controller has 'Partial Update' feature ---
> updating only a part of a image, which can be done in
> small time.
> 
> Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
> ---
>  drivers/video/fb_defio.c |    7 ++++++-
>  1 files changed, 6 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
> index 24843fd..5517e51 100644
> --- a/drivers/video/fb_defio.c
> +++ b/drivers/video/fb_defio.c
> @@ -74,6 +74,7 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
>  {
>  	struct fb_info *info = vma->vm_private_data;
>  	struct fb_deferred_io *fbdefio = info->fbdefio;
> +	struct page *cur;
>  
>  	/* this is a callback we get when userspace first tries to
>  	write to the page. we schedule a workqueue. that workqueue
> @@ -83,7 +84,11 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
>  
>  	/* protect against the workqueue changing the page list */
>  	mutex_lock(&fbdefio->lock);
> -	list_add(&page->lru, &fbdefio->pagelist);
> +	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
> +		if (cur->index > page->index)
> +			break;
> +	}
> +	list_add(&page->lru, cur->lru.prev);
>  	mutex_unlock(&fbdefio->lock);
>  
>  	/* come back after delay to process the deferred IO */

We just merged the below:

From: Jaya Kumar <jayakumar.lkml@gmail.com>

This patch is a bugfix for how defio handles multiple processes manipulating
the same framebuffer.

Thanks to Bernard Blackham for identifying this bug.

It occurs when two applications mmap the same framebuffer and concurrently
write to the same page.  Normally, this doesn't occur since only a single
process mmaps the framebuffer.  The symptom of the bug is that the mapping
applications will hang.  The cause is that defio incorrectly tries to add the
same page twice to the pagelist.  The solution I have is to walk the pagelist
and check for a duplicate before adding.  Since I needed to walk the pagelist,
I now also keep the pagelist in sorted order.

Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com>
Cc: Bernard Blackham <bernard@largestprime.net>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---

 drivers/video/fb_defio.c |   20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff -puN drivers/video/fb_defio.c~fbdev-bugfix-for-multiprocess-defio drivers/video/fb_defio.c
--- a/drivers/video/fb_defio.c~fbdev-bugfix-for-multiprocess-defio
+++ a/drivers/video/fb_defio.c
@@ -74,6 +74,7 @@ static int fb_deferred_io_mkwrite(struct
 {
 	struct fb_info *info = vma->vm_private_data;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct page *cur;
 
 	/* this is a callback we get when userspace first tries to
 	write to the page. we schedule a workqueue. that workqueue
@@ -83,7 +84,24 @@ static int fb_deferred_io_mkwrite(struct
 
 	/* protect against the workqueue changing the page list */
 	mutex_lock(&fbdefio->lock);
-	list_add(&page->lru, &fbdefio->pagelist);
+
+	/* we loop through the pagelist before adding in order
+	to keep the pagelist sorted */
+	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+		/* this check is to catch the case where a new
+		process could start writing to the same page
+		through a new pte. this new access can cause the
+		mkwrite even when the original ps's pte is marked
+		writable */
+		if (unlikely(cur == page))
+			goto page_already_added;
+		else if (cur->index > page->index)
+			break;
+	}
+
+	list_add_tail(&page->lru, &cur->lru);
+
+page_already_added:
 	mutex_unlock(&fbdefio->lock);
 
 	/* come back after delay to process the deferred IO */
_


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-11 14:54   ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
@ 2008-07-13  2:20     ` Andrew Morton
  2008-07-13  3:02       ` Jaya Kumar
  2008-09-24  0:01     ` Andrew Morton
  1 sibling, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2008-07-13  2:20 UTC (permalink / raw)
  To: Yauhen Kharuzhy; +Cc: Ingo Molnar, linux-fbdev-devel

On Fri, 11 Jul 2008 17:54:31 +0300 Yauhen Kharuzhy <jekhor@gmail.com> wrote:

> Add a new driver for eInk Apollo controller. This
> controller is used in various bookreaders with eInk
> displays.
> 
> The eink_apollofb driver supports many features of
> Apollo chip, in difference with the hecubafb driver:
> 2-bit color depth, partial picture loading, flash
> read/write.
> 
> The driver uses platform_device framework for
> platform-specific functions (set values of control
> lines, read/write data from/to bus etc.) and can be
> used on any platform.
> 
> This driver has been developed for the OpenInkpot
> project (http://openinkpot.org/).

Please copy linux-fbdev-devel on fbdev patches.

Please consider adding a MAINTAINERS record when adding new drivers.

checkpatch correctly warns:

WARNING: consider using strict_strtoul in preference to simple_strtoul
#703: FILE: drivers/video/eink_apollofb.c:573:
+	unsigned long state = simple_strtoul(buf, &after, 10);

WARNING: consider using strict_strtoul in preference to simple_strtoul
#734: FILE: drivers/video/eink_apollofb.c:604:
+	unsigned long state = simple_strtoul(buf, &after, 10);

WARNING: consider using strict_strtoul in preference to simple_strtoul
#773: FILE: drivers/video/eink_apollofb.c:643:
+	unsigned long state = simple_strtoul(buf, &after, 10);

> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index e0c5f96..ad23464 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -718,6 +718,23 @@ config FB_N411
>           This enables support for the Apollo display controller in its
>           Hecuba form using the n411 devkit.
>  
> +config FB_EINK_APOLLO
> +       tristate "Apollo eInk display controller support"
> +       depends on EXPERIMENTAL && FB && MMU
> +       select FB_SYS_FILLRECT
> +       select FB_SYS_COPYAREA
> +       select FB_SYS_IMAGEBLIT
> +       select FB_SYS_FOPS
> +       select FB_DEFERRED_IO
> +       help
> +         This enables support for Apollo eInk display controller.
> +	 Includes support of deferred IO and 2-bit grayscale mode.

Whitespace inconsistency there, which I will fix.

>
> ...
>
> +/* Display specific information */
> +#define DPY_W 600
> +#define DPY_H 800
> +
> +#define is_portrait(var) (!(var.rotate % 180))

Could be implemented in a C function.

>
> ...
>
> +static inline int apollo_wait_for_ack_value(struct apollofb_par *par,
> +		unsigned int value)
> +{
> +	unsigned long timeout = jiffies + 2 * HZ;
> +
> +	while ((par->ops->get_ctl_pin(H_ACK) != value) &&
> +			time_before(jiffies, timeout))
> +		schedule();
> +
> +	if (par->ops->get_ctl_pin(H_ACK) != value) {
> +		printk(KERN_ERR "%s: Wait for H_ACK == %u, timeout\n",
> +				__func__, value);
> +		return 1;
> +	}
> +
> +	return 0;
> +}

Is a busy-wait unavoidable here?

Are you sure this function is never called from atomic context?

This function is far too large to inline.  I'll fix that.

> +#define apollo_wait_for_ack(par)	apollo_wait_for_ack_value(par, 0)
> +#define apollo_wait_for_ack_clear(par)	apollo_wait_for_ack_value(par, 1)

Could be implemented as C functions.

> +static int apollo_send_data(struct apollofb_par *par, unsigned char data)
> +{
> +	int res = 0;;
> +
> +	par->ops->write_value(data);
> +	par->ops->set_ctl_pin(H_DS, 0);
> +	res = apollo_wait_for_ack(par);
> +	par->ops->set_ctl_pin(H_DS, 1);
> +	if (!res)
> +		apollo_wait_for_ack_clear(par);
> +	return res;
> +}
> +
> +

extra newline

>
> ...
>
> +static void apollofb_apollo_update_part(struct apollofb_par *par,
> +		unsigned int x1, unsigned int y1,
> +		unsigned int x2, unsigned int y2)
> +{
> +	int i, j, k;
> +	struct fb_info *info = par->info;
> +	unsigned int width = is_portrait(info->var) ? info->var.xres :
> +							info->var.yres;
> +	unsigned int bpp = info->var.green.length;
> +	unsigned int pixels_in_byte = 8 / bpp;
> +	unsigned char *buf = (unsigned char __force *)info->screen_base;
> +	unsigned char tmp, mask;
> +
> +	y1 -= y1 % 4;
> +
> +	if ((y2 + 1) % 4)
> +		y2 += 4 - ((y2 + 1) % 4);
> +
> +	x1 -= x1 % 4;
> +
> +	if ((x2 + 1) % 4)
> +		x2 += 4 - ((x2 + 1) % 4);
> +
> +	mask = 0;
> +	for (i = 0; i < bpp; i++)
> +		mask = (mask << 1) | 1;

I think this is this equivalent to

	(1 << bpp) - 1

> +	mutex_lock(&par->lock);
> +
> +	if (par->current_mode == APOLLO_STATUS_MODE_SLEEP)
> +		apollo_set_normal_mode(par);
> +
> +	if (par->options.manual_refresh)
> +		apollo_send_command(par, APOLLO_MANUAL_REFRESH);
> +
> +	apollo_send_command(par, APOLLO_LOAD_PARTIAL_PICTURE);
> +	apollo_send_data(par, (x1 >> 8) & 0xff);
> +	apollo_send_data(par, x1 & 0xff);
> +	apollo_send_data(par, (y1 >> 8) & 0xff);
> +	apollo_send_data(par, y1 & 0xff);
> +	apollo_send_data(par, (x2 >> 8) & 0xff);
> +	apollo_send_data(par, x2 & 0xff);
> +	apollo_send_data(par, (y2 >> 8) & 0xff);
> +	apollo_send_data(par, y2 & 0xff);
> +
> +	k = 0;
> +	tmp = 0;
> +	for (i = y1; i <= y2; i++)
> +		for (j = x1; j <= x2; j++) {
> +			tmp = (tmp << bpp) | (buf[i * width + j] & mask);
> +			k++;
> +			if (k % pixels_in_byte == 0)
> +				apollo_send_data(par, tmp);
> +		}
> +
> +	apollo_send_command(par, APOLLO_STOP_LOADING);
> +	apollo_send_command(par, APOLLO_DISPLAY_PARTIAL_PICTURE);
> +
> +	if (par->options.use_sleep_mode)
> +		apollo_set_sleep_mode(par);
> +
> +	mutex_unlock(&par->lock);
> +}
> +
> +/* this is called back from the deferred io workqueue */
> +static void apollofb_dpy_deferred_io(struct fb_info *info,
> +				struct list_head *pagelist)
> +{
> +
> +	struct apollofb_par *par = info->par;
> +	unsigned int width = is_portrait(info->var) ? info->var.xres :
> +							info->var.yres;
> +	unsigned int height = is_portrait(info->var) ? info->var.yres :
> +							info->var.xres;
> +	unsigned long int start_page = -1, end_page = -1;
> +	unsigned int y1 = 0, y2 = 0;
> +	struct page *cur;
> +
> +

extra newline

> +	list_for_each_entry(cur, pagelist, lru) {
> +		if (start_page == -1) {
> +			start_page = cur->index;
> +			end_page = cur->index;
> +			continue;
> +		}
> +
> +		if (cur->index == end_page + 1) {
> +			end_page++;
> +		} else {
> +			y1 = start_page * PAGE_SIZE / width;
> +			y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
> +			if (y2 >= height)
> +				y2 = height - 1;
> +
> +			apollofb_apollo_update_part(par, 0, y1, width - 1, y2);
> +
> +			start_page = cur->index;
> +			end_page = cur->index;
> +		}
> +	}
> +
> +	y1 = start_page * PAGE_SIZE / width;
> +	y2 = ((end_page + 1) * PAGE_SIZE - 1) / width;
> +	if (y2 >= height)
> +		y2 = height - 1;
> +
> +	apollofb_apollo_update_part(par, 0, y1,	width - 1, y2);
> +}
> +
>
> ...
>
>
> +static void apollofb_imageblit(struct fb_info *info,
> +				const struct fb_image *image)
> +{
> +	struct apollofb_par *par = info->par;
> +
> +	sys_imageblit(info, image);
> +
> +	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
> +}

hrm.  The sys_foo namespace is normally for system calls.  Looks like
the fbdev layer has been involved in a bit of namespace piracy.

> +int apollofb_cursor(struct fb_info *info, struct fb_cursor *cursor)
> +{
> +	return 0;
> +}

I made this static.

> +/*
> + * 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 apollofb_write(struct fb_info *info, const char __user *buf,
> +				size_t count, loff_t *ppos)
> +{
> +	unsigned long p;
> +	int err = -EINVAL;
> +	struct apollofb_par *par;
> +	unsigned int xres;
> +	unsigned int fbmemlength;
> +
> +	p = *ppos;
> +	par = info->par;
> +	xres = info->var.xres;
> +	fbmemlength = (xres * info->var.yres)/8 * info->var.bits_per_pixel;
> +
> +	if (p > fbmemlength)
> +		return -ENOSPC;
> +
> +	err = 0;
> +	if ((count + p) > fbmemlength) {
> +		count = fbmemlength - p;
> +		err = -ENOSPC;
> +	}

ENOSPC is "ran out of disk space".  It's a bit weird to use it here. 
EINVAL would make more sense?

> +	if (count) {
> +		char *base_addr;
> +
> +		base_addr = (char __force *)info->screen_base;
> +		count -= copy_from_user(base_addr + p, buf, count);
> +		*ppos += count;
> +		err = -EFAULT;
> +	}
> +
> +	schedule_delayed_work(&par->deferred_work, info->fbdefio->delay);
> +
> +	if (count)
> +		return count;
> +
> +	return err;
> +}
> +
>
> ...
>
> +static ssize_t apollofb_temperature_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct fb_info *info = dev_get_drvdata(dev);
> +	struct apollofb_par *par = info->par;
> +	char temp;
> +
> +	mutex_lock(&par->lock);
> +
> +	apollo_send_command(par, APOLLO_READ_TEMPERATURE);
> +
> +	temp = apollo_read_data(par);
> +
> +	mutex_unlock(&par->lock);
> +
> +	sprintf(buf, "%d\n", temp);
> +	return strlen(buf) + 1;

I think

	return sprintf(buf, "%d\n", temp);

would work here.  Not sure about the accounting of the trailing \0.

> +}
> +
> +static ssize_t apollofb_manual_refresh_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct fb_info *info = dev_get_drvdata(dev);
> +	struct apollofb_par *par = info->par;
> +
> +	sprintf(buf, "%d\n", par->options.manual_refresh);
> +	return strlen(buf) + 1;

ditto.

> +}
> +
> +static ssize_t apollofb_manual_refresh_store(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	struct fb_info *info = dev_get_drvdata(dev);
> +	struct apollofb_par *par = info->par;
> +	char *after;
> +	unsigned long state = simple_strtoul(buf, &after, 10);
> +	size_t count = after - buf;
> +	ssize_t ret = -EINVAL;
> +
> +	if (*after && isspace(*after))
> +		count++;
> +
> +	if ((count == size) && (state <= 1)) {
> +		ret = count;
> +		par->options.manual_refresh = state;
> +	}
> +
> +	return ret;
> +}
> +
> +static ssize_t apollofb_use_sleep_mode_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct fb_info *info = dev_get_drvdata(dev);
> +	struct apollofb_par *par = info->par;
> +
> +	sprintf(buf, "%d\n", par->options.use_sleep_mode);
> +	return strlen(buf) + 1;

tritto

> +}
> +
> +static ssize_t apollofb_use_sleep_mode_store(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	struct fb_info *info = dev_get_drvdata(dev);
> +	struct apollofb_par *par = info->par;
> +	char *after;
> +	unsigned long state = simple_strtoul(buf, &after, 10);
> +	size_t count = after - buf;
> +	ssize_t ret = -EINVAL;
> +
> +	if (*after && isspace(*after))
> +		count++;
> +
> +	if ((count == size) && (state <= 1)) {
> +		ret = count;
> +		par->options.use_sleep_mode = state;
> +
> +		mutex_lock(&par->lock);
> +
> +		if (state)
> +			apollo_set_sleep_mode(par);
> +		else
> +			apollo_set_normal_mode(par);
> +
> +		mutex_unlock(&par->lock);
> +
> +	}
> +
> +	return ret;
> +}

Is this usersapce interface documented anywhere?


> +static ssize_t apollofb_defio_delay_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct fb_info *info = dev_get_drvdata(dev);
> +
> +	sprintf(buf, "%lu\n", info->fbdefio->delay * 1000 / HZ);
> +	return strlen(buf) + 1;

whatever comes after tritto ;)

> +}
> +
> +static ssize_t apollofb_defio_delay_store(struct device *dev,
> +		struct device_attribute *attr, const char *buf, size_t size)
> +{
> +	struct fb_info *info = dev_get_drvdata(dev);
> +	char *after;
> +	unsigned long state = simple_strtoul(buf, &after, 10);
> +	size_t count = after - buf;
> +	ssize_t ret = -EINVAL;
> +
> +	if (*after && isspace(*after))
> +		count++;
> +
> +	state = state * HZ / 1000;
> +
> +	if (!state)
> +		state = 1;
> +
> +	if (count == size) {
> +		ret = count;
> +		info->fbdefio->delay = state;
> +	}
> +
> +	return ret;
> +}

See, there might be simpler ways of doing all this string parsing.  But
there's no description of what it's doing and I can't be bothered
reverse-engineering it.

> +static void apollofb_remove_chrdev(struct apollofb_par *par)
> +{
> +	cdev_del(&par->cdev);
> +	unregister_chrdev_region(par->cdev.dev, 1);
> +}

It creates a chardev as well?  Please, these things must be documented
_at least_ in the changelog.  Fully.

> +static u16 red4[] __read_mostly = {
> +    0x0000, 0x5555, 0xaaaa, 0xffff
> +};
> +static u16 green4[] __read_mostly = {
> +    0x0000, 0x5555, 0xaaaa, 0xffff
> +};
> +static u16 blue4[] __read_mostly = {
> +    0x0000, 0x5555, 0xaaaa, 0xffff
> +};
> +
>
> ...
>
> +static int __devexit apollofb_remove(struct platform_device *dev)
> +{
> +	struct fb_info *info = platform_get_drvdata(dev);
> +	struct apollofb_par *par = info->par;
> +
> +	if (info) {
> +		fb_deferred_io_cleanup(info);
> +		cancel_delayed_work(&par->deferred_work);
> +		flush_scheduled_work();

I suspect cancel_delayed_work_sync() would suffice here.

> +		device_remove_file(info->dev, &dev_attr_use_sleep_mode);
> +		device_remove_file(info->dev, &dev_attr_manual_refresh);
> +		device_remove_file(info->dev, &dev_attr_defio_delay);
> +		device_remove_file(info->dev, &dev_attr_temperature);
> +		unregister_framebuffer(info);
> +		vfree((void __force *)info->screen_base);
> +		apollofb_remove_chrdev(info->par);
> +		framebuffer_release(info);
> +	}
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int apollofb_suspend(struct platform_device *pdev, pm_message_t message)
> +{
> +	struct fb_info *info = platform_get_drvdata(pdev);
> +	struct apollofb_par *par = info->par;
> +
> +	mutex_lock(&par->lock);
> +	apollo_send_command(par, APOLLO_STANDBY_MODE);
> +	par->current_mode = APOLLO_STATUS_MODE_SLEEP;
> +	mutex_unlock(&par->lock);
> +
> +	return 0;
> +}
> +
> +static int apollofb_resume(struct platform_device *pdev)
> +{
> +	struct fb_info *info = platform_get_drvdata(pdev);
> +	struct apollofb_par *par = info->par;
> +
> +	mutex_lock(&par->lock);
> +	apollo_wakeup(par);
> +	if (!par->options.use_sleep_mode)
> +		apollo_set_normal_mode(par);
> +	mutex_unlock(&par->lock);
> +
> +	return 0;
> +}

Please put

#else
#define apollofb_suspend NULL
#define apollofb_resume NULL

here

> +#endif
> +
> +
> +static struct platform_driver apollofb_driver = {
> +	.probe	= apollofb_probe,
> +	.remove = apollofb_remove,
> +	.driver	= {
> +		.owner	= THIS_MODULE,
> +		.name	= "eink-apollo",
> +	},
> +#ifdef CONFIG_PM
> +	.suspend = apollofb_suspend,
> +	.resume = apollofb_resume,
> +#endif

and remove these ifdefs.  For consistency, and it saves an ifdef.

> +};
> +
> +static int __init apollofb_init(void)
> +{
> +	int ret = 0;

Unneeded initialization

> +
> +	ret = platform_driver_register(&apollofb_driver);
> +
> +	return ret;
> +}

Plain old

	return platform_driver_register(&apollofb_driver);

would suffice?

> +static void __exit apollofb_exit(void)
> +{
> +	platform_driver_unregister(&apollofb_driver);
> +}
> +
> +


-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-13  2:20     ` Andrew Morton
@ 2008-07-13  3:02       ` Jaya Kumar
  2008-07-13 12:20         ` Mikhail Gusarov
  0 siblings, 1 reply; 13+ messages in thread
From: Jaya Kumar @ 2008-07-13  3:02 UTC (permalink / raw)
  To: Andrew Morton; +Cc: Ingo Molnar, linux-fbdev-devel

On Sun, Jul 13, 2008 at 10:20 AM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Fri, 11 Jul 2008 17:54:31 +0300 Yauhen Kharuzhy <jekhor@gmail.com> wrote:
>
>> This driver has been developed for the OpenInkpot
>> project (http://openinkpot.org/).

Hi Yauhen,

I'm excited to see openinkpot.org. Excellent efforts.

> + *
> + * This driver based on hecubafb driver code.
> + *

If possible, please also CC me, I'm the author of hecubafb, defio, and
am always interested to know if people are finding it useful.

> The eink_apollofb driver supports many features of
> Apollo chip, in difference with the hecubafb driver:
> 2-bit color depth, partial picture loading, flash
> read/write.

Yes, you've done good work here. I see the additions you've made as
support for 2-bit, partial updates and also suspend/resume.

However, I should point out that hecubafb is a driver for this same
apollo controller. There are several names for the Apollo controller
including PVI-1006A, Hecuba among others. Looking at the diff between
your work above and hecubafb, I think it should be feasible to add
your code to hecubafb rather than to create a new driver. Please let
me know if there's any problems achieving that, I'll be happy to help.

Thanks,
jaya

-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-13  3:02       ` Jaya Kumar
@ 2008-07-13 12:20         ` Mikhail Gusarov
  2008-07-13 19:03           ` Jaya Kumar
  0 siblings, 1 reply; 13+ messages in thread
From: Mikhail Gusarov @ 2008-07-13 12:20 UTC (permalink / raw)
  To: Jaya Kumar; +Cc: Andrew Morton, Ingo Molnar, linux-fbdev-devel


[-- Attachment #1.1: Type: text/plain, Size: 338 bytes --]

Twas brillig at 11:02:29 13.07.2008 UTC+08 when jayakumar.lkml@gmail.com did gyre and gimble:

 JK> However, I should point out that hecubafb is a driver for this same
 JK> apollo controller.

AFAICT from the diff, eink_apollofb already includes all the features of
hecubafb, so that would be just renaming. Is it worth it?

-- 

[-- Attachment #1.2: Type: application/pgp-signature, Size: 188 bytes --]

[-- Attachment #2: Type: text/plain, Size: 347 bytes --]

-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

[-- Attachment #3: Type: text/plain, Size: 182 bytes --]

_______________________________________________
Linux-fbdev-devel mailing list
Linux-fbdev-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-fbdev-devel

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-13 12:20         ` Mikhail Gusarov
@ 2008-07-13 19:03           ` Jaya Kumar
  2008-07-13 19:19             ` Yauhen Kharuzhy
  0 siblings, 1 reply; 13+ messages in thread
From: Jaya Kumar @ 2008-07-13 19:03 UTC (permalink / raw)
  To: Mikhail Gusarov; +Cc: Andrew Morton, Ingo Molnar, linux-fbdev-devel

On Sun, Jul 13, 2008 at 8:20 PM, Mikhail Gusarov
<dottedmag@dottedmag.net> wrote:
> Twas brillig at 11:02:29 13.07.2008 UTC+08 when jayakumar.lkml@gmail.com did gyre and gimble:
>
>  JK> However, I should point out that hecubafb is a driver for this same
>  JK> apollo controller.
>
> AFAICT from the diff, eink_apollofb already includes all the features of
> hecubafb, so that would be just renaming. Is it worth it?
>
> --
>

Hi Mikhail,

Looking at the posted code, it is the same code as hecubafb, renamed,
and with the 2-bit, partial update, and flash features added. The new
features are good but basically, its a fork of hecubafb. I'm
suggesting that rather than doing that, it would make more sense to
add those features to the existing hecubafb code. If any help is
needed in making that happen, please let me know, I'm happy to help.

Thanks,
jaya

-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-13 19:03           ` Jaya Kumar
@ 2008-07-13 19:19             ` Yauhen Kharuzhy
  2008-07-25 23:35               ` Andrew Morton
  0 siblings, 1 reply; 13+ messages in thread
From: Yauhen Kharuzhy @ 2008-07-13 19:19 UTC (permalink / raw)
  To: Jaya Kumar; +Cc: linux-fbdev-devel, Andrew Morton

On Mon, Jul 14, 2008 at 03:03:05AM +0800, Jaya Kumar wrote:
> On Sun, Jul 13, 2008 at 8:20 PM, Mikhail Gusarov
> <dottedmag@dottedmag.net> wrote:
> > Twas brillig at 11:02:29 13.07.2008 UTC+08 when jayakumar.lkml@gmail.com did gyre and gimble:
> >
> >  JK> However, I should point out that hecubafb is a driver for this same
> >  JK> apollo controller.
> >
> > AFAICT from the diff, eink_apollofb already includes all the features of
> > hecubafb, so that would be just renaming. Is it worth it?
> >
> > --
> >
> 
> Hi Mikhail,
> 
> Looking at the posted code, it is the same code as hecubafb, renamed,
> and with the 2-bit, partial update, and flash features added.
Hardware abstraction layer (with platform_device framework) and rotation also :)

> The new
> features are good but basically, its a fork of hecubafb. I'm
> suggesting that rather than doing that, it would make more sense to
> add those features to the existing hecubafb code. If any help is
> needed in making that happen, please let me know, I'm happy to help.

I am agreed. I saw 'Apollo' name more frequently than any other, but
it is not a matter of principe.

-- 
Yauhen Kharuzhy		jekhor _at_ gmail.com
			JID: jek@jabber.ru

A: No
Q: Should I quote below my post?

-------------------------------------------------------------------------
Sponsored by: SourceForge.net Community Choice Awards: VOTE NOW!
Studies have shown that voting for your favorite open source project,
along with a healthy diet, reduces your potential for chronic lameness
and boredom. Vote Now at http://www.sourceforge.net/community/cca08

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-13 19:19             ` Yauhen Kharuzhy
@ 2008-07-25 23:35               ` Andrew Morton
  0 siblings, 0 replies; 13+ messages in thread
From: Andrew Morton @ 2008-07-25 23:35 UTC (permalink / raw)
  To: Yauhen Kharuzhy; +Cc: linux-fbdev-devel, jayakumar.lkml

On Sun, 13 Jul 2008 22:19:28 +0300
Yauhen Kharuzhy <jekhor@gmail.com> wrote:

> On Mon, Jul 14, 2008 at 03:03:05AM +0800, Jaya Kumar wrote:
> > On Sun, Jul 13, 2008 at 8:20 PM, Mikhail Gusarov
> > <dottedmag@dottedmag.net> wrote:
> > > Twas brillig at 11:02:29 13.07.2008 UTC+08 when jayakumar.lkml@gmail.com did gyre and gimble:
> > >
> > >  JK> However, I should point out that hecubafb is a driver for this same
> > >  JK> apollo controller.
> > >
> > > AFAICT from the diff, eink_apollofb already includes all the features of
> > > hecubafb, so that would be just renaming. Is it worth it?
> > >
> > > --
> > >
> > 
> > Hi Mikhail,
> > 
> > Looking at the posted code, it is the same code as hecubafb, renamed,
> > and with the 2-bit, partial update, and flash features added.
> Hardware abstraction layer (with platform_device framework) and rotation also :)
> 
> > The new
> > features are good but basically, its a fork of hecubafb. I'm
> > suggesting that rather than doing that, it would make more sense to
> > add those features to the existing hecubafb code. If any help is
> > needed in making that happen, please let me know, I'm happy to help.
> 
> I am agreed. I saw 'Apollo' name more frequently than any other, but
> it is not a matter of principe.
> 

I agree that having a separate-but-similar driver for the same hardware
is most undesirable.

I presently have
eink_apollofb-new-driver-for-apollo-eink-controller.patch on "hold"
status, waiting for some resolution here.


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] eink_apollofb: new driver for Apollo eInk controller.
  2008-07-11 14:54   ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
  2008-07-13  2:20     ` Andrew Morton
@ 2008-09-24  0:01     ` Andrew Morton
  1 sibling, 0 replies; 13+ messages in thread
From: Andrew Morton @ 2008-09-24  0:01 UTC (permalink / raw)
  To: Yauhen Kharuzhy; +Cc: mingo, linux-fbdev-devel

On Fri, 11 Jul 2008 17:54:31 +0300
Yauhen Kharuzhy <jekhor@gmail.com> wrote:

> Add a new driver for eInk Apollo controller. This
> controller is used in various bookreaders with eInk
> displays.

I thought the consensus was that this functionality would be added to
the hecubafb driver, but there doesn't seem to have been any movement
along those lines?

The driver is causing some problems on arm:

drivers/video/eink_apollofb.c:36:32: asm/arch/regs-gpio.h: No such file or directory
drivers/video/eink_apollofb.c:37:26: asm/hardware.h: No such file or directory

So I'll drop it.

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2008-09-24  0:02 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-11 14:50 [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
2008-07-11 14:50 ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
2008-07-11 14:54 ` [PATCH] FB deffered io: keep pagelist sorted Yauhen Kharuzhy
2008-07-11 14:54   ` [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy
2008-07-13  2:20     ` Andrew Morton
2008-07-13  3:02       ` Jaya Kumar
2008-07-13 12:20         ` Mikhail Gusarov
2008-07-13 19:03           ` Jaya Kumar
2008-07-13 19:19             ` Yauhen Kharuzhy
2008-07-25 23:35               ` Andrew Morton
2008-09-24  0:01     ` Andrew Morton
2008-07-13  1:56   ` [PATCH] FB deffered io: keep pagelist sorted Andrew Morton
  -- strict thread matches above, loose matches on Subject: below --
2008-07-07 13:09 [PATCH] eink_apollofb: new driver for Apollo eInk controller Yauhen Kharuzhy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).