Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * linux/drivers/video/metronomefb.c -- FB driver for Metronome controller
0003  *
0004  * Copyright (C) 2008, Jaya Kumar
0005  *
0006  * This file is subject to the terms and conditions of the GNU General Public
0007  * License. See the file COPYING in the main directory of this archive for
0008  * more details.
0009  *
0010  * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
0011  *
0012  * This work was made possible by help and equipment support from E-Ink
0013  * Corporation. https://www.eink.com/
0014  *
0015  * This driver is written to be used with the Metronome display controller.
0016  * It is intended to be architecture independent. A board specific driver
0017  * must be used to perform all the physical IO interactions. An example
0018  * is provided as am200epd.c
0019  *
0020  */
0021 #include <linux/module.h>
0022 #include <linux/kernel.h>
0023 #include <linux/errno.h>
0024 #include <linux/string.h>
0025 #include <linux/mm.h>
0026 #include <linux/vmalloc.h>
0027 #include <linux/delay.h>
0028 #include <linux/interrupt.h>
0029 #include <linux/fb.h>
0030 #include <linux/init.h>
0031 #include <linux/platform_device.h>
0032 #include <linux/list.h>
0033 #include <linux/firmware.h>
0034 #include <linux/dma-mapping.h>
0035 #include <linux/uaccess.h>
0036 #include <linux/irq.h>
0037 
0038 #include <video/metronomefb.h>
0039 
0040 #include <asm/unaligned.h>
0041 
0042 /* Display specific information */
0043 #define DPY_W 832
0044 #define DPY_H 622
0045 
0046 static int user_wfm_size;
0047 
0048 /* frame differs from image. frame includes non-visible pixels */
0049 struct epd_frame {
0050     int fw; /* frame width */
0051     int fh; /* frame height */
0052     u16 config[4];
0053     int wfm_size;
0054 };
0055 
0056 static struct epd_frame epd_frame_table[] = {
0057     {
0058         .fw = 832,
0059         .fh = 622,
0060         .config = {
0061             15 /* sdlew */
0062             | 2 << 8 /* sdosz */
0063             | 0 << 11 /* sdor */
0064             | 0 << 12 /* sdces */
0065             | 0 << 15, /* sdcer */
0066             42 /* gdspl */
0067             | 1 << 8 /* gdr1 */
0068             | 1 << 9 /* sdshr */
0069             | 0 << 15, /* gdspp */
0070             18 /* gdspw */
0071             | 0 << 15, /* dispc */
0072             599 /* vdlc */
0073             | 0 << 11 /* dsi */
0074             | 0 << 12, /* dsic */
0075         },
0076         .wfm_size = 47001,
0077     },
0078     {
0079         .fw = 1088,
0080         .fh = 791,
0081         .config = {
0082             0x0104,
0083             0x031f,
0084             0x0088,
0085             0x02ff,
0086         },
0087         .wfm_size = 46770,
0088     },
0089     {
0090         .fw = 1200,
0091         .fh = 842,
0092         .config = {
0093             0x0101,
0094             0x030e,
0095             0x0012,
0096             0x0280,
0097         },
0098         .wfm_size = 46770,
0099     },
0100 };
0101 
0102 static struct fb_fix_screeninfo metronomefb_fix = {
0103     .id =       "metronomefb",
0104     .type =     FB_TYPE_PACKED_PIXELS,
0105     .visual =   FB_VISUAL_STATIC_PSEUDOCOLOR,
0106     .xpanstep = 0,
0107     .ypanstep = 0,
0108     .ywrapstep =    0,
0109     .line_length =  DPY_W,
0110     .accel =    FB_ACCEL_NONE,
0111 };
0112 
0113 static struct fb_var_screeninfo metronomefb_var = {
0114     .xres       = DPY_W,
0115     .yres       = DPY_H,
0116     .xres_virtual   = DPY_W,
0117     .yres_virtual   = DPY_H,
0118     .bits_per_pixel = 8,
0119     .grayscale  = 1,
0120     .nonstd     = 1,
0121     .red =      { 4, 3, 0 },
0122     .green =    { 0, 0, 0 },
0123     .blue =     { 0, 0, 0 },
0124     .transp =   { 0, 0, 0 },
0125 };
0126 
0127 /* the waveform structure that is coming from userspace firmware */
0128 struct waveform_hdr {
0129     u8 stuff[32];
0130 
0131     u8 wmta[3];
0132     u8 fvsn;
0133 
0134     u8 luts;
0135     u8 mc;
0136     u8 trc;
0137     u8 stuff3;
0138 
0139     u8 endb;
0140     u8 swtb;
0141     u8 stuff2a[2];
0142 
0143     u8 stuff2b[3];
0144     u8 wfm_cs;
0145 } __attribute__ ((packed));
0146 
0147 /* main metronomefb functions */
0148 static u8 calc_cksum(int start, int end, u8 *mem)
0149 {
0150     u8 tmp = 0;
0151     int i;
0152 
0153     for (i = start; i < end; i++)
0154         tmp += mem[i];
0155 
0156     return tmp;
0157 }
0158 
0159 static u16 calc_img_cksum(u16 *start, int length)
0160 {
0161     u16 tmp = 0;
0162 
0163     while (length--)
0164         tmp += *start++;
0165 
0166     return tmp;
0167 }
0168 
0169 /* here we decode the incoming waveform file and populate metromem */
0170 static int load_waveform(u8 *mem, size_t size, int m, int t,
0171              struct metronomefb_par *par)
0172 {
0173     int tta;
0174     int wmta;
0175     int trn = 0;
0176     int i;
0177     unsigned char v;
0178     u8 cksum;
0179     int cksum_idx;
0180     int wfm_idx, owfm_idx;
0181     int mem_idx = 0;
0182     struct waveform_hdr *wfm_hdr;
0183     u8 *metromem = par->metromem_wfm;
0184     struct device *dev = par->info->dev;
0185 
0186     if (user_wfm_size)
0187         epd_frame_table[par->dt].wfm_size = user_wfm_size;
0188 
0189     if (size != epd_frame_table[par->dt].wfm_size) {
0190         dev_err(dev, "Error: unexpected size %zd != %d\n", size,
0191                     epd_frame_table[par->dt].wfm_size);
0192         return -EINVAL;
0193     }
0194 
0195     wfm_hdr = (struct waveform_hdr *) mem;
0196 
0197     if (wfm_hdr->fvsn != 1) {
0198         dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn);
0199         return -EINVAL;
0200     }
0201     if (wfm_hdr->luts != 0) {
0202         dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts);
0203         return -EINVAL;
0204     }
0205     cksum = calc_cksum(32, 47, mem);
0206     if (cksum != wfm_hdr->wfm_cs) {
0207         dev_err(dev, "Error: bad cksum %x != %x\n", cksum,
0208                     wfm_hdr->wfm_cs);
0209         return -EINVAL;
0210     }
0211     wfm_hdr->mc += 1;
0212     wfm_hdr->trc += 1;
0213     for (i = 0; i < 5; i++) {
0214         if (*(wfm_hdr->stuff2a + i) != 0) {
0215             dev_err(dev, "Error: unexpected value in padding\n");
0216             return -EINVAL;
0217         }
0218     }
0219 
0220     /* calculating trn. trn is something used to index into
0221     the waveform. presumably selecting the right one for the
0222     desired temperature. it works out the offset of the first
0223     v that exceeds the specified temperature */
0224     if ((sizeof(*wfm_hdr) + wfm_hdr->trc) > size)
0225         return -EINVAL;
0226 
0227     for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
0228         if (mem[i] > t) {
0229             trn = i - sizeof(*wfm_hdr) - 1;
0230             break;
0231         }
0232     }
0233 
0234     /* check temperature range table checksum */
0235     cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
0236     if (cksum_idx >= size)
0237         return -EINVAL;
0238     cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
0239     if (cksum != mem[cksum_idx]) {
0240         dev_err(dev, "Error: bad temperature range table cksum"
0241                 " %x != %x\n", cksum, mem[cksum_idx]);
0242         return -EINVAL;
0243     }
0244 
0245     /* check waveform mode table address checksum */
0246     wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF;
0247     cksum_idx = wmta + m*4 + 3;
0248     if (cksum_idx >= size)
0249         return -EINVAL;
0250     cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
0251     if (cksum != mem[cksum_idx]) {
0252         dev_err(dev, "Error: bad mode table address cksum"
0253                 " %x != %x\n", cksum, mem[cksum_idx]);
0254         return -EINVAL;
0255     }
0256 
0257     /* check waveform temperature table address checksum */
0258     tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF;
0259     cksum_idx = tta + trn*4 + 3;
0260     if (cksum_idx >= size)
0261         return -EINVAL;
0262     cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
0263     if (cksum != mem[cksum_idx]) {
0264         dev_err(dev, "Error: bad temperature table address cksum"
0265             " %x != %x\n", cksum, mem[cksum_idx]);
0266         return -EINVAL;
0267     }
0268 
0269     /* here we do the real work of putting the waveform into the
0270     metromem buffer. this does runlength decoding of the waveform */
0271     wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF;
0272     owfm_idx = wfm_idx;
0273     if (wfm_idx >= size)
0274         return -EINVAL;
0275     while (wfm_idx < size) {
0276         unsigned char rl;
0277         v = mem[wfm_idx++];
0278         if (v == wfm_hdr->swtb) {
0279             while (((v = mem[wfm_idx++]) != wfm_hdr->swtb) &&
0280                 wfm_idx < size)
0281                 metromem[mem_idx++] = v;
0282 
0283             continue;
0284         }
0285 
0286         if (v == wfm_hdr->endb)
0287             break;
0288 
0289         rl = mem[wfm_idx++];
0290         for (i = 0; i <= rl; i++)
0291             metromem[mem_idx++] = v;
0292     }
0293 
0294     cksum_idx = wfm_idx;
0295     if (cksum_idx >= size)
0296         return -EINVAL;
0297     cksum = calc_cksum(owfm_idx, cksum_idx, mem);
0298     if (cksum != mem[cksum_idx]) {
0299         dev_err(dev, "Error: bad waveform data cksum"
0300                 " %x != %x\n", cksum, mem[cksum_idx]);
0301         return -EINVAL;
0302     }
0303     par->frame_count = (mem_idx/64);
0304 
0305     return 0;
0306 }
0307 
0308 static int metronome_display_cmd(struct metronomefb_par *par)
0309 {
0310     int i;
0311     u16 cs;
0312     u16 opcode;
0313     static u8 borderval;
0314 
0315     /* setup display command
0316     we can't immediately set the opcode since the controller
0317     will try parse the command before we've set it all up
0318     so we just set cs here and set the opcode at the end */
0319 
0320     if (par->metromem_cmd->opcode == 0xCC40)
0321         opcode = cs = 0xCC41;
0322     else
0323         opcode = cs = 0xCC40;
0324 
0325     /* set the args ( 2 bytes ) for display */
0326     i = 0;
0327     par->metromem_cmd->args[i] =    1 << 3 /* border update */
0328                     | ((borderval++ % 4) & 0x0F) << 4
0329                     | (par->frame_count - 1) << 8;
0330     cs += par->metromem_cmd->args[i++];
0331 
0332     /* the rest are 0 */
0333     memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
0334 
0335     par->metromem_cmd->csum = cs;
0336     par->metromem_cmd->opcode = opcode; /* display cmd */
0337 
0338     return par->board->met_wait_event_intr(par);
0339 }
0340 
0341 static int metronome_powerup_cmd(struct metronomefb_par *par)
0342 {
0343     int i;
0344     u16 cs;
0345 
0346     /* setup power up command */
0347     par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
0348     cs = par->metromem_cmd->opcode;
0349 
0350     /* set pwr1,2,3 to 1024 */
0351     for (i = 0; i < 3; i++) {
0352         par->metromem_cmd->args[i] = 1024;
0353         cs += par->metromem_cmd->args[i];
0354     }
0355 
0356     /* the rest are 0 */
0357     memset(&par->metromem_cmd->args[i], 0,
0358            (ARRAY_SIZE(par->metromem_cmd->args) - i) * 2);
0359 
0360     par->metromem_cmd->csum = cs;
0361 
0362     msleep(1);
0363     par->board->set_rst(par, 1);
0364 
0365     msleep(1);
0366     par->board->set_stdby(par, 1);
0367 
0368     return par->board->met_wait_event(par);
0369 }
0370 
0371 static int metronome_config_cmd(struct metronomefb_par *par)
0372 {
0373     /* setup config command
0374     we can't immediately set the opcode since the controller
0375     will try parse the command before we've set it all up */
0376 
0377     memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
0378         sizeof(epd_frame_table[par->dt].config));
0379     /* the rest are 0 */
0380     memset(&par->metromem_cmd->args[4], 0,
0381            (ARRAY_SIZE(par->metromem_cmd->args) - 4) * 2);
0382 
0383     par->metromem_cmd->csum = 0xCC10;
0384     par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
0385     par->metromem_cmd->opcode = 0xCC10; /* config cmd */
0386 
0387     return par->board->met_wait_event(par);
0388 }
0389 
0390 static int metronome_init_cmd(struct metronomefb_par *par)
0391 {
0392     int i;
0393     u16 cs;
0394 
0395     /* setup init command
0396     we can't immediately set the opcode since the controller
0397     will try parse the command before we've set it all up
0398     so we just set cs here and set the opcode at the end */
0399 
0400     cs = 0xCC20;
0401 
0402     /* set the args ( 2 bytes ) for init */
0403     i = 0;
0404     par->metromem_cmd->args[i] = 0;
0405     cs += par->metromem_cmd->args[i++];
0406 
0407     /* the rest are 0 */
0408     memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
0409 
0410     par->metromem_cmd->csum = cs;
0411     par->metromem_cmd->opcode = 0xCC20; /* init cmd */
0412 
0413     return par->board->met_wait_event(par);
0414 }
0415 
0416 static int metronome_init_regs(struct metronomefb_par *par)
0417 {
0418     int res;
0419 
0420     res = par->board->setup_io(par);
0421     if (res)
0422         return res;
0423 
0424     res = metronome_powerup_cmd(par);
0425     if (res)
0426         return res;
0427 
0428     res = metronome_config_cmd(par);
0429     if (res)
0430         return res;
0431 
0432     res = metronome_init_cmd(par);
0433 
0434     return res;
0435 }
0436 
0437 static void metronomefb_dpy_update(struct metronomefb_par *par)
0438 {
0439     int fbsize;
0440     u16 cksum;
0441     unsigned char *buf = (unsigned char __force *)par->info->screen_base;
0442 
0443     fbsize = par->info->fix.smem_len;
0444     /* copy from vm to metromem */
0445     memcpy(par->metromem_img, buf, fbsize);
0446 
0447     cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
0448     *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
0449     metronome_display_cmd(par);
0450 }
0451 
0452 static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
0453 {
0454     int i;
0455     u16 csum = 0;
0456     u16 *buf = (u16 __force *)(par->info->screen_base + index);
0457     u16 *img = (u16 *)(par->metromem_img + index);
0458 
0459     /* swizzle from vm to metromem and recalc cksum at the same time*/
0460     for (i = 0; i < PAGE_SIZE/2; i++) {
0461         *(img + i) = (buf[i] << 5) & 0xE0E0;
0462         csum += *(img + i);
0463     }
0464     return csum;
0465 }
0466 
0467 /* this is called back from the deferred io workqueue */
0468 static void metronomefb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
0469 {
0470     u16 cksum;
0471     struct fb_deferred_io_pageref *pageref;
0472     struct metronomefb_par *par = info->par;
0473 
0474     /* walk the written page list and swizzle the data */
0475     list_for_each_entry(pageref, pagereflist, list) {
0476         unsigned long pgoffset = pageref->offset >> PAGE_SHIFT;
0477         cksum = metronomefb_dpy_update_page(par, pageref->offset);
0478         par->metromem_img_csum -= par->csum_table[pgoffset];
0479         par->csum_table[pgoffset] = cksum;
0480         par->metromem_img_csum += cksum;
0481     }
0482 
0483     metronome_display_cmd(par);
0484 }
0485 
0486 static void metronomefb_fillrect(struct fb_info *info,
0487                    const struct fb_fillrect *rect)
0488 {
0489     struct metronomefb_par *par = info->par;
0490 
0491     sys_fillrect(info, rect);
0492     metronomefb_dpy_update(par);
0493 }
0494 
0495 static void metronomefb_copyarea(struct fb_info *info,
0496                    const struct fb_copyarea *area)
0497 {
0498     struct metronomefb_par *par = info->par;
0499 
0500     sys_copyarea(info, area);
0501     metronomefb_dpy_update(par);
0502 }
0503 
0504 static void metronomefb_imageblit(struct fb_info *info,
0505                 const struct fb_image *image)
0506 {
0507     struct metronomefb_par *par = info->par;
0508 
0509     sys_imageblit(info, image);
0510     metronomefb_dpy_update(par);
0511 }
0512 
0513 /*
0514  * this is the slow path from userspace. they can seek and write to
0515  * the fb. it is based on fb_sys_write
0516  */
0517 static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
0518                 size_t count, loff_t *ppos)
0519 {
0520     struct metronomefb_par *par = info->par;
0521     unsigned long p = *ppos;
0522     void *dst;
0523     int err = 0;
0524     unsigned long total_size;
0525 
0526     if (info->state != FBINFO_STATE_RUNNING)
0527         return -EPERM;
0528 
0529     total_size = info->fix.smem_len;
0530 
0531     if (p > total_size)
0532         return -EFBIG;
0533 
0534     if (count > total_size) {
0535         err = -EFBIG;
0536         count = total_size;
0537     }
0538 
0539     if (count + p > total_size) {
0540         if (!err)
0541             err = -ENOSPC;
0542 
0543         count = total_size - p;
0544     }
0545 
0546     dst = (void __force *)(info->screen_base + p);
0547 
0548     if (copy_from_user(dst, buf, count))
0549         err = -EFAULT;
0550 
0551     if  (!err)
0552         *ppos += count;
0553 
0554     metronomefb_dpy_update(par);
0555 
0556     return (err) ? err : count;
0557 }
0558 
0559 static const struct fb_ops metronomefb_ops = {
0560     .owner      = THIS_MODULE,
0561     .fb_write   = metronomefb_write,
0562     .fb_fillrect    = metronomefb_fillrect,
0563     .fb_copyarea    = metronomefb_copyarea,
0564     .fb_imageblit   = metronomefb_imageblit,
0565     .fb_mmap    = fb_deferred_io_mmap,
0566 };
0567 
0568 static struct fb_deferred_io metronomefb_defio = {
0569     .delay          = HZ,
0570     .sort_pagereflist   = true,
0571     .deferred_io        = metronomefb_dpy_deferred_io,
0572 };
0573 
0574 static int metronomefb_probe(struct platform_device *dev)
0575 {
0576     struct fb_info *info;
0577     struct metronome_board *board;
0578     int retval = -ENOMEM;
0579     int videomemorysize;
0580     unsigned char *videomemory;
0581     struct metronomefb_par *par;
0582     const struct firmware *fw_entry;
0583     int i;
0584     int panel_type;
0585     int fw, fh;
0586     int epd_dt_index;
0587 
0588     /* pick up board specific routines */
0589     board = dev->dev.platform_data;
0590     if (!board)
0591         return -EINVAL;
0592 
0593     /* try to count device specific driver, if can't, platform recalls */
0594     if (!try_module_get(board->owner))
0595         return -ENODEV;
0596 
0597     info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
0598     if (!info)
0599         goto err;
0600 
0601     /* we have two blocks of memory.
0602     info->screen_base which is vm, and is the fb used by apps.
0603     par->metromem which is physically contiguous memory and
0604     contains the display controller commands, waveform,
0605     processed image data and padding. this is the data pulled
0606     by the device's LCD controller and pushed to Metronome.
0607     the metromem memory is allocated by the board driver and
0608     is provided to us */
0609 
0610     panel_type = board->get_panel_type();
0611     switch (panel_type) {
0612     case 6:
0613         epd_dt_index = 0;
0614         break;
0615     case 8:
0616         epd_dt_index = 1;
0617         break;
0618     case 97:
0619         epd_dt_index = 2;
0620         break;
0621     default:
0622         dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
0623         epd_dt_index = 0;
0624         break;
0625     }
0626 
0627     fw = epd_frame_table[epd_dt_index].fw;
0628     fh = epd_frame_table[epd_dt_index].fh;
0629 
0630     /* we need to add a spare page because our csum caching scheme walks
0631      * to the end of the page */
0632     videomemorysize = PAGE_SIZE + (fw * fh);
0633     videomemory = vzalloc(videomemorysize);
0634     if (!videomemory)
0635         goto err_fb_rel;
0636 
0637     info->screen_base = (char __force __iomem *)videomemory;
0638     info->fbops = &metronomefb_ops;
0639 
0640     metronomefb_fix.line_length = fw;
0641     metronomefb_var.xres = fw;
0642     metronomefb_var.yres = fh;
0643     metronomefb_var.xres_virtual = fw;
0644     metronomefb_var.yres_virtual = fh;
0645     info->var = metronomefb_var;
0646     info->fix = metronomefb_fix;
0647     info->fix.smem_len = videomemorysize;
0648     par = info->par;
0649     par->info = info;
0650     par->board = board;
0651     par->dt = epd_dt_index;
0652     init_waitqueue_head(&par->waitq);
0653 
0654     /* this table caches per page csum values. */
0655     par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
0656     if (!par->csum_table)
0657         goto err_vfree;
0658 
0659     /* the physical framebuffer that we use is setup by
0660      * the platform device driver. It will provide us
0661      * with cmd, wfm and image memory in a contiguous area. */
0662     retval = board->setup_fb(par);
0663     if (retval) {
0664         dev_err(&dev->dev, "Failed to setup fb\n");
0665         goto err_csum_table;
0666     }
0667 
0668     /* after this point we should have a framebuffer */
0669     if ((!par->metromem_wfm) ||  (!par->metromem_img) ||
0670         (!par->metromem_dma)) {
0671         dev_err(&dev->dev, "fb access failure\n");
0672         retval = -EINVAL;
0673         goto err_csum_table;
0674     }
0675 
0676     info->fix.smem_start = par->metromem_dma;
0677 
0678     /* load the waveform in. assume mode 3, temp 31 for now
0679         a) request the waveform file from userspace
0680         b) process waveform and decode into metromem */
0681     retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
0682     if (retval < 0) {
0683         dev_err(&dev->dev, "Failed to get waveform\n");
0684         goto err_csum_table;
0685     }
0686 
0687     retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
0688                 par);
0689     release_firmware(fw_entry);
0690     if (retval < 0) {
0691         dev_err(&dev->dev, "Failed processing waveform\n");
0692         goto err_csum_table;
0693     }
0694 
0695     retval = board->setup_irq(info);
0696     if (retval)
0697         goto err_csum_table;
0698 
0699     retval = metronome_init_regs(par);
0700     if (retval < 0)
0701         goto err_free_irq;
0702 
0703     info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
0704 
0705     info->fbdefio = &metronomefb_defio;
0706     fb_deferred_io_init(info);
0707 
0708     retval = fb_alloc_cmap(&info->cmap, 8, 0);
0709     if (retval < 0) {
0710         dev_err(&dev->dev, "Failed to allocate colormap\n");
0711         goto err_free_irq;
0712     }
0713 
0714     /* set cmap */
0715     for (i = 0; i < 8; i++)
0716         info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
0717     memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
0718     memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
0719 
0720     retval = register_framebuffer(info);
0721     if (retval < 0)
0722         goto err_cmap;
0723 
0724     platform_set_drvdata(dev, info);
0725 
0726     dev_dbg(&dev->dev,
0727         "fb%d: Metronome frame buffer device, using %dK of video"
0728         " memory\n", info->node, videomemorysize >> 10);
0729 
0730     return 0;
0731 
0732 err_cmap:
0733     fb_dealloc_cmap(&info->cmap);
0734 err_free_irq:
0735     board->cleanup(par);
0736 err_csum_table:
0737     vfree(par->csum_table);
0738 err_vfree:
0739     vfree(videomemory);
0740 err_fb_rel:
0741     framebuffer_release(info);
0742 err:
0743     module_put(board->owner);
0744     return retval;
0745 }
0746 
0747 static int metronomefb_remove(struct platform_device *dev)
0748 {
0749     struct fb_info *info = platform_get_drvdata(dev);
0750 
0751     if (info) {
0752         struct metronomefb_par *par = info->par;
0753 
0754         unregister_framebuffer(info);
0755         fb_deferred_io_cleanup(info);
0756         fb_dealloc_cmap(&info->cmap);
0757         par->board->cleanup(par);
0758         vfree(par->csum_table);
0759         vfree((void __force *)info->screen_base);
0760         module_put(par->board->owner);
0761         dev_dbg(&dev->dev, "calling release\n");
0762         framebuffer_release(info);
0763     }
0764     return 0;
0765 }
0766 
0767 static struct platform_driver metronomefb_driver = {
0768     .probe  = metronomefb_probe,
0769     .remove = metronomefb_remove,
0770     .driver = {
0771         .name   = "metronomefb",
0772     },
0773 };
0774 module_platform_driver(metronomefb_driver);
0775 
0776 module_param(user_wfm_size, uint, 0);
0777 MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
0778 
0779 MODULE_DESCRIPTION("fbdev driver for Metronome controller");
0780 MODULE_AUTHOR("Jaya Kumar");
0781 MODULE_LICENSE("GPL");