[ubicom32]: move new files out from platform support patch
[openwrt.git] / target / linux / ubicom32 / files / drivers / video / ubicom32plio80.c
1 /*
2  * drivers/video/ubicom32plio80.c
3  *      Ubicom32 80 bus PLIO buffer driver
4  *
5  * (C) Copyright 2009, Ubicom, Inc.
6  *
7  * This file is part of the Ubicom32 Linux Kernel Port.
8  *
9  * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10  * it and/or modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation, either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15  * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17  * the GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with the Ubicom32 Linux Kernel Port.  If not,
21  * see <http://www.gnu.org/licenses/>.
22  */
23
24 /*
25  * This driver was based on skeletonfb.c, Skeleton for a frame buffer device by
26  * Geert Uytterhoeven.
27  */
28
29 #include <linux/device.h>
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/version.h>
33 #include <linux/errno.h>
34 #include <linux/string.h>
35 #include <linux/mm.h>
36 #include <linux/fb.h>
37 #include <linux/init.h>
38 #include <linux/interrupt.h>
39 #include <linux/dma-mapping.h>
40 #include <linux/platform_device.h>
41 #include <linux/device.h>
42 #include <linux/uaccess.h>
43 #include <asm/plio.h>
44
45 #define DRIVER_NAME             "ubicom32plio80"
46 #define DRIVER_DESCRIPTION      "Ubicom32 80 bus PLIO frame buffer driver"
47
48 #define PALETTE_ENTRIES_NO      16
49
50 /*
51  * Option variables
52  *
53  * vram_size:   VRAM size in kilobytes, subject to alignment
54  */
55 static int vram_size = 0;
56 module_param(vram_size, int, 0);
57 MODULE_PARM_DESC(vram_size, "VRAM size, in kilobytes to allocate, should be at least the size of one screen, subject to alignment");
58
59 static int xres = 240;
60 module_param(xres, int, 0);
61 MODULE_PARM_DESC(xres, "x (horizontal) resolution");
62
63 static int yres = 320;
64 module_param(yres, int, 0);
65 MODULE_PARM_DESC(yres, "y (vertical) resolution");
66
67 static int bgr = 0;
68 module_param(bgr, int, 0);
69 MODULE_PARM_DESC(bgr, "display is BGR (Blue is MSB)");
70
71 #define BITS_PER_PIXEL  16
72
73 /*
74  * Buffer alignment, must not be 0
75  */
76 #define UBICOM32PLIO80_ALIGNMENT 4
77
78 /*
79  * PLIO FSM
80  *      16-bit data bus on port I
81  *      CS on EXTCTL[6]
82  *      WR on EXTCTL[4]
83  */
84 static const plio_fctl_t plio_fctl = {
85         .fctl0 = {
86                 .ptif_port_mode = PLIO_PORT_MODE_DI,
87                 .ptif_portd_cfg = 0,
88                 .ptif_porti_cfg = 3,
89                 .edif_ds = 6,
90                 .edif_cmp_mode = 1,
91                 .ecif_extclk_ena = 0, // enable clock output on PD7 table 2.65/p111 says extctl[0]?
92                 .icif_clk_src_sel = PLIO_CLK_IO,
93         },
94         .fctl2 = {
95                 .icif_eclk_div = 10,
96                 .icif_iclk_div = 10,
97         },
98
99         };
100
101         static const plio_config_t plio_config = {
102         .pfsm = {
103                 /*
104                  * Table 12.63
105                  */
106                 .grpsel[0] = {1,1,1,1,1,1,1,1,1,1},
107
108                 /*
109                 * Table 12.66 Counter load value
110                 */
111                 .cs_lut[0] = {0,0,0,0,0,0,0,0},
112
113                 /*
114                  * Table 2.75 PLIO PFSM Configuration Registers
115                  */
116                 //                      3     2     1     0
117                 .extctl_o_lut[0] = {0x3f, 0x2f, 0x3f, 0x3f},
118                 //                      7     6     5     4
119                 .extctl_o_lut[1] = {0x3f, 0x3f, 0x3f, 0x2f},
120         },
121         .edif = {
122                 .odr_oe = 0xffff,
123         },
124         .ecif = {
125                 .output_ena = (1 << 6) | (1 << 4),
126         },
127 };
128
129 static const u32_t ubicom32plio80_plio_fsm[] = {
130         // 0-F
131         0x00070007, 0x00070007,
132         0x00070007, 0x00070007,
133         0x00070007, 0x00070007,
134         0x00070007, 0x00070007,
135
136         0x16260806, 0x16260806,
137         0x16260806, 0x16260806,
138         0x16260806, 0x16260806,
139         0x16260806, 0x16260806,
140
141         // 10 - 1f
142         0x22061806, 0x22061806,
143         0x22061806, 0x22061806,
144         0x22061806, 0x22061806,
145         0x22061806, 0x22061806,
146
147         0x22061806, 0x22061806,
148         0x22061806, 0x22061806,
149         0x22061806, 0x22061806,
150         0x22061806, 0x22061806,
151
152         // 20 - 2f
153         0x00070806, 0x00070806,
154         0x00070806, 0x00070806,
155         0x00070806, 0x00070806,
156         0x00070806, 0x00070806,
157
158         0x00070806, 0x00070806,
159         0x00070806, 0x00070806,
160         0x00070806, 0x00070806,
161         0x00070806, 0x00070806,
162 };
163
164 /*
165  * fb_fix_screeninfo defines the non-changeable properties of the VDC, depending on what mode it is in.
166  */
167 static struct fb_fix_screeninfo ubicom32plio80_fix = {
168         .id =           "Ubicom32",
169         .type =         FB_TYPE_PACKED_PIXELS,
170         .visual =       FB_VISUAL_TRUECOLOR,
171         .accel =        FB_ACCEL_UBICOM32_PLIO80,
172 };
173
174 /*
175  * Filled in at probe time when we find out what the hardware supports
176  */
177 static struct fb_var_screeninfo ubicom32plio80_var;
178
179 /*
180  * Private data structure
181  */
182 struct ubicom32plio80_drvdata {
183         struct fb_info                  *fbinfo;
184         bool                            cmap_alloc;
185
186         /*
187          * The address of the framebuffer in memory
188          */
189         void                            *fb;
190         void                            *fb_aligned;
191
192         /*
193          * Total size of vram including alignment allowance
194          */
195         u32                             total_vram_size;
196
197         /*
198          * Fake palette of 16 colors
199          */
200         u32                             pseudo_palette[PALETTE_ENTRIES_NO];
201
202         int                             irq_req;
203
204         /*
205          * Current pointer and bytes left to transfer with the PLIO
206          */
207         void                            *xfer_ptr;
208         u32                             bytes_to_xfer;
209         u32                             busy;
210 };
211
212 static struct platform_device *ubicom32plio80_platform_device;
213
214 /*
215  * ubicom32plio80_isr
216  */
217 static int ubicom32plio80_isr(int irq, void *appdata)
218 {
219         struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)appdata;
220
221         if (!ud->bytes_to_xfer) {
222                 ubicom32_disable_interrupt(TX_FIFO_INT(PLIO_PORT));
223                 PLIO_NBR->intmask.txfifo_wm = 0;
224                 ud->busy = 0;
225                 return IRQ_HANDLED;
226         }
227
228         asm volatile (
229                 ".rept 8                                \n\t"
230                 "move.4 (%[fifo]), (%[data])4++         \n\t"
231                 ".endr                                  \n\t"
232                 : [data] "+a" (ud->xfer_ptr)
233                 : [fifo] "a" (&PLIO_NBR->tx_lo)
234         );
235
236         ud->bytes_to_xfer -= 32;
237
238         return IRQ_HANDLED;
239 }
240
241 /*
242  * ubicom32plio80_update
243  */
244 static void ubicom32plio80_update(struct ubicom32plio80_drvdata *ud, u32 *fb)
245 {
246         struct ubicom32_io_port *ri = (struct ubicom32_io_port *)RI;
247         struct ubicom32_io_port *rd = (struct ubicom32_io_port *)RD;
248
249         ud->xfer_ptr = fb;
250         ud->bytes_to_xfer = (xres * yres * 2) - 64;
251         ud->busy = 1;
252
253         ri->gpio_mask = 0;
254         rd->gpio_mask &= ~((1 << 4) | (1 << 2));
255
256         *(u32 *)(&PLIO_NBR->intclr) = ~0;
257         PLIO_NBR->intmask.txfifo_wm = 1;
258         PLIO_NBR->fifo_wm.tx = 8;
259         ubicom32_enable_interrupt(TX_FIFO_INT(PLIO_PORT));
260
261         asm volatile (
262                 ".rept 16                               \n\t"
263                 "move.4 (%[fifo]), (%[data])4++         \n\t"
264                 ".endr                                  \n\t"
265                 : [data] "+a" (ud->xfer_ptr)
266                 : [fifo] "a" (&PLIO_NBR->tx_lo)
267         );
268 }
269
270 /*
271  * ubicom32plio80_pan_display
272  *      Pans the display to a given location.  Supports only y direction panning.
273  */
274 static int ubicom32plio80_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi)
275 {
276         struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)fbi->par;
277         void *new_addr;
278
279         /*
280          * Get the last y line that would be displayed.  Since we don't support YWRAP,
281          * it must be less than our virtual y size.
282          */
283         u32 lasty = var->yoffset + var->yres;
284         if (lasty > fbi->var.yres_virtual) {
285                 /*
286                  * We would fall off the end of our frame buffer if we panned here.
287                  */
288                 return -EINVAL;
289         }
290
291         if (var->xoffset) {
292                 /*
293                  * We don't support panning in the x direction
294                  */
295                 return -EINVAL;
296         }
297
298         /*
299          * Everything looks sane, go ahead and pan
300          *
301          * We have to calculate a new address for the VDC to look at
302          */
303         new_addr = ud->fb_aligned + (var->yoffset * fbi->fix.line_length);
304
305         return 0;
306 }
307
308 /*
309  * ubicom32plio80_setcolreg
310  *      Sets a color in our virtual palette
311  */
312 static int ubicom32plio80_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi)
313 {
314         u32 *palette = fbi->pseudo_palette;
315
316         if (regno >= PALETTE_ENTRIES_NO) {
317                 return -EINVAL;
318         }
319
320         /*
321          * We only use 8 bits from each color
322          */
323         red >>= 8;
324         green >>= 8;
325         blue >>= 8;
326
327         /*
328          * Convert any grayscale values
329          */
330         if (fbi->var.grayscale) {
331                 u16 gray = red + green + blue;
332                 gray += (gray >> 2) + (gray >> 3) - (gray >> 7);
333                 gray >>= 2;
334                 if (gray > 255) {
335                         gray = 255;
336                 }
337                 red = gray;
338                 blue = gray;
339                 green = gray;
340         }
341
342         palette[regno] = (red << fbi->var.red.offset) | (green << fbi->var.green.offset) |
343                          (blue << fbi->var.blue.offset);
344
345         return 0;
346 }
347
348 /*
349  * ubicom32plio80_mmap
350  */
351 static int ubicom32plio80_mmap(struct fb_info *info, struct vm_area_struct *vma)
352 {
353         struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)info->par;
354
355         vma->vm_start = (unsigned long)(ud->fb_aligned);
356
357         vma->vm_end = vma->vm_start + info->fix.smem_len;
358
359         /* For those who don't understand how mmap works, go read
360          *   Documentation/nommu-mmap.txt.
361          * For those that do, you will know that the VM_MAYSHARE flag
362          * must be set in the vma->vm_flags structure on noMMU
363          *   Other flags can be set, and are documented in
364          *   include/linux/mm.h
365          */
366
367         vma->vm_flags |=  VM_MAYSHARE | VM_SHARED;
368
369         return 0;
370 }
371
372 /*
373  * ubicom32plio80_check_var
374  *      Check the var, tweak it but don't change operational parameters.
375  */
376 static int ubicom32plio80_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
377 {
378         struct ubicom32plio80_drvdata *ud = (struct ubicom32plio80_drvdata *)info->par;
379         u32 line_size = var->xres * (BITS_PER_PIXEL / 8);
380
381         /*
382          * See if we can handle this bpp
383          */
384         if (var->bits_per_pixel > BITS_PER_PIXEL) {
385                 return -EINVAL;
386         }
387         var->bits_per_pixel = BITS_PER_PIXEL;
388
389         /*
390          * See if we have enough memory to handle this resolution
391          */
392         if ((line_size * var->yres * BITS_PER_PIXEL / 8) > ud->total_vram_size) {
393                 return -EINVAL;
394         }
395
396         var->xres_virtual = var->xres;
397         var->yres_virtual = ud->total_vram_size / line_size;
398
399         var->red.length = 5;
400         var->green.length = 6;
401         var->green.offset = 5;
402         var->blue.length = 5;
403         var->transp.offset = var->transp.length = 0;
404
405         if (bgr) {
406                 var->red.offset = 0;
407                 var->blue.offset = 11;
408         } else {
409                 var->red.offset = 11;
410                 var->blue.offset = 0;
411         }
412
413         var->nonstd = 0;
414         var->height = -1;
415         var->width = -1;
416         var->vmode = FB_VMODE_NONINTERLACED;
417         var->sync = 0;
418
419         return 0;
420 }
421
422 /*
423  * ubicom32plio80_set_par
424  *      Set the video mode according to info->var
425  */
426 static int ubicom32plio80_set_par(struct fb_info *info)
427 {
428         /*
429          * Anything changed?
430          */
431         if ((xres == info->var.xres) && (yres == info->var.yres)) {
432                 return 0;
433         }
434
435         /*
436          * Implement changes
437          */
438         xres = info->var.xres;
439         yres = info->var.yres;
440         info->fix.visual = FB_VISUAL_TRUECOLOR;
441         info->fix.xpanstep = 0;
442         info->fix.ypanstep = 1;
443         info->fix.line_length = xres * (BITS_PER_PIXEL / 8);
444
445         return 0;
446 }
447
448 /*
449  * ubicom32plio80_ops
450  *      List of supported operations
451  */
452 static struct fb_ops ubicom32plio80_ops =
453 {
454         .owner                  = THIS_MODULE,
455         .fb_pan_display         = ubicom32plio80_pan_display,
456         .fb_setcolreg           = ubicom32plio80_setcolreg,
457         .fb_mmap                = ubicom32plio80_mmap,
458         .fb_check_var           = ubicom32plio80_check_var,
459         .fb_set_par             = ubicom32plio80_set_par,
460         .fb_fillrect            = cfb_fillrect,
461         .fb_copyarea            = cfb_copyarea,
462         .fb_imageblit           = cfb_imageblit,
463 };
464
465 /*
466  * ubicom32plio80_release
467  */
468 static int ubicom32plio80_release(struct device *dev)
469 {
470         struct ubicom32plio80_drvdata *ud = dev_get_drvdata(dev);
471
472         unregister_framebuffer(ud->fbinfo);
473
474         if (ud->irq_req) {
475                 free_irq(TX_FIFO_INT(PLIO_PORT), ud);
476         }
477         if (ud->cmap_alloc) {
478                 fb_dealloc_cmap(&ud->fbinfo->cmap);
479         }
480
481         if (ud->fb) {
482                 kfree(ud->fb);
483         }
484
485         framebuffer_release(ud->fbinfo);
486         dev_set_drvdata(dev, NULL);
487
488         return 0;
489 }
490
491 /*
492  * ubicom32plio80_platform_probe
493  */
494 static int __init ubicom32plio80_platform_probe(struct platform_device *pdev)
495 {
496         struct ubicom32plio80_drvdata *ud;
497         struct fb_info *fbinfo;
498         int rc;
499         size_t fbsize;
500         struct device *dev = &pdev->dev;
501         int offset;
502
503         /*
504          * This is the minimum VRAM size
505          */
506         fbsize = xres * yres * 2;
507         if (!vram_size) {
508                 vram_size = (fbsize + 1023) / 1024;
509         } else {
510                 if (fbsize > (vram_size * 1024)) {
511                         dev_err(dev, "Not enough VRAM for display, need >= %u bytes\n", fbsize);
512                         return -ENOMEM; // should be ebadparam?
513                 }
514         }
515
516         /*
517          * Allocate the framebuffer instance + our private data
518          */
519         fbinfo = framebuffer_alloc(sizeof(struct ubicom32plio80_drvdata), &pdev->dev);
520         if (!fbinfo) {
521                 dev_err(dev, "Not enough memory to allocate instance.\n");
522                 return -ENOMEM;
523         }
524
525         /*
526          * Fill in our private data.
527          */
528         ud = (struct ubicom32plio80_drvdata *)fbinfo->par;
529         ud->fbinfo = fbinfo;
530         dev_set_drvdata(dev, ud);
531
532         /*
533          * Allocate and align the requested amount of VRAM
534          */
535         ud->total_vram_size = (vram_size * 1024) + UBICOM32PLIO80_ALIGNMENT;
536         ud->fb = kmalloc(ud->total_vram_size, GFP_KERNEL);
537         if (ud->fb == NULL) {
538                 dev_err(dev, "Couldn't allocate VRAM\n");
539                 rc = -ENOMEM;
540                 goto fail;
541         }
542
543         offset = (u32_t)ud->fb & (UBICOM32PLIO80_ALIGNMENT - 1);
544         if (!offset) {
545                 ud->fb_aligned = ud->fb;
546         } else {
547                 offset =  UBICOM32PLIO80_ALIGNMENT - offset;
548                 ud->fb_aligned = ud->fb + offset;
549         }
550
551         /*
552          * Clear the entire frame buffer
553          */
554         memset(ud->fb_aligned, 0, vram_size * 1024);
555
556         /*
557          * Fill in the fb_var_screeninfo structure
558          */
559         memset(&ubicom32plio80_var, 0, sizeof(ubicom32plio80_var));
560         ubicom32plio80_var.bits_per_pixel = BITS_PER_PIXEL;
561         ubicom32plio80_var.red.length = 5;
562         ubicom32plio80_var.green.length = 6;
563         ubicom32plio80_var.green.offset = 5;
564         ubicom32plio80_var.blue.length = 5;
565         ubicom32plio80_var.activate = FB_ACTIVATE_NOW;
566
567         if (bgr) {
568                 ubicom32plio80_var.red.offset = 0;
569                 ubicom32plio80_var.blue.offset = 11;
570         } else {
571                 ubicom32plio80_var.red.offset = 11;
572                 ubicom32plio80_var.blue.offset = 0;
573         }
574
575         /*
576          * Fill in the fb_info structure
577          */
578         ud->fbinfo->device = dev;
579         ud->fbinfo->screen_base = (void *)ud->fb_aligned;
580         ud->fbinfo->fbops = &ubicom32plio80_ops;
581         ud->fbinfo->fix = ubicom32plio80_fix;
582         ud->fbinfo->fix.smem_start = (u32)ud->fb_aligned;
583         ud->fbinfo->fix.smem_len = vram_size * 1024;
584         ud->fbinfo->fix.line_length = xres * 2;
585         ud->fbinfo->fix.mmio_start = (u32)ud;
586         ud->fbinfo->fix.mmio_len = sizeof(struct ubicom32plio80_drvdata);
587
588         /*
589          * We support panning in the y direction only
590          */
591         ud->fbinfo->fix.xpanstep = 0;
592         ud->fbinfo->fix.ypanstep = 1;
593
594         ud->fbinfo->pseudo_palette = ud->pseudo_palette;
595         ud->fbinfo->flags = FBINFO_DEFAULT;
596         ud->fbinfo->var = ubicom32plio80_var;
597         ud->fbinfo->var.xres = xres;
598         ud->fbinfo->var.yres = yres;
599
600         /*
601          * We cannot pan in the X direction, so xres_virtual is xres
602          * We can pan in the Y direction, so yres_virtual is vram_size / ud->fbinfo->fix.line_length
603          */
604         ud->fbinfo->var.xres_virtual = xres;
605         ud->fbinfo->var.yres_virtual = (vram_size * 1024) / ud->fbinfo->fix.line_length;
606
607         /*
608          * Allocate a color map
609          */
610         rc = fb_alloc_cmap(&ud->fbinfo->cmap, PALETTE_ENTRIES_NO, 0);
611         if (rc) {
612                 dev_err(dev, "Fail to allocate colormap (%d entries)\n",
613                         PALETTE_ENTRIES_NO);
614                 goto fail;
615         }
616         ud->cmap_alloc = true;
617
618         /*
619          * Register new frame buffer
620          */
621         rc = register_framebuffer(ud->fbinfo);
622         if (rc) {
623                 dev_err(dev, "Could not register frame buffer\n");
624                 goto fail;
625         }
626
627         /*
628          * request the PLIO IRQ
629          */
630         rc = request_irq(TX_FIFO_INT(PLIO_PORT), ubicom32plio80_isr, IRQF_DISABLED, "ubicom32plio80", ud);
631         if (rc) {
632                 dev_err(dev, "Could not request IRQ\n");
633                 goto fail;
634         }
635         ud->irq_req = 1;
636
637         /*
638          * Clear any garbage out of the TX FIFOs (idif_txfifo_flush)
639          *
640          * cast through ubicom32_io_port to make sure the compiler does a word write
641          */
642         ((struct ubicom32_io_port *)PLIO_NBR)->int_set = (1 << 18);
643
644         /*
645          * Start up the state machine
646          */
647         plio_init(&plio_fctl, &plio_config, (plio_sram_t *)ubicom32plio80_plio_fsm, sizeof(ubicom32plio80_plio_fsm));
648         PLIO_NBR->fctl0.pfsm_cmd = 0;
649
650         ubicom32plio80_update(ud, ud->fb_aligned);
651
652         /*
653          * Tell the log we are here
654          */
655         dev_info(dev, "fbaddr=%p align=%p, size=%uKB screen(%ux%u) virt(%ux%u)\n",
656                 ud->fb, ud->fb_aligned, vram_size, ud->fbinfo->var.xres, ud->fbinfo->var.yres,
657                 ud->fbinfo->var.xres_virtual, ud->fbinfo->var.yres_virtual);
658
659         /*
660          * Success
661          */
662         return 0;
663
664 fail:
665         ubicom32plio80_release(dev);
666         return rc;
667 }
668
669 /*
670  * ubicom32plio80_platform_remove
671  */
672 static int ubicom32plio80_platform_remove(struct platform_device *pdev)
673 {
674         dev_info(&(pdev->dev), "Ubicom32 FB Driver Remove\n");
675         return ubicom32plio80_release(&pdev->dev);
676 }
677
678 static struct platform_driver ubicom32plio80_platform_driver = {
679         .probe          = ubicom32plio80_platform_probe,
680         .remove         = ubicom32plio80_platform_remove,
681         .driver = {
682                 .name = DRIVER_NAME,
683                 .owner = THIS_MODULE,
684         },
685 };
686
687 #ifndef MODULE
688 /*
689  * ubicom32plio80_setup
690  *      Process kernel boot options
691  */
692 static int __init ubicom32plio80_setup(char *options)
693 {
694         char *this_opt;
695
696         if (!options || !*options) {
697                 return 0;
698         }
699
700         while ((this_opt = strsep(&options, ",")) != NULL) {
701                 if (!*this_opt) {
702                         continue;
703                 }
704
705                 if (!strncmp(this_opt, "vram_size=", 10)) {
706                         vram_size = simple_strtoul(this_opt + 10, NULL, 0);
707                         continue;
708                 }
709
710                 if (!strncmp(this_opt, "bgr=", 4)) {
711                         bgr = simple_strtoul(this_opt + 4, NULL, 0);
712                         continue;
713                 }
714
715                 if (!strncmp(this_opt, "xres=", 5)) {
716                         xres = simple_strtoul(this_opt + 5, NULL, 0);
717                         continue;
718                 }
719
720                 if (!strncmp(this_opt, "yres=", 5)) {
721                         yres = simple_strtoul(this_opt + 5, NULL, 0);
722                         continue;
723                 }
724         }
725         return 0;
726 }
727 #endif /* MODULE */
728
729 /*
730  * ubicom32plio80_init
731  */
732 static int __devinit ubicom32plio80_init(void)
733 {
734         int ret;
735
736 #ifndef MODULE
737         /*
738          * Get kernel boot options (in 'video=ubicom32plio80:<options>')
739          */
740         char *option = NULL;
741
742         if (fb_get_options(DRIVER_NAME, &option)) {
743                 return -ENODEV;
744         }
745         ubicom32plio80_setup(option);
746 #endif /* MODULE */
747
748         ret = platform_driver_register(&ubicom32plio80_platform_driver);
749
750         if (!ret) {
751                 ubicom32plio80_platform_device = platform_device_alloc(DRIVER_NAME, 0);
752
753                 if (ubicom32plio80_platform_device)
754                         ret = platform_device_add(ubicom32plio80_platform_device);
755                 else
756                         ret = -ENOMEM;
757
758                 if (ret) {
759                         platform_device_put(ubicom32plio80_platform_device);
760                         platform_driver_unregister(&ubicom32plio80_platform_driver);
761                 }
762         }
763
764         return ret;
765 }
766 module_init(ubicom32plio80_init);
767
768 /*
769  * ubicom32plio80_exit
770  */
771 static void __exit ubicom32plio80_exit(void)
772 {
773         platform_device_unregister(ubicom32plio80_platform_device);
774         platform_driver_unregister(&ubicom32plio80_platform_driver);
775 }
776 module_exit(ubicom32plio80_exit);
777
778 MODULE_LICENSE("GPL");
779 MODULE_AUTHOR("Patrick Tjin <@ubicom.com>");
780 MODULE_DESCRIPTION(DRIVER_DESCRIPTION);