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