[ARM] 5353/1: fbdev: add E-Ink Broadsheet controller support v3

This patch adds support for the E-Ink Broadsheet display controller.

Cc: Eric Miao <eric.miao@marvell.com>
Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Jaya Kumar 2009-01-01 17:49:19 +01:00 committed by Russell King
parent b4e411294a
commit 0d4ff4df34
4 changed files with 642 additions and 0 deletions

View file

@ -2135,6 +2135,20 @@ config FB_MX3
far only synchronous displays are supported. If you plan to use far only synchronous displays are supported. If you plan to use
an LCD display with your i.MX31 system, say Y here. an LCD display with your i.MX31 system, say Y here.
config FB_BROADSHEET
tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
depends on FB
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
help
This driver implements support for the E-Ink Broadsheet
controller. The release name for this device was Epson S1D13521
and could also have been called by other names when coupled with
a bridge adapter.
source "drivers/video/omap/Kconfig" source "drivers/video/omap/Kconfig"
source "drivers/video/backlight/Kconfig" source "drivers/video/backlight/Kconfig"

View file

@ -106,6 +106,7 @@ obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o
obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o
obj-$(CONFIG_FB_MAXINE) += maxinefb.o obj-$(CONFIG_FB_MAXINE) += maxinefb.o
obj-$(CONFIG_FB_METRONOME) += metronomefb.o obj-$(CONFIG_FB_METRONOME) += metronomefb.o
obj-$(CONFIG_FB_BROADSHEET) += broadsheetfb.o
obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o
obj-$(CONFIG_FB_SH7760) += sh7760fb.o obj-$(CONFIG_FB_SH7760) += sh7760fb.o
obj-$(CONFIG_FB_IMX) += imxfb.o obj-$(CONFIG_FB_IMX) += imxfb.o

View file

@ -0,0 +1,568 @@
/*
* broadsheetfb.c -- FB driver for E-Ink Broadsheet controller
*
* Copyright (C) 2008, Jaya Kumar
*
* 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.
*
* Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
*
* This driver is written to be used with the Broadsheet display controller.
*
* It is intended to be architecture independent. A board specific driver
* must be used to perform all the physical IO interactions.
*
*/
#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/init.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/uaccess.h>
#include <video/broadsheetfb.h>
/* Display specific information */
#define DPY_W 800
#define DPY_H 600
static struct fb_fix_screeninfo broadsheetfb_fix __devinitdata = {
.id = "broadsheetfb",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.line_length = DPY_W,
.accel = FB_ACCEL_NONE,
};
static struct fb_var_screeninfo broadsheetfb_var __devinitdata = {
.xres = DPY_W,
.yres = DPY_H,
.xres_virtual = DPY_W,
.yres_virtual = DPY_H,
.bits_per_pixel = 8,
.grayscale = 1,
.red = { 0, 4, 0 },
.green = { 0, 4, 0 },
.blue = { 0, 4, 0 },
.transp = { 0, 0, 0 },
};
/* main broadsheetfb functions */
static void broadsheet_issue_data(struct broadsheetfb_par *par, u16 data)
{
par->board->set_ctl(par, BS_WR, 0);
par->board->set_hdb(par, data);
par->board->set_ctl(par, BS_WR, 1);
}
static void broadsheet_issue_cmd(struct broadsheetfb_par *par, u16 data)
{
par->board->set_ctl(par, BS_DC, 0);
broadsheet_issue_data(par, data);
}
static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
{
par->board->wait_for_rdy(par);
par->board->set_ctl(par, BS_CS, 0);
broadsheet_issue_cmd(par, data);
par->board->set_ctl(par, BS_DC, 1);
par->board->set_ctl(par, BS_CS, 1);
}
static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
int argc, u16 *argv)
{
int i;
par->board->wait_for_rdy(par);
par->board->set_ctl(par, BS_CS, 0);
broadsheet_issue_cmd(par, cmd);
par->board->set_ctl(par, BS_DC, 1);
for (i = 0; i < argc; i++)
broadsheet_issue_data(par, argv[i]);
par->board->set_ctl(par, BS_CS, 1);
}
static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
u16 *data)
{
int i;
u16 tmp;
par->board->set_ctl(par, BS_CS, 0);
par->board->set_ctl(par, BS_DC, 1);
for (i = 0; i < size; i++) {
par->board->set_ctl(par, BS_WR, 0);
tmp = (data[i] & 0x0F) << 4;
tmp |= (data[i] & 0x0F00) << 4;
par->board->set_hdb(par, tmp);
par->board->set_ctl(par, BS_WR, 1);
}
par->board->set_ctl(par, BS_CS, 1);
}
static u16 broadsheet_get_data(struct broadsheetfb_par *par)
{
u16 res;
/* wait for ready to go hi. (lo is busy) */
par->board->wait_for_rdy(par);
/* cs lo, dc lo for cmd, we lo for each data, db as usual */
par->board->set_ctl(par, BS_DC, 1);
par->board->set_ctl(par, BS_CS, 0);
par->board->set_ctl(par, BS_WR, 0);
res = par->board->get_hdb(par);
/* strobe wr */
par->board->set_ctl(par, BS_WR, 1);
par->board->set_ctl(par, BS_CS, 1);
return res;
}
static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
u16 data)
{
/* wait for ready to go hi. (lo is busy) */
par->board->wait_for_rdy(par);
/* cs lo, dc lo for cmd, we lo for each data, db as usual */
par->board->set_ctl(par, BS_CS, 0);
broadsheet_issue_cmd(par, BS_CMD_WR_REG);
par->board->set_ctl(par, BS_DC, 1);
broadsheet_issue_data(par, reg);
broadsheet_issue_data(par, data);
par->board->set_ctl(par, BS_CS, 1);
}
static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
{
broadsheet_send_command(par, reg);
msleep(100);
return broadsheet_get_data(par);
}
static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
{
u16 args[5];
args[0] = DPY_W;
args[1] = DPY_H;
args[2] = (100 | (1 << 8) | (1 << 9)); /* sdcfg */
args[3] = 2; /* gdrv cfg */
args[4] = (4 | (1 << 7)); /* lut index format */
broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
/* did the controller really set it? */
broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
args[0] = 4; /* fsync len */
args[1] = (10 << 8) | 4; /* fend/fbegin len */
args[2] = 10; /* line sync len */
args[3] = (100 << 8) | 4; /* line end/begin len */
args[4] = 6; /* pixel clock cfg */
broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);
/* setup waveform */
args[0] = 0x886;
args[1] = 0;
broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
broadsheet_write_reg(par, 0x330, 0x84);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
args[0] = (0x3 << 4);
broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
args[0] = 0x154;
broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
broadsheet_burst_write(par, DPY_W*DPY_H/2,
(u16 *) par->info->screen_base);
broadsheet_send_command(par, BS_CMD_LD_IMG_END);
args[0] = 0x4300;
broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
par->board->wait_for_rdy(par);
}
static void __devinit broadsheet_init(struct broadsheetfb_par *par)
{
broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
/* the controller needs a second */
msleep(1000);
broadsheet_init_display(par);
}
static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
u16 y1, u16 y2)
{
u16 args[5];
unsigned char *buf = (unsigned char *)par->info->screen_base;
/* y1 must be a multiple of 4 so drop the lower bits */
y1 &= 0xFFFC;
/* y2 must be a multiple of 4 , but - 1 so up the lower bits */
y2 |= 0x0003;
args[0] = 0x3 << 4;
args[1] = 0;
args[2] = y1;
args[3] = cpu_to_le16(par->info->var.xres);
args[4] = y2;
broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
args[0] = 0x154;
broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
buf += y1 * par->info->var.xres;
broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
(u16 *) buf);
broadsheet_send_command(par, BS_CMD_LD_IMG_END);
args[0] = 0x4300;
broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
par->board->wait_for_rdy(par);
}
static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
{
u16 args[5];
args[0] = 0x3 << 4;
broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
args[0] = 0x154;
broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
broadsheet_burst_write(par, DPY_W*DPY_H/2,
(u16 *) par->info->screen_base);
broadsheet_send_command(par, BS_CMD_LD_IMG_END);
args[0] = 0x4300;
broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
par->board->wait_for_rdy(par);
}
/* this is called back from the deferred io workqueue */
static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
u16 y1 = 0, h = 0;
int prev_index = -1;
struct page *cur;
struct fb_deferred_io *fbdefio = info->fbdefio;
int h_inc;
u16 yres = info->var.yres;
u16 xres = info->var.xres;
/* height increment is fixed per page */
h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
/* walk the written page list and swizzle the data */
list_for_each_entry(cur, &fbdefio->pagelist, lru) {
if (prev_index < 0) {
/* just starting so assign first page */
y1 = (cur->index << PAGE_SHIFT) / xres;
h = h_inc;
} else if ((prev_index + 1) == cur->index) {
/* this page is consecutive so increase our height */
h += h_inc;
} else {
/* page not consecutive, issue previous update first */
broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);
/* start over with our non consecutive page */
y1 = (cur->index << PAGE_SHIFT) / xres;
h = h_inc;
}
prev_index = cur->index;
}
/* if we still have any pages to update we do so now */
if (h >= yres) {
/* its a full screen update, just do it */
broadsheetfb_dpy_update(info->par);
} else {
broadsheetfb_dpy_update_pages(info->par, y1,
min((u16) (y1 + h), yres));
}
}
static void broadsheetfb_fillrect(struct fb_info *info,
const struct fb_fillrect *rect)
{
struct broadsheetfb_par *par = info->par;
sys_fillrect(info, rect);
broadsheetfb_dpy_update(par);
}
static void broadsheetfb_copyarea(struct fb_info *info,
const struct fb_copyarea *area)
{
struct broadsheetfb_par *par = info->par;
sys_copyarea(info, area);
broadsheetfb_dpy_update(par);
}
static void broadsheetfb_imageblit(struct fb_info *info,
const struct fb_image *image)
{
struct broadsheetfb_par *par = info->par;
sys_imageblit(info, image);
broadsheetfb_dpy_update(par);
}
/*
* 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 broadsheetfb_write(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos)
{
struct broadsheetfb_par *par = info->par;
unsigned long p = *ppos;
void *dst;
int err = 0;
unsigned long total_size;
if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;
total_size = info->fix.smem_len;
if (p > total_size)
return -EFBIG;
if (count > total_size) {
err = -EFBIG;
count = total_size;
}
if (count + p > total_size) {
if (!err)
err = -ENOSPC;
count = total_size - p;
}
dst = (void *)(info->screen_base + p);
if (copy_from_user(dst, buf, count))
err = -EFAULT;
if (!err)
*ppos += count;
broadsheetfb_dpy_update(par);
return (err) ? err : count;
}
static struct fb_ops broadsheetfb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
.fb_write = broadsheetfb_write,
.fb_fillrect = broadsheetfb_fillrect,
.fb_copyarea = broadsheetfb_copyarea,
.fb_imageblit = broadsheetfb_imageblit,
};
static struct fb_deferred_io broadsheetfb_defio = {
.delay = HZ/4,
.deferred_io = broadsheetfb_dpy_deferred_io,
};
static int __devinit broadsheetfb_probe(struct platform_device *dev)
{
struct fb_info *info;
struct broadsheet_board *board;
int retval = -ENOMEM;
int videomemorysize;
unsigned char *videomemory;
struct broadsheetfb_par *par;
int i;
/* pick up board specific routines */
board = dev->dev.platform_data;
if (!board)
return -EINVAL;
/* try to count device specific driver, if can't, platform recalls */
if (!try_module_get(board->owner))
return -ENODEV;
info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
if (!info)
goto err;
videomemorysize = (DPY_W*DPY_H);
videomemory = vmalloc(videomemorysize);
if (!videomemory)
goto err_fb_rel;
memset(videomemory, 0, videomemorysize);
info->screen_base = (char *)videomemory;
info->fbops = &broadsheetfb_ops;
info->var = broadsheetfb_var;
info->fix = broadsheetfb_fix;
info->fix.smem_len = videomemorysize;
par = info->par;
par->info = info;
par->board = board;
par->write_reg = broadsheet_write_reg;
par->read_reg = broadsheet_read_reg;
init_waitqueue_head(&par->waitq);
info->flags = FBINFO_FLAG_DEFAULT;
info->fbdefio = &broadsheetfb_defio;
fb_deferred_io_init(info);
retval = fb_alloc_cmap(&info->cmap, 16, 0);
if (retval < 0) {
dev_err(&dev->dev, "Failed to allocate colormap\n");
goto err_vfree;
}
/* set cmap */
for (i = 0; i < 16; i++)
info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;
memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);
memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);
retval = par->board->setup_irq(info);
if (retval < 0)
goto err_cmap;
/* this inits the dpy */
retval = board->init(par);
if (retval < 0)
goto err_free_irq;
broadsheet_init(par);
retval = register_framebuffer(info);
if (retval < 0)
goto err_free_irq;
platform_set_drvdata(dev, info);
printk(KERN_INFO
"fb%d: Broadsheet frame buffer, using %dK of video memory\n",
info->node, videomemorysize >> 10);
return 0;
err_free_irq:
board->cleanup(par);
err_cmap:
fb_dealloc_cmap(&info->cmap);
err_vfree:
vfree(videomemory);
err_fb_rel:
framebuffer_release(info);
err:
module_put(board->owner);
return retval;
}
static int __devexit broadsheetfb_remove(struct platform_device *dev)
{
struct fb_info *info = platform_get_drvdata(dev);
if (info) {
struct broadsheetfb_par *par = info->par;
unregister_framebuffer(info);
fb_deferred_io_cleanup(info);
par->board->cleanup(par);
fb_dealloc_cmap(&info->cmap);
vfree((void *)info->screen_base);
module_put(par->board->owner);
framebuffer_release(info);
}
return 0;
}
static struct platform_driver broadsheetfb_driver = {
.probe = broadsheetfb_probe,
.remove = broadsheetfb_remove,
.driver = {
.owner = THIS_MODULE,
.name = "broadsheetfb",
},
};
static int __init broadsheetfb_init(void)
{
return platform_driver_register(&broadsheetfb_driver);
}
static void __exit broadsheetfb_exit(void)
{
platform_driver_unregister(&broadsheetfb_driver);
}
module_init(broadsheetfb_init);
module_exit(broadsheetfb_exit);
MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,59 @@
/*
* broadsheetfb.h - definitions for the broadsheet framebuffer driver
*
* Copyright (C) 2008 by Jaya Kumar
*
* 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.
*
*/
#ifndef _LINUX_BROADSHEETFB_H_
#define _LINUX_BROADSHEETFB_H_
/* Broadsheet command defines */
#define BS_CMD_INIT_SYS_RUN 0x06
#define BS_CMD_INIT_DSPE_CFG 0x09
#define BS_CMD_INIT_DSPE_TMG 0x0A
#define BS_CMD_INIT_ROTMODE 0x0B
#define BS_CMD_RD_REG 0x10
#define BS_CMD_WR_REG 0x11
#define BS_CMD_LD_IMG 0x20
#define BS_CMD_LD_IMG_AREA 0x22
#define BS_CMD_LD_IMG_END 0x23
#define BS_CMD_WAIT_DSPE_TRG 0x28
#define BS_CMD_WAIT_DSPE_FREND 0x29
#define BS_CMD_RD_WFM_INFO 0x30
#define BS_CMD_UPD_INIT 0x32
#define BS_CMD_UPD_FULL 0x33
#define BS_CMD_UPD_GDRV_CLR 0x37
/* Broadsheet pin interface specific defines */
#define BS_CS 0x01
#define BS_DC 0x02
#define BS_WR 0x03
/* struct used by broadsheet. board specific stuff comes from *board */
struct broadsheetfb_par {
struct fb_info *info;
struct broadsheet_board *board;
void (*write_reg)(struct broadsheetfb_par *, u16 reg, u16 val);
u16 (*read_reg)(struct broadsheetfb_par *, u16 reg);
wait_queue_head_t waitq;
};
/* board specific routines */
struct broadsheet_board {
struct module *owner;
int (*init)(struct broadsheetfb_par *);
int (*wait_for_rdy)(struct broadsheetfb_par *);
void (*set_ctl)(struct broadsheetfb_par *, unsigned char, u8);
void (*set_hdb)(struct broadsheetfb_par *, u16);
u16 (*get_hdb)(struct broadsheetfb_par *);
void (*cleanup)(struct broadsheetfb_par *);
int (*get_panel_type)(void);
int (*setup_irq)(struct fb_info *);
};
#endif