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