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 50 static 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 61 static 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 72 static 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 90 static 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 102 static 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 */ 118 static void hecubafb_dpy_deferred_io(struct fb_info *info, 119 struct list_head *pagelist) 120 { 121 hecubafb_dpy_update(info->par); 122 } 123 124 static void hecubafb_fillrect(struct fb_info *info, 125 const struct fb_fillrect *rect) 126 { 127 struct hecubafb_par *par = info->par; 128 129 sys_fillrect(info, rect); 130 131 hecubafb_dpy_update(par); 132 } 133 134 static void hecubafb_copyarea(struct fb_info *info, 135 const struct fb_copyarea *area) 136 { 137 struct hecubafb_par *par = info->par; 138 139 sys_copyarea(info, area); 140 141 hecubafb_dpy_update(par); 142 } 143 144 static void hecubafb_imageblit(struct fb_info *info, 145 const struct fb_image *image) 146 { 147 struct hecubafb_par *par = info->par; 148 149 sys_imageblit(info, image); 150 151 hecubafb_dpy_update(par); 152 } 153 154 /* 155 * this is the slow path from userspace. they can seek and write to 156 * the fb. it's inefficient to do anything less than a full screen draw 157 */ 158 static ssize_t hecubafb_write(struct fb_info *info, const char __user *buf, 159 size_t count, loff_t *ppos) 160 { 161 struct hecubafb_par *par = info->par; 162 unsigned long p = *ppos; 163 void *dst; 164 int err = 0; 165 unsigned long total_size; 166 167 if (info->state != FBINFO_STATE_RUNNING) 168 return -EPERM; 169 170 total_size = info->fix.smem_len; 171 172 if (p > total_size) 173 return -EFBIG; 174 175 if (count > total_size) { 176 err = -EFBIG; 177 count = total_size; 178 } 179 180 if (count + p > total_size) { 181 if (!err) 182 err = -ENOSPC; 183 184 count = total_size - p; 185 } 186 187 dst = (void __force *) (info->screen_base + p); 188 189 if (copy_from_user(dst, buf, count)) 190 err = -EFAULT; 191 192 if (!err) 193 *ppos += count; 194 195 hecubafb_dpy_update(par); 196 197 return (err) ? err : count; 198 } 199 200 static const struct fb_ops hecubafb_ops = { 201 .owner = THIS_MODULE, 202 .fb_read = fb_sys_read, 203 .fb_write = hecubafb_write, 204 .fb_fillrect = hecubafb_fillrect, 205 .fb_copyarea = hecubafb_copyarea, 206 .fb_imageblit = hecubafb_imageblit, 207 }; 208 209 static struct fb_deferred_io hecubafb_defio = { 210 .delay = HZ, 211 .deferred_io = hecubafb_dpy_deferred_io, 212 }; 213 214 static 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; 273 err_fbreg: 274 framebuffer_release(info); 275 err_fballoc: 276 vfree(videomemory); 277 err_videomem_alloc: 278 module_put(board->owner); 279 return retval; 280 } 281 282 static 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 299 static struct platform_driver hecubafb_driver = { 300 .probe = hecubafb_probe, 301 .remove = hecubafb_remove, 302 .driver = { 303 .name = "hecubafb", 304 }, 305 }; 306 module_platform_driver(hecubafb_driver); 307 308 MODULE_DESCRIPTION("fbdev driver for Hecuba/Apollo controller"); 309 MODULE_AUTHOR("Jaya Kumar"); 310 MODULE_LICENSE("GPL"); 311