Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
metronomefb.c
Go to the documentation of this file.
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 __devinitdata = {
103  .id = "metronomefb",
104  .type = FB_TYPE_PACKED_PIXELS,
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 __devinitdata = {
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];
133 
138 
142 
145 } __attribute__ ((packed));
147 /* main metronomefb functions */
148 static u8 calc_cksum(int start, int end, u8 *mem)
149 {
150  u8 tmp = 0;
151  int i;
153  for (i = start; i < end; i++)
154  tmp += mem[i];
155 
156  return tmp;
157 }
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 __devinit 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 __devinit 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 __devinit 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 __devinit 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 __devinit 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 __devinit 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  if (board->setup_irq(info))
694  goto err_csum_table;
695 
696  retval = metronome_init_regs(par);
697  if (retval < 0)
698  goto err_free_irq;
699 
701 
702  info->fbdefio = &metronomefb_defio;
703  fb_deferred_io_init(info);
704 
705  retval = fb_alloc_cmap(&info->cmap, 8, 0);
706  if (retval < 0) {
707  dev_err(&dev->dev, "Failed to allocate colormap\n");
708  goto err_free_irq;
709  }
710 
711  /* set cmap */
712  for (i = 0; i < 8; i++)
713  info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/16;
714  memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*8);
715  memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*8);
716 
717  retval = register_framebuffer(info);
718  if (retval < 0)
719  goto err_cmap;
720 
721  platform_set_drvdata(dev, info);
722 
723  dev_dbg(&dev->dev,
724  "fb%d: Metronome frame buffer device, using %dK of video"
725  " memory\n", info->node, videomemorysize >> 10);
726 
727  return 0;
728 
729 err_cmap:
730  fb_dealloc_cmap(&info->cmap);
731 err_free_irq:
732  board->cleanup(par);
733 err_csum_table:
734  vfree(par->csum_table);
735 err_vfree:
736  vfree(videomemory);
737 err_fb_rel:
738  framebuffer_release(info);
739 err:
740  module_put(board->owner);
741  return retval;
742 }
743 
744 static int __devexit metronomefb_remove(struct platform_device *dev)
745 {
746  struct fb_info *info = platform_get_drvdata(dev);
747 
748  if (info) {
749  struct metronomefb_par *par = info->par;
750 
753  fb_dealloc_cmap(&info->cmap);
754  par->board->cleanup(par);
755  vfree(par->csum_table);
756  vfree((void __force *)info->screen_base);
757  module_put(par->board->owner);
758  dev_dbg(&dev->dev, "calling release\n");
759  framebuffer_release(info);
760  }
761  return 0;
762 }
763 
764 static struct platform_driver metronomefb_driver = {
765  .probe = metronomefb_probe,
766  .remove = __devexit_p(metronomefb_remove),
767  .driver = {
768  .owner = THIS_MODULE,
769  .name = "metronomefb",
770  },
771 };
772 
773 static int __init metronomefb_init(void)
774 {
775  return platform_driver_register(&metronomefb_driver);
776 }
777 
778 static void __exit metronomefb_exit(void)
779 {
780  platform_driver_unregister(&metronomefb_driver);
781 }
782 
783 module_param(user_wfm_size, uint, 0);
784 MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
785 
786 module_init(metronomefb_init);
787 module_exit(metronomefb_exit);
788 
789 MODULE_DESCRIPTION("fbdev driver for Metronome controller");
790 MODULE_AUTHOR("Jaya Kumar");
791 MODULE_LICENSE("GPL");