From: Yauhen K. <je...@gm...> - 2008-07-11 14:51:29
|
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 <je...@gm...> --- 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 |
From: Yauhen K. <je...@gm...> - 2008-07-11 14:51:29
|
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 <je...@gm...> --- 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 |
From: Yauhen K. <je...@gm...> - 2008-07-11 14:54:42
|
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 <je...@gm...> --- 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 |
From: Andrew M. <ak...@li...> - 2008-07-13 01:56:34
|
On Fri, 11 Jul 2008 17:54:30 +0300 Yauhen Kharuzhy <je...@gm...> 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 <je...@gm...> > --- > 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 <jay...@gm...> 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 <jay...@gm...> Cc: Bernard Blackham <be...@la...> Cc: <st...@ke...> Signed-off-by: Andrew Morton <ak...@li...> --- 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 */ _ |
From: Yauhen K. <je...@gm...> - 2008-07-11 14:54:43
|
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 <je...@gm...> --- 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 |
From: Andrew M. <ak...@li...> - 2008-09-24 00:02:08
|
On Fri, 11 Jul 2008 17:54:31 +0300 Yauhen Kharuzhy <je...@gm...> 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. |
From: Andrew M. <ak...@li...> - 2008-07-13 02:20:24
|
On Fri, 11 Jul 2008 17:54:31 +0300 Yauhen Kharuzhy <je...@gm...> 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); > +} > + > + |
From: Jaya K. <jay...@gm...> - 2008-07-13 03:02:35
|
On Sun, Jul 13, 2008 at 10:20 AM, Andrew Morton <ak...@li...> wrote: > On Fri, 11 Jul 2008 17:54:31 +0300 Yauhen Kharuzhy <je...@gm...> 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 |
From: Mikhail G. <dot...@do...> - 2008-07-13 12:20:30
|
Twas brillig at 11:02:29 13.07.2008 UTC+08 when jay...@gm... 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? -- |
From: Jaya K. <jay...@gm...> - 2008-07-13 19:03:10
|
On Sun, Jul 13, 2008 at 8:20 PM, Mikhail Gusarov <dot...@do...> wrote: > Twas brillig at 11:02:29 13.07.2008 UTC+08 when jay...@gm... 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 |
From: Yauhen K. <je...@gm...> - 2008-07-13 19:24:13
|
On Mon, Jul 14, 2008 at 03:03:05AM +0800, Jaya Kumar wrote: > On Sun, Jul 13, 2008 at 8:20 PM, Mikhail Gusarov > <dot...@do...> wrote: > > Twas brillig at 11:02:29 13.07.2008 UTC+08 when jay...@gm... 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: je...@ja... A: No Q: Should I quote below my post? |
From: Andrew M. <ak...@li...> - 2008-07-25 23:36:10
|
On Sun, 13 Jul 2008 22:19:28 +0300 Yauhen Kharuzhy <je...@gm...> 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 > > <dot...@do...> wrote: > > > Twas brillig at 11:02:29 13.07.2008 UTC+08 when jay...@gm... 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. |