linux/drivers/video/fbdev/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. https://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(&par->metromem_cmd->args[i], 0,
 358               (ARRAY_SIZE(par->metromem_cmd->args) - i) * 2);
 359
 360        par->metromem_cmd->csum = cs;
 361
 362        msleep(1);
 363        par->board->set_rst(par, 1);
 364
 365        msleep(1);
 366        par->board->set_stdby(par, 1);
 367
 368        return par->board->met_wait_event(par);
 369}
 370
 371static int metronome_config_cmd(struct metronomefb_par *par)
 372{
 373        /* setup config command
 374        we can't immediately set the opcode since the controller
 375        will try parse the command before we've set it all up */
 376
 377        memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
 378                sizeof(epd_frame_table[par->dt].config));
 379        /* the rest are 0 */
 380        memset(&par->metromem_cmd->args[4], 0,
 381               (ARRAY_SIZE(par->metromem_cmd->args) - 4) * 2);
 382
 383        par->metromem_cmd->csum = 0xCC10;
 384        par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
 385        par->metromem_cmd->opcode = 0xCC10; /* config cmd */
 386
 387        return par->board->met_wait_event(par);
 388}
 389
 390static int metronome_init_cmd(struct metronomefb_par *par)
 391{
 392        int i;
 393        u16 cs;
 394
 395        /* setup init command
 396        we can't immediately set the opcode since the controller
 397        will try parse the command before we've set it all up
 398        so we just set cs here and set the opcode at the end */
 399
 400        cs = 0xCC20;
 401
 402        /* set the args ( 2 bytes ) for init */
 403        i = 0;
 404        par->metromem_cmd->args[i] = 0;
 405        cs += par->metromem_cmd->args[i++];
 406
 407        /* the rest are 0 */
 408        memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
 409
 410        par->metromem_cmd->csum = cs;
 411        par->metromem_cmd->opcode = 0xCC20; /* init cmd */
 412
 413        return par->board->met_wait_event(par);
 414}
 415
 416static int metronome_init_regs(struct metronomefb_par *par)
 417{
 418        int res;
 419
 420        res = par->board->setup_io(par);
 421        if (res)
 422                return res;
 423
 424        res = metronome_powerup_cmd(par);
 425        if (res)
 426                return res;
 427
 428        res = metronome_config_cmd(par);
 429        if (res)
 430                return res;
 431
 432        res = metronome_init_cmd(par);
 433
 434        return res;
 435}
 436
 437static void metronomefb_dpy_update(struct metronomefb_par *par)
 438{
 439        int fbsize;
 440        u16 cksum;
 441        unsigned char *buf = (unsigned char __force *)par->info->screen_base;
 442
 443        fbsize = par->info->fix.smem_len;
 444        /* copy from vm to metromem */
 445        memcpy(par->metromem_img, buf, fbsize);
 446
 447        cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
 448        *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
 449        metronome_display_cmd(par);
 450}
 451
 452static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
 453{
 454        int i;
 455        u16 csum = 0;
 456        u16 *buf = (u16 __force *)(par->info->screen_base + index);
 457        u16 *img = (u16 *)(par->metromem_img + index);
 458
 459        /* swizzle from vm to metromem and recalc cksum at the same time*/
 460        for (i = 0; i < PAGE_SIZE/2; i++) {
 461                *(img + i) = (buf[i] << 5) & 0xE0E0;
 462                csum += *(img + i);
 463        }
 464        return csum;
 465}
 466
 467/* this is called back from the deferred io workqueue */
 468static void metronomefb_dpy_deferred_io(struct fb_info *info,
 469                                struct list_head *pagelist)
 470{
 471        u16 cksum;
 472        struct page *cur;
 473        struct fb_deferred_io *fbdefio = info->fbdefio;
 474        struct metronomefb_par *par = info->par;
 475
 476        /* walk the written page list and swizzle the data */
 477        list_for_each_entry(cur, &fbdefio->pagelist, lru) {
 478                cksum = metronomefb_dpy_update_page(par,
 479                                        (cur->index << PAGE_SHIFT));
 480                par->metromem_img_csum -= par->csum_table[cur->index];
 481                par->csum_table[cur->index] = cksum;
 482                par->metromem_img_csum += cksum;
 483        }
 484
 485        metronome_display_cmd(par);
 486}
 487
 488static void metronomefb_fillrect(struct fb_info *info,
 489                                   const struct fb_fillrect *rect)
 490{
 491        struct metronomefb_par *par = info->par;
 492
 493        sys_fillrect(info, rect);
 494        metronomefb_dpy_update(par);
 495}
 496
 497static void metronomefb_copyarea(struct fb_info *info,
 498                                   const struct fb_copyarea *area)
 499{
 500        struct metronomefb_par *par = info->par;
 501
 502        sys_copyarea(info, area);
 503        metronomefb_dpy_update(par);
 504}
 505
 506static void metronomefb_imageblit(struct fb_info *info,
 507                                const struct fb_image *image)
 508{
 509        struct metronomefb_par *par = info->par;
 510
 511        sys_imageblit(info, image);
 512        metronomefb_dpy_update(par);
 513}
 514
 515/*
 516 * this is the slow path from userspace. they can seek and write to
 517 * the fb. it is based on fb_sys_write
 518 */
 519static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
 520                                size_t count, loff_t *ppos)
 521{
 522        struct metronomefb_par *par = info->par;
 523        unsigned long p = *ppos;
 524        void *dst;
 525        int err = 0;
 526        unsigned long total_size;
 527
 528        if (info->state != FBINFO_STATE_RUNNING)
 529                return -EPERM;
 530
 531        total_size = info->fix.smem_len;
 532
 533        if (p > total_size)
 534                return -EFBIG;
 535
 536        if (count > total_size) {
 537                err = -EFBIG;
 538                count = total_size;
 539        }
 540
 541        if (count + p > total_size) {
 542                if (!err)
 543                        err = -ENOSPC;
 544
 545                count = total_size - p;
 546        }
 547
 548        dst = (void __force *)(info->screen_base + p);
 549
 550        if (copy_from_user(dst, buf, count))
 551                err = -EFAULT;
 552
 553        if  (!err)
 554                *ppos += count;
 555
 556        metronomefb_dpy_update(par);
 557
 558        return (err) ? err : count;
 559}
 560
 561static const struct fb_ops metronomefb_ops = {
 562        .owner          = THIS_MODULE,
 563        .fb_write       = metronomefb_write,
 564        .fb_fillrect    = metronomefb_fillrect,
 565        .fb_copyarea    = metronomefb_copyarea,
 566        .fb_imageblit   = metronomefb_imageblit,
 567};
 568
 569static struct fb_deferred_io metronomefb_defio = {
 570        .delay          = HZ,
 571        .deferred_io    = metronomefb_dpy_deferred_io,
 572};
 573
 574static int metronomefb_probe(struct platform_device *dev)
 575{
 576        struct fb_info *info;
 577        struct metronome_board *board;
 578        int retval = -ENOMEM;
 579        int videomemorysize;
 580        unsigned char *videomemory;
 581        struct metronomefb_par *par;
 582        const struct firmware *fw_entry;
 583        int i;
 584        int panel_type;
 585        int fw, fh;
 586        int epd_dt_index;
 587
 588        /* pick up board specific routines */
 589        board = dev->dev.platform_data;
 590        if (!board)
 591                return -EINVAL;
 592
 593        /* try to count device specific driver, if can't, platform recalls */
 594        if (!try_module_get(board->owner))
 595                return -ENODEV;
 596
 597        info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
 598        if (!info)
 599                goto err;
 600
 601        /* we have two blocks of memory.
 602        info->screen_base which is vm, and is the fb used by apps.
 603        par->metromem which is physically contiguous memory and
 604        contains the display controller commands, waveform,
 605        processed image data and padding. this is the data pulled
 606        by the device's LCD controller and pushed to Metronome.
 607        the metromem memory is allocated by the board driver and
 608        is provided to us */
 609
 610        panel_type = board->get_panel_type();
 611        switch (panel_type) {
 612        case 6:
 613                epd_dt_index = 0;
 614                break;
 615        case 8:
 616                epd_dt_index = 1;
 617                break;
 618        case 97:
 619                epd_dt_index = 2;
 620                break;
 621        default:
 622                dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
 623                epd_dt_index = 0;
 624                break;
 625        }
 626
 627        fw = epd_frame_table[epd_dt_index].fw;
 628        fh = epd_frame_table[epd_dt_index].fh;
 629
 630        /* we need to add a spare page because our csum caching scheme walks
 631         * to the end of the page */
 632        videomemorysize = PAGE_SIZE + (fw * fh);
 633        videomemory = vzalloc(videomemorysize);
 634        if (!videomemory)
 635                goto err_fb_rel;
 636
 637        info->screen_base = (char __force __iomem *)videomemory;
 638        info->fbops = &metronomefb_ops;
 639
 640        metronomefb_fix.line_length = fw;
 641        metronomefb_var.xres = fw;
 642        metronomefb_var.yres = fh;
 643        metronomefb_var.xres_virtual = fw;
 644        metronomefb_var.yres_virtual = fh;
 645        info->var = metronomefb_var;
 646        info->fix = metronomefb_fix;
 647        info->fix.smem_len = videomemorysize;
 648        par = info->par;
 649        par->info = info;
 650        par->board = board;
 651        par->dt = epd_dt_index;
 652        init_waitqueue_head(&par->waitq);
 653
 654        /* this table caches per page csum values. */
 655        par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
 656        if (!par->csum_table)
 657                goto err_vfree;
 658
 659        /* the physical framebuffer that we use is setup by
 660         * the platform device driver. It will provide us
 661         * with cmd, wfm and image memory in a contiguous area. */
 662        retval = board->setup_fb(par);
 663        if (retval) {
 664                dev_err(&dev->dev, "Failed to setup fb\n");
 665                goto err_csum_table;
 666        }
 667
 668        /* after this point we should have a framebuffer */
 669        if ((!par->metromem_wfm) ||  (!par->metromem_img) ||
 670                (!par->metromem_dma)) {
 671                dev_err(&dev->dev, "fb access failure\n");
 672                retval = -EINVAL;
 673                goto err_csum_table;
 674        }
 675
 676        info->fix.smem_start = par->metromem_dma;
 677
 678        /* load the waveform in. assume mode 3, temp 31 for now
 679                a) request the waveform file from userspace
 680                b) process waveform and decode into metromem */
 681        retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
 682        if (retval < 0) {
 683                dev_err(&dev->dev, "Failed to get waveform\n");
 684                goto err_csum_table;
 685        }
 686
 687        retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
 688                                par);
 689        release_firmware(fw_entry);
 690        if (retval < 0) {
 691                dev_err(&dev->dev, "Failed processing waveform\n");
 692                goto err_csum_table;
 693        }
 694
 695        retval = board->setup_irq(info);
 696        if (retval)
 697                goto err_csum_table;
 698
 699        retval = metronome_init_regs(par);
 700        if (retval < 0)
 701                goto err_free_irq;
 702
 703        info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
 704
 705        info->fbdefio = &metronomefb_defio;
 706        fb_deferred_io_init(info);
 707
 708        retval = fb_alloc_cmap(&info->cmap, 8, 0);
 709        if (retval < 0) {
 710                dev_err(&dev->dev, "Failed to allocate colormap\n");
 711                goto err_free_irq;
 712        }
 713
 714        /* set cmap */
 715        for (i = 0; i < 8; i++)
 716                info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
 717        memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
 718        memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
 719
 720        retval = register_framebuffer(info);
 721        if (retval < 0)
 722                goto err_cmap;
 723
 724        platform_set_drvdata(dev, info);
 725
 726        dev_dbg(&dev->dev,
 727                "fb%d: Metronome frame buffer device, using %dK of video"
 728                " memory\n", info->node, videomemorysize >> 10);
 729
 730        return 0;
 731
 732err_cmap:
 733        fb_dealloc_cmap(&info->cmap);
 734err_free_irq:
 735        board->cleanup(par);
 736err_csum_table:
 737        vfree(par->csum_table);
 738err_vfree:
 739        vfree(videomemory);
 740err_fb_rel:
 741        framebuffer_release(info);
 742err:
 743        module_put(board->owner);
 744        return retval;
 745}
 746
 747static int metronomefb_remove(struct platform_device *dev)
 748{
 749        struct fb_info *info = platform_get_drvdata(dev);
 750
 751        if (info) {
 752                struct metronomefb_par *par = info->par;
 753
 754                unregister_framebuffer(info);
 755                fb_deferred_io_cleanup(info);
 756                fb_dealloc_cmap(&info->cmap);
 757                par->board->cleanup(par);
 758                vfree(par->csum_table);
 759                vfree((void __force *)info->screen_base);
 760                module_put(par->board->owner);
 761                dev_dbg(&dev->dev, "calling release\n");
 762                framebuffer_release(info);
 763        }
 764        return 0;
 765}
 766
 767static struct platform_driver metronomefb_driver = {
 768        .probe  = metronomefb_probe,
 769        .remove = metronomefb_remove,
 770        .driver = {
 771                .name   = "metronomefb",
 772        },
 773};
 774module_platform_driver(metronomefb_driver);
 775
 776module_param(user_wfm_size, uint, 0);
 777MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
 778
 779MODULE_DESCRIPTION("fbdev driver for Metronome controller");
 780MODULE_AUTHOR("Jaya Kumar");
 781MODULE_LICENSE("GPL");
 782