linux/drivers/video/broadsheetfb.c
<<
>>
Prefs
   1/*
   2 * broadsheetfb.c -- FB driver for E-Ink Broadsheet controller
   3 *
   4 * Copyright (C) 2008, Jaya Kumar
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License. See the file COPYING in the main directory of this archive for
   8 * more details.
   9 *
  10 * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
  11 *
  12 * This driver is written to be used with the Broadsheet display controller.
  13 *
  14 * It is intended to be architecture independent. A board specific driver
  15 * must be used to perform all the physical IO interactions.
  16 *
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/kernel.h>
  21#include <linux/errno.h>
  22#include <linux/string.h>
  23#include <linux/mm.h>
  24#include <linux/slab.h>
  25#include <linux/vmalloc.h>
  26#include <linux/delay.h>
  27#include <linux/interrupt.h>
  28#include <linux/fb.h>
  29#include <linux/init.h>
  30#include <linux/platform_device.h>
  31#include <linux/list.h>
  32#include <linux/uaccess.h>
  33
  34#include <video/broadsheetfb.h>
  35
  36/* Display specific information */
  37#define DPY_W 800
  38#define DPY_H 600
  39
  40static struct fb_fix_screeninfo broadsheetfb_fix __devinitdata = {
  41        .id =           "broadsheetfb",
  42        .type =         FB_TYPE_PACKED_PIXELS,
  43        .visual =       FB_VISUAL_STATIC_PSEUDOCOLOR,
  44        .xpanstep =     0,
  45        .ypanstep =     0,
  46        .ywrapstep =    0,
  47        .line_length =  DPY_W,
  48        .accel =        FB_ACCEL_NONE,
  49};
  50
  51static struct fb_var_screeninfo broadsheetfb_var __devinitdata = {
  52        .xres           = DPY_W,
  53        .yres           = DPY_H,
  54        .xres_virtual   = DPY_W,
  55        .yres_virtual   = DPY_H,
  56        .bits_per_pixel = 8,
  57        .grayscale      = 1,
  58        .red =          { 0, 4, 0 },
  59        .green =        { 0, 4, 0 },
  60        .blue =         { 0, 4, 0 },
  61        .transp =       { 0, 0, 0 },
  62};
  63
  64/* main broadsheetfb functions */
  65static void broadsheet_issue_data(struct broadsheetfb_par *par, u16 data)
  66{
  67        par->board->set_ctl(par, BS_WR, 0);
  68        par->board->set_hdb(par, data);
  69        par->board->set_ctl(par, BS_WR, 1);
  70}
  71
  72static void broadsheet_issue_cmd(struct broadsheetfb_par *par, u16 data)
  73{
  74        par->board->set_ctl(par, BS_DC, 0);
  75        broadsheet_issue_data(par, data);
  76}
  77
  78static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
  79{
  80        par->board->wait_for_rdy(par);
  81
  82        par->board->set_ctl(par, BS_CS, 0);
  83        broadsheet_issue_cmd(par, data);
  84        par->board->set_ctl(par, BS_DC, 1);
  85        par->board->set_ctl(par, BS_CS, 1);
  86}
  87
  88static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
  89                                        int argc, u16 *argv)
  90{
  91        int i;
  92
  93        par->board->wait_for_rdy(par);
  94
  95        par->board->set_ctl(par, BS_CS, 0);
  96        broadsheet_issue_cmd(par, cmd);
  97        par->board->set_ctl(par, BS_DC, 1);
  98
  99        for (i = 0; i < argc; i++)
 100                broadsheet_issue_data(par, argv[i]);
 101        par->board->set_ctl(par, BS_CS, 1);
 102}
 103
 104static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
 105                                        u16 *data)
 106{
 107        int i;
 108        u16 tmp;
 109
 110        par->board->set_ctl(par, BS_CS, 0);
 111        par->board->set_ctl(par, BS_DC, 1);
 112
 113        for (i = 0; i < size; i++) {
 114                par->board->set_ctl(par, BS_WR, 0);
 115                tmp = (data[i] & 0x0F) << 4;
 116                tmp |= (data[i] & 0x0F00) << 4;
 117                par->board->set_hdb(par, tmp);
 118                par->board->set_ctl(par, BS_WR, 1);
 119        }
 120
 121        par->board->set_ctl(par, BS_CS, 1);
 122}
 123
 124static u16 broadsheet_get_data(struct broadsheetfb_par *par)
 125{
 126        u16 res;
 127        /* wait for ready to go hi. (lo is busy) */
 128        par->board->wait_for_rdy(par);
 129
 130        /* cs lo, dc lo for cmd, we lo for each data, db as usual */
 131        par->board->set_ctl(par, BS_DC, 1);
 132        par->board->set_ctl(par, BS_CS, 0);
 133        par->board->set_ctl(par, BS_WR, 0);
 134
 135        res = par->board->get_hdb(par);
 136
 137        /* strobe wr */
 138        par->board->set_ctl(par, BS_WR, 1);
 139        par->board->set_ctl(par, BS_CS, 1);
 140
 141        return res;
 142}
 143
 144static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
 145                                        u16 data)
 146{
 147        /* wait for ready to go hi. (lo is busy) */
 148        par->board->wait_for_rdy(par);
 149
 150        /* cs lo, dc lo for cmd, we lo for each data, db as usual */
 151        par->board->set_ctl(par, BS_CS, 0);
 152
 153        broadsheet_issue_cmd(par, BS_CMD_WR_REG);
 154
 155        par->board->set_ctl(par, BS_DC, 1);
 156
 157        broadsheet_issue_data(par, reg);
 158        broadsheet_issue_data(par, data);
 159
 160        par->board->set_ctl(par, BS_CS, 1);
 161}
 162
 163static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
 164{
 165        broadsheet_send_command(par, reg);
 166        msleep(100);
 167        return broadsheet_get_data(par);
 168}
 169
 170static void __devinit broadsheet_init_display(struct broadsheetfb_par *par)
 171{
 172        u16 args[5];
 173
 174        args[0] = DPY_W;
 175        args[1] = DPY_H;
 176        args[2] = (100 | (1 << 8) | (1 << 9)); /* sdcfg */
 177        args[3] = 2; /* gdrv cfg */
 178        args[4] = (4 | (1 << 7)); /* lut index format */
 179        broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
 180
 181        /* did the controller really set it? */
 182        broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
 183
 184        args[0] = 4; /* fsync len */
 185        args[1] = (10 << 8) | 4; /* fend/fbegin len */
 186        args[2] = 10; /* line sync len */
 187        args[3] = (100 << 8) | 4; /* line end/begin len */
 188        args[4] = 6; /* pixel clock cfg */
 189        broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);
 190
 191        /* setup waveform */
 192        args[0] = 0x886;
 193        args[1] = 0;
 194        broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
 195
 196        broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
 197
 198        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
 199
 200        broadsheet_write_reg(par, 0x330, 0x84);
 201
 202        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
 203
 204        args[0] = (0x3 << 4);
 205        broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
 206
 207        args[0] = 0x154;
 208        broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
 209
 210        broadsheet_burst_write(par, DPY_W*DPY_H/2,
 211                                (u16 *) par->info->screen_base);
 212
 213        broadsheet_send_command(par, BS_CMD_LD_IMG_END);
 214
 215        args[0] = 0x4300;
 216        broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
 217
 218        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
 219
 220        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
 221
 222        par->board->wait_for_rdy(par);
 223}
 224
 225static void __devinit broadsheet_init(struct broadsheetfb_par *par)
 226{
 227        broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
 228        /* the controller needs a second */
 229        msleep(1000);
 230        broadsheet_init_display(par);
 231}
 232
 233static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
 234                                                u16 y1, u16 y2)
 235{
 236        u16 args[5];
 237        unsigned char *buf = (unsigned char *)par->info->screen_base;
 238
 239        /* y1 must be a multiple of 4 so drop the lower bits */
 240        y1 &= 0xFFFC;
 241        /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
 242        y2 |= 0x0003;
 243
 244        args[0] = 0x3 << 4;
 245        args[1] = 0;
 246        args[2] = y1;
 247        args[3] = cpu_to_le16(par->info->var.xres);
 248        args[4] = y2;
 249        broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
 250
 251        args[0] = 0x154;
 252        broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
 253
 254        buf += y1 * par->info->var.xres;
 255        broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
 256                                (u16 *) buf);
 257
 258        broadsheet_send_command(par, BS_CMD_LD_IMG_END);
 259
 260        args[0] = 0x4300;
 261        broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
 262
 263        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
 264
 265        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
 266
 267        par->board->wait_for_rdy(par);
 268
 269}
 270
 271static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
 272{
 273        u16 args[5];
 274
 275        args[0] = 0x3 << 4;
 276        broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
 277
 278        args[0] = 0x154;
 279        broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
 280        broadsheet_burst_write(par, DPY_W*DPY_H/2,
 281                                (u16 *) par->info->screen_base);
 282
 283        broadsheet_send_command(par, BS_CMD_LD_IMG_END);
 284
 285        args[0] = 0x4300;
 286        broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
 287
 288        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
 289
 290        broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
 291
 292        par->board->wait_for_rdy(par);
 293
 294}
 295
 296/* this is called back from the deferred io workqueue */
 297static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
 298                                struct list_head *pagelist)
 299{
 300        u16 y1 = 0, h = 0;
 301        int prev_index = -1;
 302        struct page *cur;
 303        struct fb_deferred_io *fbdefio = info->fbdefio;
 304        int h_inc;
 305        u16 yres = info->var.yres;
 306        u16 xres = info->var.xres;
 307
 308        /* height increment is fixed per page */
 309        h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
 310
 311        /* walk the written page list and swizzle the data */
 312        list_for_each_entry(cur, &fbdefio->pagelist, lru) {
 313                if (prev_index < 0) {
 314                        /* just starting so assign first page */
 315                        y1 = (cur->index << PAGE_SHIFT) / xres;
 316                        h = h_inc;
 317                } else if ((prev_index + 1) == cur->index) {
 318                        /* this page is consecutive so increase our height */
 319                        h += h_inc;
 320                } else {
 321                        /* page not consecutive, issue previous update first */
 322                        broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);
 323                        /* start over with our non consecutive page */
 324                        y1 = (cur->index << PAGE_SHIFT) / xres;
 325                        h = h_inc;
 326                }
 327                prev_index = cur->index;
 328        }
 329
 330        /* if we still have any pages to update we do so now */
 331        if (h >= yres) {
 332                /* its a full screen update, just do it */
 333                broadsheetfb_dpy_update(info->par);
 334        } else {
 335                broadsheetfb_dpy_update_pages(info->par, y1,
 336                                                min((u16) (y1 + h), yres));
 337        }
 338}
 339
 340static void broadsheetfb_fillrect(struct fb_info *info,
 341                                   const struct fb_fillrect *rect)
 342{
 343        struct broadsheetfb_par *par = info->par;
 344
 345        sys_fillrect(info, rect);
 346
 347        broadsheetfb_dpy_update(par);
 348}
 349
 350static void broadsheetfb_copyarea(struct fb_info *info,
 351                                   const struct fb_copyarea *area)
 352{
 353        struct broadsheetfb_par *par = info->par;
 354
 355        sys_copyarea(info, area);
 356
 357        broadsheetfb_dpy_update(par);
 358}
 359
 360static void broadsheetfb_imageblit(struct fb_info *info,
 361                                const struct fb_image *image)
 362{
 363        struct broadsheetfb_par *par = info->par;
 364
 365        sys_imageblit(info, image);
 366
 367        broadsheetfb_dpy_update(par);
 368}
 369
 370/*
 371 * this is the slow path from userspace. they can seek and write to
 372 * the fb. it's inefficient to do anything less than a full screen draw
 373 */
 374static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
 375                                size_t count, loff_t *ppos)
 376{
 377        struct broadsheetfb_par *par = info->par;
 378        unsigned long p = *ppos;
 379        void *dst;
 380        int err = 0;
 381        unsigned long total_size;
 382
 383        if (info->state != FBINFO_STATE_RUNNING)
 384                return -EPERM;
 385
 386        total_size = info->fix.smem_len;
 387
 388        if (p > total_size)
 389                return -EFBIG;
 390
 391        if (count > total_size) {
 392                err = -EFBIG;
 393                count = total_size;
 394        }
 395
 396        if (count + p > total_size) {
 397                if (!err)
 398                        err = -ENOSPC;
 399
 400                count = total_size - p;
 401        }
 402
 403        dst = (void *)(info->screen_base + p);
 404
 405        if (copy_from_user(dst, buf, count))
 406                err = -EFAULT;
 407
 408        if  (!err)
 409                *ppos += count;
 410
 411        broadsheetfb_dpy_update(par);
 412
 413        return (err) ? err : count;
 414}
 415
 416static struct fb_ops broadsheetfb_ops = {
 417        .owner          = THIS_MODULE,
 418        .fb_read        = fb_sys_read,
 419        .fb_write       = broadsheetfb_write,
 420        .fb_fillrect    = broadsheetfb_fillrect,
 421        .fb_copyarea    = broadsheetfb_copyarea,
 422        .fb_imageblit   = broadsheetfb_imageblit,
 423};
 424
 425static struct fb_deferred_io broadsheetfb_defio = {
 426        .delay          = HZ/4,
 427        .deferred_io    = broadsheetfb_dpy_deferred_io,
 428};
 429
 430static int __devinit broadsheetfb_probe(struct platform_device *dev)
 431{
 432        struct fb_info *info;
 433        struct broadsheet_board *board;
 434        int retval = -ENOMEM;
 435        int videomemorysize;
 436        unsigned char *videomemory;
 437        struct broadsheetfb_par *par;
 438        int i;
 439
 440        /* pick up board specific routines */
 441        board = dev->dev.platform_data;
 442        if (!board)
 443                return -EINVAL;
 444
 445        /* try to count device specific driver, if can't, platform recalls */
 446        if (!try_module_get(board->owner))
 447                return -ENODEV;
 448
 449        info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
 450        if (!info)
 451                goto err;
 452
 453        videomemorysize = (DPY_W*DPY_H);
 454        videomemory = vmalloc(videomemorysize);
 455        if (!videomemory)
 456                goto err_fb_rel;
 457
 458        memset(videomemory, 0, videomemorysize);
 459
 460        info->screen_base = (char *)videomemory;
 461        info->fbops = &broadsheetfb_ops;
 462
 463        info->var = broadsheetfb_var;
 464        info->fix = broadsheetfb_fix;
 465        info->fix.smem_len = videomemorysize;
 466        par = info->par;
 467        par->info = info;
 468        par->board = board;
 469        par->write_reg = broadsheet_write_reg;
 470        par->read_reg = broadsheet_read_reg;
 471        init_waitqueue_head(&par->waitq);
 472
 473        info->flags = FBINFO_FLAG_DEFAULT;
 474
 475        info->fbdefio = &broadsheetfb_defio;
 476        fb_deferred_io_init(info);
 477
 478        retval = fb_alloc_cmap(&info->cmap, 16, 0);
 479        if (retval < 0) {
 480                dev_err(&dev->dev, "Failed to allocate colormap\n");
 481                goto err_vfree;
 482        }
 483
 484        /* set cmap */
 485        for (i = 0; i < 16; i++)
 486                info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;
 487        memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);
 488        memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);
 489
 490        retval = par->board->setup_irq(info);
 491        if (retval < 0)
 492                goto err_cmap;
 493
 494        /* this inits the dpy */
 495        retval = board->init(par);
 496        if (retval < 0)
 497                goto err_free_irq;
 498
 499        broadsheet_init(par);
 500
 501        retval = register_framebuffer(info);
 502        if (retval < 0)
 503                goto err_free_irq;
 504        platform_set_drvdata(dev, info);
 505
 506        printk(KERN_INFO
 507               "fb%d: Broadsheet frame buffer, using %dK of video memory\n",
 508               info->node, videomemorysize >> 10);
 509
 510
 511        return 0;
 512
 513err_free_irq:
 514        board->cleanup(par);
 515err_cmap:
 516        fb_dealloc_cmap(&info->cmap);
 517err_vfree:
 518        vfree(videomemory);
 519err_fb_rel:
 520        framebuffer_release(info);
 521err:
 522        module_put(board->owner);
 523        return retval;
 524
 525}
 526
 527static int __devexit broadsheetfb_remove(struct platform_device *dev)
 528{
 529        struct fb_info *info = platform_get_drvdata(dev);
 530
 531        if (info) {
 532                struct broadsheetfb_par *par = info->par;
 533                unregister_framebuffer(info);
 534                fb_deferred_io_cleanup(info);
 535                par->board->cleanup(par);
 536                fb_dealloc_cmap(&info->cmap);
 537                vfree((void *)info->screen_base);
 538                module_put(par->board->owner);
 539                framebuffer_release(info);
 540        }
 541        return 0;
 542}
 543
 544static struct platform_driver broadsheetfb_driver = {
 545        .probe  = broadsheetfb_probe,
 546        .remove = broadsheetfb_remove,
 547        .driver = {
 548                .owner  = THIS_MODULE,
 549                .name   = "broadsheetfb",
 550        },
 551};
 552
 553static int __init broadsheetfb_init(void)
 554{
 555        return platform_driver_register(&broadsheetfb_driver);
 556}
 557
 558static void __exit broadsheetfb_exit(void)
 559{
 560        platform_driver_unregister(&broadsheetfb_driver);
 561}
 562
 563module_init(broadsheetfb_init);
 564module_exit(broadsheetfb_exit);
 565
 566MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
 567MODULE_AUTHOR("Jaya Kumar");
 568MODULE_LICENSE("GPL");
 569