hecubafb.c (7420B)
1/* 2 * linux/drivers/video/hecubafb.c -- FB driver for Hecuba/Apollo controller 3 * 4 * Copyright (C) 2006, Jaya Kumar 5 * This work was sponsored by CIS(M) Sdn Bhd 6 * 7 * This file is subject to the terms and conditions of the GNU General Public 8 * License. See the file COPYING in the main directory of this archive for 9 * more details. 10 * 11 * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. 12 * This work was possible because of apollo display code from E-Ink's website 13 * http://support.eink.com/community 14 * All information used to write this code is from public material made 15 * available by E-Ink on its support site. Some commands such as 0xA4 16 * were found by looping through cmd=0x00 thru 0xFF and supplying random 17 * values. There are other commands that the display is capable of, 18 * beyond the 5 used here but they are more complex. 19 * 20 * This driver is written to be used with the Hecuba display architecture. 21 * The actual display chip is called Apollo and the interface electronics 22 * it needs is called Hecuba. 23 * 24 * It is intended to be architecture independent. A board specific driver 25 * must be used to perform all the physical IO interactions. An example 26 * is provided as n411.c 27 * 28 */ 29 30#include <linux/module.h> 31#include <linux/kernel.h> 32#include <linux/errno.h> 33#include <linux/string.h> 34#include <linux/mm.h> 35#include <linux/vmalloc.h> 36#include <linux/delay.h> 37#include <linux/interrupt.h> 38#include <linux/fb.h> 39#include <linux/init.h> 40#include <linux/platform_device.h> 41#include <linux/list.h> 42#include <linux/uaccess.h> 43 44#include <video/hecubafb.h> 45 46/* Display specific information */ 47#define DPY_W 600 48#define DPY_H 800 49 50static const struct fb_fix_screeninfo hecubafb_fix = { 51 .id = "hecubafb", 52 .type = FB_TYPE_PACKED_PIXELS, 53 .visual = FB_VISUAL_MONO01, 54 .xpanstep = 0, 55 .ypanstep = 0, 56 .ywrapstep = 0, 57 .line_length = DPY_W, 58 .accel = FB_ACCEL_NONE, 59}; 60 61static const struct fb_var_screeninfo hecubafb_var = { 62 .xres = DPY_W, 63 .yres = DPY_H, 64 .xres_virtual = DPY_W, 65 .yres_virtual = DPY_H, 66 .bits_per_pixel = 1, 67 .nonstd = 1, 68}; 69 70/* main hecubafb functions */ 71 72static void apollo_send_data(struct hecubafb_par *par, unsigned char data) 73{ 74 /* set data */ 75 par->board->set_data(par, data); 76 77 /* set DS low */ 78 par->board->set_ctl(par, HCB_DS_BIT, 0); 79 80 /* wait for ack */ 81 par->board->wait_for_ack(par, 0); 82 83 /* set DS hi */ 84 par->board->set_ctl(par, HCB_DS_BIT, 1); 85 86 /* wait for ack to clear */ 87 par->board->wait_for_ack(par, 1); 88} 89 90static void apollo_send_command(struct hecubafb_par *par, unsigned char data) 91{ 92 /* command so set CD to high */ 93 par->board->set_ctl(par, HCB_CD_BIT, 1); 94 95 /* actually strobe with command */ 96 apollo_send_data(par, data); 97 98 /* clear CD back to low */ 99 par->board->set_ctl(par, HCB_CD_BIT, 0); 100} 101 102static void hecubafb_dpy_update(struct hecubafb_par *par) 103{ 104 int i; 105 unsigned char *buf = (unsigned char __force *)par->info->screen_base; 106 107 apollo_send_command(par, APOLLO_START_NEW_IMG); 108 109 for (i=0; i < (DPY_W*DPY_H/8); i++) { 110 apollo_send_data(par, *(buf++)); 111 } 112 113 apollo_send_command(par, APOLLO_STOP_IMG_DATA); 114 apollo_send_command(par, APOLLO_DISPLAY_IMG); 115} 116 117/* this is called back from the deferred io workqueue */ 118static void hecubafb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist) 119{ 120 hecubafb_dpy_update(info->par); 121} 122 123static void hecubafb_fillrect(struct fb_info *info, 124 const struct fb_fillrect *rect) 125{ 126 struct hecubafb_par *par = info->par; 127 128 sys_fillrect(info, rect); 129 130 hecubafb_dpy_update(par); 131} 132 133static void hecubafb_copyarea(struct fb_info *info, 134 const struct fb_copyarea *area) 135{ 136 struct hecubafb_par *par = info->par; 137 138 sys_copyarea(info, area); 139 140 hecubafb_dpy_update(par); 141} 142 143static void hecubafb_imageblit(struct fb_info *info, 144 const struct fb_image *image) 145{ 146 struct hecubafb_par *par = info->par; 147 148 sys_imageblit(info, image); 149 150 hecubafb_dpy_update(par); 151} 152 153/* 154 * this is the slow path from userspace. they can seek and write to 155 * the fb. it's inefficient to do anything less than a full screen draw 156 */ 157static ssize_t hecubafb_write(struct fb_info *info, const char __user *buf, 158 size_t count, loff_t *ppos) 159{ 160 struct hecubafb_par *par = info->par; 161 unsigned long p = *ppos; 162 void *dst; 163 int err = 0; 164 unsigned long total_size; 165 166 if (info->state != FBINFO_STATE_RUNNING) 167 return -EPERM; 168 169 total_size = info->fix.smem_len; 170 171 if (p > total_size) 172 return -EFBIG; 173 174 if (count > total_size) { 175 err = -EFBIG; 176 count = total_size; 177 } 178 179 if (count + p > total_size) { 180 if (!err) 181 err = -ENOSPC; 182 183 count = total_size - p; 184 } 185 186 dst = (void __force *) (info->screen_base + p); 187 188 if (copy_from_user(dst, buf, count)) 189 err = -EFAULT; 190 191 if (!err) 192 *ppos += count; 193 194 hecubafb_dpy_update(par); 195 196 return (err) ? err : count; 197} 198 199static const struct fb_ops hecubafb_ops = { 200 .owner = THIS_MODULE, 201 .fb_read = fb_sys_read, 202 .fb_write = hecubafb_write, 203 .fb_fillrect = hecubafb_fillrect, 204 .fb_copyarea = hecubafb_copyarea, 205 .fb_imageblit = hecubafb_imageblit, 206 .fb_mmap = fb_deferred_io_mmap, 207}; 208 209static struct fb_deferred_io hecubafb_defio = { 210 .delay = HZ, 211 .deferred_io = hecubafb_dpy_deferred_io, 212}; 213 214static int hecubafb_probe(struct platform_device *dev) 215{ 216 struct fb_info *info; 217 struct hecuba_board *board; 218 int retval = -ENOMEM; 219 int videomemorysize; 220 unsigned char *videomemory; 221 struct hecubafb_par *par; 222 223 /* pick up board specific routines */ 224 board = dev->dev.platform_data; 225 if (!board) 226 return -EINVAL; 227 228 /* try to count device specific driver, if can't, platform recalls */ 229 if (!try_module_get(board->owner)) 230 return -ENODEV; 231 232 videomemorysize = (DPY_W*DPY_H)/8; 233 234 videomemory = vzalloc(videomemorysize); 235 if (!videomemory) 236 goto err_videomem_alloc; 237 238 info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev); 239 if (!info) 240 goto err_fballoc; 241 242 info->screen_base = (char __force __iomem *)videomemory; 243 info->fbops = &hecubafb_ops; 244 245 info->var = hecubafb_var; 246 info->fix = hecubafb_fix; 247 info->fix.smem_len = videomemorysize; 248 par = info->par; 249 par->info = info; 250 par->board = board; 251 par->send_command = apollo_send_command; 252 par->send_data = apollo_send_data; 253 254 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; 255 256 info->fbdefio = &hecubafb_defio; 257 fb_deferred_io_init(info); 258 259 retval = register_framebuffer(info); 260 if (retval < 0) 261 goto err_fbreg; 262 platform_set_drvdata(dev, info); 263 264 fb_info(info, "Hecuba frame buffer device, using %dK of video memory\n", 265 videomemorysize >> 10); 266 267 /* this inits the dpy */ 268 retval = par->board->init(par); 269 if (retval < 0) 270 goto err_fbreg; 271 272 return 0; 273err_fbreg: 274 framebuffer_release(info); 275err_fballoc: 276 vfree(videomemory); 277err_videomem_alloc: 278 module_put(board->owner); 279 return retval; 280} 281 282static int hecubafb_remove(struct platform_device *dev) 283{ 284 struct fb_info *info = platform_get_drvdata(dev); 285 286 if (info) { 287 struct hecubafb_par *par = info->par; 288 fb_deferred_io_cleanup(info); 289 unregister_framebuffer(info); 290 vfree((void __force *)info->screen_base); 291 if (par->board->remove) 292 par->board->remove(par); 293 module_put(par->board->owner); 294 framebuffer_release(info); 295 } 296 return 0; 297} 298 299static struct platform_driver hecubafb_driver = { 300 .probe = hecubafb_probe, 301 .remove = hecubafb_remove, 302 .driver = { 303 .name = "hecubafb", 304 }, 305}; 306module_platform_driver(hecubafb_driver); 307 308MODULE_DESCRIPTION("fbdev driver for Hecuba/Apollo controller"); 309MODULE_AUTHOR("Jaya Kumar"); 310MODULE_LICENSE("GPL");