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 = par->info->screen_buffer; 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_defio_damage_range(struct fb_info *info, off_t off, size_t len) 124{ 125 struct hecubafb_par *par = info->par; 126 127 hecubafb_dpy_update(par); 128} 129 130static void hecubafb_defio_damage_area(struct fb_info *info, u32 x, u32 y, 131 u32 width, u32 height) 132{ 133 struct hecubafb_par *par = info->par; 134 135 hecubafb_dpy_update(par); 136} 137 138FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(hecubafb, 139 hecubafb_defio_damage_range, 140 hecubafb_defio_damage_area) 141 142static const struct fb_ops hecubafb_ops = { 143 .owner = THIS_MODULE, 144 FB_DEFAULT_DEFERRED_OPS(hecubafb), 145}; 146 147static struct fb_deferred_io hecubafb_defio = { 148 .delay = HZ, 149 .deferred_io = hecubafb_dpy_deferred_io, 150}; 151 152static int hecubafb_probe(struct platform_device *dev) 153{ 154 struct fb_info *info; 155 struct hecuba_board *board; 156 int retval = -ENOMEM; 157 int videomemorysize; 158 unsigned char *videomemory; 159 struct hecubafb_par *par; 160 161 /* pick up board specific routines */ 162 board = dev->dev.platform_data; 163 if (!board) 164 return -EINVAL; 165 166 /* try to count device specific driver, if can't, platform recalls */ 167 if (!try_module_get(board->owner)) 168 return -ENODEV; 169 170 videomemorysize = (DPY_W*DPY_H)/8; 171 172 videomemory = vzalloc(videomemorysize); 173 if (!videomemory) 174 goto err_videomem_alloc; 175 176 info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev); 177 if (!info) 178 goto err_fballoc; 179 180 info->screen_buffer = videomemory; 181 info->fbops = &hecubafb_ops; 182 183 info->var = hecubafb_var; 184 info->fix = hecubafb_fix; 185 info->fix.smem_len = videomemorysize; 186 par = info->par; 187 par->info = info; 188 par->board = board; 189 par->send_command = apollo_send_command; 190 par->send_data = apollo_send_data; 191 192 info->flags = FBINFO_VIRTFB; 193 194 info->fbdefio = &hecubafb_defio; 195 fb_deferred_io_init(info); 196 197 retval = register_framebuffer(info); 198 if (retval < 0) 199 goto err_fbreg; 200 platform_set_drvdata(dev, info); 201 202 fb_info(info, "Hecuba frame buffer device, using %dK of video memory\n", 203 videomemorysize >> 10); 204 205 /* this inits the dpy */ 206 retval = par->board->init(par); 207 if (retval < 0) 208 goto err_fbreg; 209 210 return 0; 211err_fbreg: 212 framebuffer_release(info); 213err_fballoc: 214 vfree(videomemory); 215err_videomem_alloc: 216 module_put(board->owner); 217 return retval; 218} 219 220static void hecubafb_remove(struct platform_device *dev) 221{ 222 struct fb_info *info = platform_get_drvdata(dev); 223 224 if (info) { 225 struct hecubafb_par *par = info->par; 226 fb_deferred_io_cleanup(info); 227 unregister_framebuffer(info); 228 vfree(info->screen_buffer); 229 if (par->board->remove) 230 par->board->remove(par); 231 module_put(par->board->owner); 232 framebuffer_release(info); 233 } 234} 235 236static struct platform_driver hecubafb_driver = { 237 .probe = hecubafb_probe, 238 .remove_new = hecubafb_remove, 239 .driver = { 240 .name = "hecubafb", 241 }, 242}; 243module_platform_driver(hecubafb_driver); 244 245MODULE_DESCRIPTION("fbdev driver for Hecuba/Apollo controller"); 246MODULE_AUTHOR("Jaya Kumar"); 247MODULE_LICENSE("GPL"); 248