linux/drivers/video/fbdev/hecubafb.c
<<
>>
Prefs
   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,
 119                                struct list_head *pagelist)
 120{
 121        hecubafb_dpy_update(info->par);
 122}
 123
 124static 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
 134static 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
 144static 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 */
 158static 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
 200static 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
 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");
 311