This source file includes following definitions.
- calc_cksum
- calc_img_cksum
- load_waveform
- metronome_display_cmd
- metronome_powerup_cmd
- metronome_config_cmd
- metronome_init_cmd
- metronome_init_regs
- metronomefb_dpy_update
- metronomefb_dpy_update_page
- metronomefb_dpy_deferred_io
- metronomefb_fillrect
- metronomefb_copyarea
- metronomefb_imageblit
- metronomefb_write
- metronomefb_probe
- metronomefb_remove
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 
  13 
  14 
  15 
  16 
  17 
  18 
  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 
  43 #define DPY_W 832
  44 #define DPY_H 622
  45 
  46 static int user_wfm_size;
  47 
  48 
  49 struct epd_frame {
  50         int fw; 
  51         int fh; 
  52         u16 config[4];
  53         int wfm_size;
  54 };
  55 
  56 static struct epd_frame epd_frame_table[] = {
  57         {
  58                 .fw = 832,
  59                 .fh = 622,
  60                 .config = {
  61                         15 
  62                         | 2 << 8 
  63                         | 0 << 11 
  64                         | 0 << 12 
  65                         | 0 << 15, 
  66                         42 
  67                         | 1 << 8 
  68                         | 1 << 9 
  69                         | 0 << 15, 
  70                         18 
  71                         | 0 << 15, 
  72                         599 
  73                         | 0 << 11 
  74                         | 0 << 12, 
  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 
 102 static 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 
 113 static 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 
 128 struct 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 
 148 static 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 
 159 static 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 
 170 static 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         
 221 
 222 
 223 
 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         
 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         
 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         
 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         
 270 
 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 
 308 static int metronome_display_cmd(struct metronomefb_par *par)
 309 {
 310         int i;
 311         u16 cs;
 312         u16 opcode;
 313         static u8 borderval;
 314 
 315         
 316 
 317 
 318 
 319 
 320         if (par->metromem_cmd->opcode == 0xCC40)
 321                 opcode = cs = 0xCC41;
 322         else
 323                 opcode = cs = 0xCC40;
 324 
 325         
 326         i = 0;
 327         par->metromem_cmd->args[i] =    1 << 3 
 328                                         | ((borderval++ % 4) & 0x0F) << 4
 329                                         | (par->frame_count - 1) << 8;
 330         cs += par->metromem_cmd->args[i++];
 331 
 332         
 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; 
 337 
 338         return par->board->met_wait_event_intr(par);
 339 }
 340 
 341 static int metronome_powerup_cmd(struct metronomefb_par *par)
 342 {
 343         int i;
 344         u16 cs;
 345 
 346         
 347         par->metromem_cmd->opcode = 0x1234; 
 348         cs = par->metromem_cmd->opcode;
 349 
 350         
 351         for (i = 0; i < 3; i++) {
 352                 par->metromem_cmd->args[i] = 1024;
 353                 cs += par->metromem_cmd->args[i];
 354         }
 355 
 356         
 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 
 371 static int metronome_config_cmd(struct metronomefb_par *par)
 372 {
 373         
 374 
 375 
 376 
 377         memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
 378                 sizeof(epd_frame_table[par->dt].config));
 379         
 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; 
 386 
 387         return par->board->met_wait_event(par);
 388 }
 389 
 390 static int metronome_init_cmd(struct metronomefb_par *par)
 391 {
 392         int i;
 393         u16 cs;
 394 
 395         
 396 
 397 
 398 
 399 
 400         cs = 0xCC20;
 401 
 402         
 403         i = 0;
 404         par->metromem_cmd->args[i] = 0;
 405         cs += par->metromem_cmd->args[i++];
 406 
 407         
 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; 
 412 
 413         return par->board->met_wait_event(par);
 414 }
 415 
 416 static 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 
 437 static 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         
 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 
 452 static 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         
 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 
 468 static 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         
 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 
 488 static 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 
 497 static 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 
 506 static 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 
 517 
 518 
 519 static 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 
 561 static 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 
 569 static struct fb_deferred_io metronomefb_defio = {
 570         .delay          = HZ,
 571         .deferred_io    = metronomefb_dpy_deferred_io,
 572 };
 573 
 574 static 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         
 589         board = dev->dev.platform_data;
 590         if (!board)
 591                 return -EINVAL;
 592 
 593         
 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         
 602 
 603 
 604 
 605 
 606 
 607 
 608 
 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         
 631 
 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         
 655         par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
 656         if (!par->csum_table)
 657                 goto err_vfree;
 658 
 659         
 660 
 661 
 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         
 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         
 679 
 680 
 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         
 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 
 732 err_cmap:
 733         fb_dealloc_cmap(&info->cmap);
 734 err_free_irq:
 735         board->cleanup(par);
 736 err_csum_table:
 737         vfree(par->csum_table);
 738 err_vfree:
 739         vfree(videomemory);
 740 err_fb_rel:
 741         framebuffer_release(info);
 742 err:
 743         module_put(board->owner);
 744         return retval;
 745 }
 746 
 747 static 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 
 767 static struct platform_driver metronomefb_driver = {
 768         .probe  = metronomefb_probe,
 769         .remove = metronomefb_remove,
 770         .driver = {
 771                 .name   = "metronomefb",
 772         },
 773 };
 774 module_platform_driver(metronomefb_driver);
 775 
 776 module_param(user_wfm_size, uint, 0);
 777 MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
 778 
 779 MODULE_DESCRIPTION("fbdev driver for Metronome controller");
 780 MODULE_AUTHOR("Jaya Kumar");
 781 MODULE_LICENSE("GPL");