linux/drivers/video/metronomefb.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/metronomefb.c -- FB driver for Metronome 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 work was made possible by help and equipment support from E-Ink
  13 * Corporation. http://www.eink.com/
  14 *
  15 * This driver is written to be used with the Metronome display controller.
  16 * It is intended to be architecture independent. A board specific driver
  17 * must be used to perform all the physical IO interactions. An example
  18 * is provided as am200epd.c
  19 *
  20 */
  21#include <linux/module.h>
  22#include <linux/kernel.h>
  23#include <linux/errno.h>
  24#include <linux/string.h>
  25#include <linux/mm.h>
  26#include <linux/vmalloc.h>
  27#include <linux/delay.h>
  28#include <linux/interrupt.h>
  29#include <linux/fb.h>
  30#include <linux/init.h>
  31#include <linux/platform_device.h>
  32#include <linux/list.h>
  33#include <linux/firmware.h>
  34#include <linux/dma-mapping.h>
  35#include <linux/uaccess.h>
  36#include <linux/irq.h>
  37
  38#include <video/metronomefb.h>
  39
  40#include <asm/unaligned.h>
  41
  42/* Display specific information */
  43#define DPY_W 832
  44#define DPY_H 622
  45
  46static int user_wfm_size;
  47
  48/* frame differs from image. frame includes non-visible pixels */
  49struct epd_frame {
  50        int fw; /* frame width */
  51        int fh; /* frame height */
  52        u16 config[4];
  53        int wfm_size;
  54};
  55
  56static struct epd_frame epd_frame_table[] = {
  57        {
  58                .fw = 832,
  59                .fh = 622,
  60                .config = {
  61                        15 /* sdlew */
  62                        | 2 << 8 /* sdosz */
  63                        | 0 << 11 /* sdor */
  64                        | 0 << 12 /* sdces */
  65                        | 0 << 15, /* sdcer */
  66                        42 /* gdspl */
  67                        | 1 << 8 /* gdr1 */
  68                        | 1 << 9 /* sdshr */
  69                        | 0 << 15, /* gdspp */
  70                        18 /* gdspw */
  71                        | 0 << 15, /* dispc */
  72                        599 /* vdlc */
  73                        | 0 << 11 /* dsi */
  74                        | 0 << 12, /* dsic */
  75                },
  76                .wfm_size = 47001,
  77        },
  78        {
  79                .fw = 1088,
  80                .fh = 791,
  81                .config = {
  82                        0x0104,
  83                        0x031f,
  84                        0x0088,
  85                        0x02ff,
  86                },
  87                .wfm_size = 46770,
  88        },
  89        {
  90                .fw = 1200,
  91                .fh = 842,
  92                .config = {
  93                        0x0101,
  94                        0x030e,
  95                        0x0012,
  96                        0x0280,
  97                },
  98                .wfm_size = 46770,
  99        },
 100};
 101
 102static struct fb_fix_screeninfo metronomefb_fix = {
 103        .id =           "metronomefb",
 104        .type =         FB_TYPE_PACKED_PIXELS,
 105        .visual =       FB_VISUAL_STATIC_PSEUDOCOLOR,
 106        .xpanstep =     0,
 107        .ypanstep =     0,
 108        .ywrapstep =    0,
 109        .line_length =  DPY_W,
 110        .accel =        FB_ACCEL_NONE,
 111};
 112
 113static struct fb_var_screeninfo metronomefb_var = {
 114        .xres           = DPY_W,
 115        .yres           = DPY_H,
 116        .xres_virtual   = DPY_W,
 117        .yres_virtual   = DPY_H,
 118        .bits_per_pixel = 8,
 119        .grayscale      = 1,
 120        .nonstd         = 1,
 121        .red =          { 4, 3, 0 },
 122        .green =        { 0, 0, 0 },
 123        .blue =         { 0, 0, 0 },
 124        .transp =       { 0, 0, 0 },
 125};
 126
 127/* the waveform structure that is coming from userspace firmware */
 128struct waveform_hdr {
 129        u8 stuff[32];
 130
 131        u8 wmta[3];
 132        u8 fvsn;
 133
 134        u8 luts;
 135        u8 mc;
 136        u8 trc;
 137        u8 stuff3;
 138
 139        u8 endb;
 140        u8 swtb;
 141        u8 stuff2a[2];
 142
 143        u8 stuff2b[3];
 144        u8 wfm_cs;
 145} __attribute__ ((packed));
 146
 147/* main metronomefb functions */
 148static u8 calc_cksum(int start, int end, u8 *mem)
 149{
 150        u8 tmp = 0;
 151        int i;
 152
 153        for (i = start; i < end; i++)
 154                tmp += mem[i];
 155
 156        return tmp;
 157}
 158
 159static u16 calc_img_cksum(u16 *start, int length)
 160{
 161        u16 tmp = 0;
 162
 163        while (length--)
 164                tmp += *start++;
 165
 166        return tmp;
 167}
 168
 169/* here we decode the incoming waveform file and populate metromem */
 170static int load_waveform(u8 *mem, size_t size, int m, int t,
 171                         struct metronomefb_par *par)
 172{
 173        int tta;
 174        int wmta;
 175        int trn = 0;
 176        int i;
 177        unsigned char v;
 178        u8 cksum;
 179        int cksum_idx;
 180        int wfm_idx, owfm_idx;
 181        int mem_idx = 0;
 182        struct waveform_hdr *wfm_hdr;
 183        u8 *metromem = par->metromem_wfm;
 184        struct device *dev = par->info->dev;
 185
 186        if (user_wfm_size)
 187                epd_frame_table[par->dt].wfm_size = user_wfm_size;
 188
 189        if (size != epd_frame_table[par->dt].wfm_size) {
 190                dev_err(dev, "Error: unexpected size %Zd != %d\n", size,
 191                                        epd_frame_table[par->dt].wfm_size);
 192                return -EINVAL;
 193        }
 194
 195        wfm_hdr = (struct waveform_hdr *) mem;
 196
 197        if (wfm_hdr->fvsn != 1) {
 198                dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn);
 199                return -EINVAL;
 200        }
 201        if (wfm_hdr->luts != 0) {
 202                dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts);
 203                return -EINVAL;
 204        }
 205        cksum = calc_cksum(32, 47, mem);
 206        if (cksum != wfm_hdr->wfm_cs) {
 207                dev_err(dev, "Error: bad cksum %x != %x\n", cksum,
 208                                        wfm_hdr->wfm_cs);
 209                return -EINVAL;
 210        }
 211        wfm_hdr->mc += 1;
 212        wfm_hdr->trc += 1;
 213        for (i = 0; i < 5; i++) {
 214                if (*(wfm_hdr->stuff2a + i) != 0) {
 215                        dev_err(dev, "Error: unexpected value in padding\n");
 216                        return -EINVAL;
 217                }
 218        }
 219
 220        /* calculating trn. trn is something used to index into
 221        the waveform. presumably selecting the right one for the
 222        desired temperature. it works out the offset of the first
 223        v that exceeds the specified temperature */
 224        if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
 225                return -EINVAL;
 226
 227        for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
 228                if (mem[i] > t) {
 229                        trn = i - sizeof(*wfm_hdr) - 1;
 230                        break;
 231                }
 232        }
 233
 234        /* check temperature range table checksum */
 235        cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
 236        if (cksum_idx > size)
 237                return -EINVAL;
 238        cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
 239        if (cksum != mem[cksum_idx]) {
 240                dev_err(dev, "Error: bad temperature range table cksum"
 241                                " %x != %x\n", cksum, mem[cksum_idx]);
 242                return -EINVAL;
 243        }
 244
 245        /* check waveform mode table address checksum */
 246        wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
 247        cksum_idx = wmta + m*4 + 3;
 248        if (cksum_idx > size)
 249                return -EINVAL;
 250        cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
 251        if (cksum != mem[cksum_idx]) {
 252                dev_err(dev, "Error: bad mode table address cksum"
 253                                " %x != %x\n", cksum, mem[cksum_idx]);
 254                return -EINVAL;
 255        }
 256
 257        /* check waveform temperature table address checksum */
 258        tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
 259        cksum_idx = tta + trn*4 + 3;
 260        if (cksum_idx > size)
 261                return -EINVAL;
 262        cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
 263        if (cksum != mem[cksum_idx]) {
 264                dev_err(dev, "Error: bad temperature table address cksum"
 265                        " %x != %x\n", cksum, mem[cksum_idx]);
 266                return -EINVAL;
 267        }
 268
 269        /* here we do the real work of putting the waveform into the
 270        metromem buffer. this does runlength decoding of the waveform */
 271        wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
 272        owfm_idx = wfm_idx;
 273        if (wfm_idx > size)
 274                return -EINVAL;
 275        while (wfm_idx < size) {
 276                unsigned char rl;
 277                v = mem[wfm_idx++];
 278                if (v == wfm_hdr->swtb) {
 279                        while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
 280                                wfm_idx < size)
 281                                metromem[mem_idx++] = v;
 282
 283                        continue;
 284                }
 285
 286                if (v == wfm_hdr->endb)
 287                        break;
 288
 289                rl = mem[wfm_idx++];
 290                for (i = 0; i <= rl; i++)
 291                        metromem[mem_idx++] = v;
 292        }
 293
 294        cksum_idx = wfm_idx;
 295        if (cksum_idx > size)
 296                return -EINVAL;
 297        cksum = calc_cksum(owfm_idx, cksum_idx, mem);
 298        if (cksum != mem[cksum_idx]) {
 299                dev_err(dev, "Error: bad waveform data cksum"
 300                                " %x != %x\n", cksum, mem[cksum_idx]);
 301                return -EINVAL;
 302        }
 303        par->frame_count = (mem_idx/64);
 304
 305        return 0;
 306}
 307
 308static int metronome_display_cmd(struct metronomefb_par *par)
 309{
 310        int i;
 311        u16 cs;
 312        u16 opcode;
 313        static u8 borderval;
 314
 315        /* setup display command
 316        we can't immediately set the opcode since the controller
 317        will try parse the command before we've set it all up
 318        so we just set cs here and set the opcode at the end */
 319
 320        if (par->metromem_cmd->opcode == 0xCC40)
 321                opcode = cs = 0xCC41;
 322        else
 323                opcode = cs = 0xCC40;
 324
 325        /* set the args ( 2 bytes ) for display */
 326        i = 0;
 327        par->metromem_cmd->args[i] =    1 << 3 /* border update */
 328                                        | ((borderval++ % 4) & 0x0F) << 4
 329                                        | (par->frame_count - 1) << 8;
 330        cs += par->metromem_cmd->args[i++];
 331
 332        /* the rest are 0 */
 333        memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
 334
 335        par->metromem_cmd->csum = cs;
 336        par->metromem_cmd->opcode = opcode; /* display cmd */
 337
 338        return par->board->met_wait_event_intr(par);
 339}
 340
 341static int metronome_powerup_cmd(struct metronomefb_par *par)
 342{
 343        int i;
 344        u16 cs;
 345
 346        /* setup power up command */
 347        par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
 348        cs = par->metromem_cmd->opcode;
 349
 350        /* set pwr1,2,3 to 1024 */
 351        for (i = 0; i < 3; i++) {
 352                par->metromem_cmd->args[i] = 1024;
 353                cs += par->metromem_cmd->args[i];
 354        }
 355
 356        /* the rest are 0 */
 357        memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
 358
 359        par->metromem_cmd->csum = cs;
 360
 361        msleep(1);
 362        par->board->set_rst(par, 1);
 363
 364        msleep(1);
 365        par->board->set_stdby(par, 1);
 366
 367        return par->board->met_wait_event(par);
 368}
 369
 370static int metronome_config_cmd(struct metronomefb_par *par)
 371{
 372        /* setup config command
 373        we can't immediately set the opcode since the controller
 374        will try parse the command before we've set it all up */
 375
 376        memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
 377                sizeof(epd_frame_table[par->dt].config));
 378        /* the rest are 0 */
 379        memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2);
 380
 381        par->metromem_cmd->csum = 0xCC10;
 382        par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
 383        par->metromem_cmd->opcode = 0xCC10; /* config cmd */
 384
 385        return par->board->met_wait_event(par);
 386}
 387
 388static int metronome_init_cmd(struct metronomefb_par *par)
 389{
 390        int i;
 391        u16 cs;
 392
 393        /* setup init command
 394        we can't immediately set the opcode since the controller
 395        will try parse the command before we've set it all up
 396        so we just set cs here and set the opcode at the end */
 397
 398        cs = 0xCC20;
 399
 400        /* set the args ( 2 bytes ) for init */
 401        i = 0;
 402        par->metromem_cmd->args[i] = 0;
 403        cs += par->metromem_cmd->args[i++];
 404
 405        /* the rest are 0 */
 406        memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
 407
 408        par->metromem_cmd->csum = cs;
 409        par->metromem_cmd->opcode = 0xCC20; /* init cmd */
 410
 411        return par->board->met_wait_event(par);
 412}
 413
 414static int metronome_init_regs(struct metronomefb_par *par)
 415{
 416        int res;
 417
 418        res = par->board->setup_io(par);
 419        if (res)
 420                return res;
 421
 422        res = metronome_powerup_cmd(par);
 423        if (res)
 424                return res;
 425
 426        res = metronome_config_cmd(par);
 427        if (res)
 428                return res;
 429
 430        res = metronome_init_cmd(par);
 431
 432        return res;
 433}
 434
 435static void metronomefb_dpy_update(struct metronomefb_par *par)
 436{
 437        int fbsize;
 438        u16 cksum;
 439        unsigned char *buf = (unsigned char __force *)par->info->screen_base;
 440
 441        fbsize = par->info->fix.smem_len;
 442        /* copy from vm to metromem */
 443        memcpy(par->metromem_img, buf, fbsize);
 444
 445        cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
 446        *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
 447        metronome_display_cmd(par);
 448}
 449
 450static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
 451{
 452        int i;
 453        u16 csum = 0;
 454        u16 *buf = (u16 __force *)(par->info->screen_base + index);
 455        u16 *img = (u16 *)(par->metromem_img + index);
 456
 457        /* swizzle from vm to metromem and recalc cksum at the same time*/
 458        for (i = 0; i < PAGE_SIZE/2; i++) {
 459                *(img + i) = (buf[i] << 5) & 0xE0E0;
 460                csum += *(img + i);
 461        }
 462        return csum;
 463}
 464
 465/* this is called back from the deferred io workqueue */
 466static void metronomefb_dpy_deferred_io(struct fb_info *info,
 467                                struct list_head *pagelist)
 468{
 469        u16 cksum;
 470        struct page *cur;
 471        struct fb_deferred_io *fbdefio = info->fbdefio;
 472        struct metronomefb_par *par = info->par;
 473
 474        /* walk the written page list and swizzle the data */
 475        list_for_each_entry(cur, &fbdefio->pagelist, lru) {
 476                cksum = metronomefb_dpy_update_page(par,
 477                                        (cur->index << PAGE_SHIFT));
 478                par->metromem_img_csum -= par->csum_table[cur->index];
 479                par->csum_table[cur->index] = cksum;
 480                par->metromem_img_csum += cksum;
 481        }
 482
 483        metronome_display_cmd(par);
 484}
 485
 486static void metronomefb_fillrect(struct fb_info *info,
 487                                   const struct fb_fillrect *rect)
 488{
 489        struct metronomefb_par *par = info->par;
 490
 491        sys_fillrect(info, rect);
 492        metronomefb_dpy_update(par);
 493}
 494
 495static void metronomefb_copyarea(struct fb_info *info,
 496                                   const struct fb_copyarea *area)
 497{
 498        struct metronomefb_par *par = info->par;
 499
 500        sys_copyarea(info, area);
 501        metronomefb_dpy_update(par);
 502}
 503
 504static void metronomefb_imageblit(struct fb_info *info,
 505                                const struct fb_image *image)
 506{
 507        struct metronomefb_par *par = info->par;
 508
 509        sys_imageblit(info, image);
 510        metronomefb_dpy_update(par);
 511}
 512
 513/*
 514 * this is the slow path from userspace. they can seek and write to
 515 * the fb. it is based on fb_sys_write
 516 */
 517static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
 518                                size_t count, loff_t *ppos)
 519{
 520        struct metronomefb_par *par = info->par;
 521        unsigned long p = *ppos;
 522        void *dst;
 523        int err = 0;
 524        unsigned long total_size;
 525
 526        if (info->state != FBINFO_STATE_RUNNING)
 527                return -EPERM;
 528
 529        total_size = info->fix.smem_len;
 530
 531        if (p > total_size)
 532                return -EFBIG;
 533
 534        if (count > total_size) {
 535                err = -EFBIG;
 536                count = total_size;
 537        }
 538
 539        if (count + p > total_size) {
 540                if (!err)
 541                        err = -ENOSPC;
 542
 543                count = total_size - p;
 544        }
 545
 546        dst = (void __force *)(info->screen_base + p);
 547
 548        if (copy_from_user(dst, buf, count))
 549                err = -EFAULT;
 550
 551        if  (!err)
 552                *ppos += count;
 553
 554        metronomefb_dpy_update(par);
 555
 556        return (err) ? err : count;
 557}
 558
 559static struct fb_ops metronomefb_ops = {
 560        .owner          = THIS_MODULE,
 561        .fb_write       = metronomefb_write,
 562        .fb_fillrect    = metronomefb_fillrect,
 563        .fb_copyarea    = metronomefb_copyarea,
 564        .fb_imageblit   = metronomefb_imageblit,
 565};
 566
 567static struct fb_deferred_io metronomefb_defio = {
 568        .delay          = HZ,
 569        .deferred_io    = metronomefb_dpy_deferred_io,
 570};
 571
 572static int metronomefb_probe(struct platform_device *dev)
 573{
 574        struct fb_info *info;
 575        struct metronome_board *board;
 576        int retval = -ENOMEM;
 577        int videomemorysize;
 578        unsigned char *videomemory;
 579        struct metronomefb_par *par;
 580        const struct firmware *fw_entry;
 581        int i;
 582        int panel_type;
 583        int fw, fh;
 584        int epd_dt_index;
 585
 586        /* pick up board specific routines */
 587        board = dev->dev.platform_data;
 588        if (!board)
 589                return -EINVAL;
 590
 591        /* try to count device specific driver, if can't, platform recalls */
 592        if (!try_module_get(board->owner))
 593                return -ENODEV;
 594
 595        info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
 596        if (!info)
 597                goto err;
 598
 599        /* we have two blocks of memory.
 600        info->screen_base which is vm, and is the fb used by apps.
 601        par->metromem which is physically contiguous memory and
 602        contains the display controller commands, waveform,
 603        processed image data and padding. this is the data pulled
 604        by the device's LCD controller and pushed to Metronome.
 605        the metromem memory is allocated by the board driver and
 606        is provided to us */
 607
 608        panel_type = board->get_panel_type();
 609        switch (panel_type) {
 610        case 6:
 611                epd_dt_index = 0;
 612                break;
 613        case 8:
 614                epd_dt_index = 1;
 615                break;
 616        case 97:
 617                epd_dt_index = 2;
 618                break;
 619        default:
 620                dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
 621                epd_dt_index = 0;
 622                break;
 623        }
 624
 625        fw = epd_frame_table[epd_dt_index].fw;
 626        fh = epd_frame_table[epd_dt_index].fh;
 627
 628        /* we need to add a spare page because our csum caching scheme walks
 629         * to the end of the page */
 630        videomemorysize = PAGE_SIZE + (fw * fh);
 631        videomemory = vzalloc(videomemorysize);
 632        if (!videomemory)
 633                goto err_fb_rel;
 634
 635        info->screen_base = (char __force __iomem *)videomemory;
 636        info->fbops = &metronomefb_ops;
 637
 638        metronomefb_fix.line_length = fw;
 639        metronomefb_var.xres = fw;
 640        metronomefb_var.yres = fh;
 641        metronomefb_var.xres_virtual = fw;
 642        metronomefb_var.yres_virtual = fh;
 643        info->var = metronomefb_var;
 644        info->fix = metronomefb_fix;
 645        info->fix.smem_len = videomemorysize;
 646        par = info->par;
 647        par->info = info;
 648        par->board = board;
 649        par->dt = epd_dt_index;
 650        init_waitqueue_head(&par->waitq);
 651
 652        /* this table caches per page csum values. */
 653        par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
 654        if (!par->csum_table)
 655                goto err_vfree;
 656
 657        /* the physical framebuffer that we use is setup by
 658         * the platform device driver. It will provide us
 659         * with cmd, wfm and image memory in a contiguous area. */
 660        retval = board->setup_fb(par);
 661        if (retval) {
 662                dev_err(&dev->dev, "Failed to setup fb\n");
 663                goto err_csum_table;
 664        }
 665
 666        /* after this point we should have a framebuffer */
 667        if ((!par->metromem_wfm) ||  (!par->metromem_img) ||
 668                (!par->metromem_dma)) {
 669                dev_err(&dev->dev, "fb access failure\n");
 670                retval = -EINVAL;
 671                goto err_csum_table;
 672        }
 673
 674        info->fix.smem_start = par->metromem_dma;
 675
 676        /* load the waveform in. assume mode 3, temp 31 for now
 677                a) request the waveform file from userspace
 678                b) process waveform and decode into metromem */
 679        retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
 680        if (retval < 0) {
 681                dev_err(&dev->dev, "Failed to get waveform\n");
 682                goto err_csum_table;
 683        }
 684
 685        retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
 686                                par);
 687        release_firmware(fw_entry);
 688        if (retval < 0) {
 689                dev_err(&dev->dev, "Failed processing waveform\n");
 690                goto err_csum_table;
 691        }
 692
 693        if (board->setup_irq(info))
 694                goto err_csum_table;
 695
 696        retval = metronome_init_regs(par);
 697        if (retval < 0)
 698                goto err_free_irq;
 699
 700        info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
 701
 702        info->fbdefio = &metronomefb_defio;
 703        fb_deferred_io_init(info);
 704
 705        retval = fb_alloc_cmap(&info->cmap, 8, 0);
 706        if (retval < 0) {
 707                dev_err(&dev->dev, "Failed to allocate colormap\n");
 708                goto err_free_irq;
 709        }
 710
 711        /* set cmap */
 712        for (i = 0; i < 8; i++)
 713                info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
 714        memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
 715        memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
 716
 717        retval = register_framebuffer(info);
 718        if (retval < 0)
 719                goto err_cmap;
 720
 721        platform_set_drvdata(dev, info);
 722
 723        dev_dbg(&dev->dev,
 724                "fb%d: Metronome frame buffer device, using %dK of video"
 725                " memory\n", info->node, videomemorysize >> 10);
 726
 727        return 0;
 728
 729err_cmap:
 730        fb_dealloc_cmap(&info->cmap);
 731err_free_irq:
 732        board->cleanup(par);
 733err_csum_table:
 734        vfree(par->csum_table);
 735err_vfree:
 736        vfree(videomemory);
 737err_fb_rel:
 738        framebuffer_release(info);
 739err:
 740        module_put(board->owner);
 741        return retval;
 742}
 743
 744static int metronomefb_remove(struct platform_device *dev)
 745{
 746        struct fb_info *info = platform_get_drvdata(dev);
 747
 748        if (info) {
 749                struct metronomefb_par *par = info->par;
 750
 751                unregister_framebuffer(info);
 752                fb_deferred_io_cleanup(info);
 753                fb_dealloc_cmap(&info->cmap);
 754                par->board->cleanup(par);
 755                vfree(par->csum_table);
 756                vfree((void __force *)info->screen_base);
 757                module_put(par->board->owner);
 758                dev_dbg(&dev->dev, "calling release\n");
 759                framebuffer_release(info);
 760        }
 761        return 0;
 762}
 763
 764static struct platform_driver metronomefb_driver = {
 765        .probe  = metronomefb_probe,
 766        .remove = metronomefb_remove,
 767        .driver = {
 768                .owner  = THIS_MODULE,
 769                .name   = "metronomefb",
 770        },
 771};
 772
 773static int __init metronomefb_init(void)
 774{
 775        return platform_driver_register(&metronomefb_driver);
 776}
 777
 778static void __exit metronomefb_exit(void)
 779{
 780        platform_driver_unregister(&metronomefb_driver);
 781}
 782
 783module_param(user_wfm_size, uint, 0);
 784MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
 785
 786module_init(metronomefb_init);
 787module_exit(metronomefb_exit);
 788
 789MODULE_DESCRIPTION("fbdev driver for Metronome controller");
 790MODULE_AUTHOR("Jaya Kumar");
 791MODULE_LICENSE("GPL");
 792