]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/video/omap/omapfb_main.c
f74aec91aee09882fa943b1d75389461ad97ec49
[karo-tx-linux.git] / drivers / video / omap / omapfb_main.c
1 /*
2  * Framebuffer driver for TI OMAP boards
3  *
4  * Copyright (C) 2004 Nokia Corporation
5  * Author: Imre Deak <imre.deak@nokia.com>
6  *
7  * Acknowledgements:
8  *   Alex McMains <aam@ridgerun.com>       - Original driver
9  *   Juha Yrjola <juha.yrjola@nokia.com>   - Original driver and improvements
10  *   Dirk Behme <dirk.behme@de.bosch.com>  - changes for 2.6 kernel API
11  *   Texas Instruments                     - H3 support
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU General Public License as published by the
15  * Free Software Foundation; either version 2 of the License, or (at your
16  * option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27 #include <linux/platform_device.h>
28 #include <linux/mm.h>
29 #include <linux/uaccess.h>
30
31 #include <plat/dma.h>
32
33 #include "omapfb.h"
34 #include "lcdc.h"
35 #include "dispc.h"
36
37 #define MODULE_NAME     "omapfb"
38
39 static unsigned int     def_accel;
40 static unsigned long    def_vram[OMAPFB_PLANE_NUM];
41 static unsigned int     def_vram_cnt;
42 static unsigned long    def_vxres;
43 static unsigned long    def_vyres;
44 static unsigned int     def_rotate;
45 static unsigned int     def_mirror;
46
47 #ifdef CONFIG_FB_OMAP_MANUAL_UPDATE
48 static int              manual_update = 1;
49 #else
50 static int              manual_update;
51 #endif
52
53 static struct platform_device   *fbdev_pdev;
54 static struct lcd_panel         *fbdev_panel;
55 static struct omapfb_device     *omapfb_dev;
56
57 struct caps_table_struct {
58         unsigned long flag;
59         const char *name;
60 };
61
62 static struct caps_table_struct ctrl_caps[] = {
63         { OMAPFB_CAPS_MANUAL_UPDATE,  "manual update" },
64         { OMAPFB_CAPS_TEARSYNC,       "tearing synchronization" },
65         { OMAPFB_CAPS_PLANE_RELOCATE_MEM, "relocate plane memory" },
66         { OMAPFB_CAPS_PLANE_SCALE,    "scale plane" },
67         { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" },
68         { OMAPFB_CAPS_WINDOW_SCALE,   "scale window" },
69         { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" },
70         { OMAPFB_CAPS_WINDOW_ROTATE,  "rotate window" },
71         { OMAPFB_CAPS_SET_BACKLIGHT,  "backlight setting" },
72 };
73
74 static struct caps_table_struct color_caps[] = {
75         { 1 << OMAPFB_COLOR_RGB565,     "RGB565", },
76         { 1 << OMAPFB_COLOR_YUV422,     "YUV422", },
77         { 1 << OMAPFB_COLOR_YUV420,     "YUV420", },
78         { 1 << OMAPFB_COLOR_CLUT_8BPP,  "CLUT8", },
79         { 1 << OMAPFB_COLOR_CLUT_4BPP,  "CLUT4", },
80         { 1 << OMAPFB_COLOR_CLUT_2BPP,  "CLUT2", },
81         { 1 << OMAPFB_COLOR_CLUT_1BPP,  "CLUT1", },
82         { 1 << OMAPFB_COLOR_RGB444,     "RGB444", },
83         { 1 << OMAPFB_COLOR_YUY422,     "YUY422", },
84 };
85
86 /* dummy device for clocks */
87 static struct platform_device omapdss_device = {
88         .name           = "omapdss",
89         .id             = -1,
90 };
91
92 /*
93  * ---------------------------------------------------------------------------
94  * LCD panel
95  * ---------------------------------------------------------------------------
96  */
97 extern struct lcd_ctrl hwa742_ctrl;
98 extern struct lcd_ctrl blizzard_ctrl;
99
100 static const struct lcd_ctrl *ctrls[] = {
101 #ifdef CONFIG_ARCH_OMAP1
102         &omap1_int_ctrl,
103 #else
104         &omap2_int_ctrl,
105 #endif
106
107 #ifdef CONFIG_FB_OMAP_LCDC_HWA742
108         &hwa742_ctrl,
109 #endif
110 #ifdef CONFIG_FB_OMAP_LCDC_BLIZZARD
111         &blizzard_ctrl,
112 #endif
113 };
114
115 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
116 #ifdef CONFIG_ARCH_OMAP1
117 extern struct lcd_ctrl_extif omap1_ext_if;
118 #else
119 extern struct lcd_ctrl_extif omap2_ext_if;
120 #endif
121 #endif
122
123 static void omapfb_rqueue_lock(struct omapfb_device *fbdev)
124 {
125         mutex_lock(&fbdev->rqueue_mutex);
126 }
127
128 static void omapfb_rqueue_unlock(struct omapfb_device *fbdev)
129 {
130         mutex_unlock(&fbdev->rqueue_mutex);
131 }
132
133 /*
134  * ---------------------------------------------------------------------------
135  * LCD controller and LCD DMA
136  * ---------------------------------------------------------------------------
137  */
138 /* Lookup table to map elem size to elem type. */
139 static const int dma_elem_type[] = {
140         0,
141         OMAP_DMA_DATA_TYPE_S8,
142         OMAP_DMA_DATA_TYPE_S16,
143         0,
144         OMAP_DMA_DATA_TYPE_S32,
145 };
146
147 /*
148  * Allocate resources needed for LCD controller and LCD DMA operations. Video
149  * memory is allocated from system memory according to the virtual display
150  * size, except if a bigger memory size is specified explicitly as a kernel
151  * parameter.
152  */
153 static int ctrl_init(struct omapfb_device *fbdev)
154 {
155         int r;
156         int i;
157
158         /* kernel/module vram parameters override boot tags/board config */
159         if (def_vram_cnt) {
160                 for (i = 0; i < def_vram_cnt; i++)
161                         fbdev->mem_desc.region[i].size =
162                                 PAGE_ALIGN(def_vram[i]);
163                 fbdev->mem_desc.region_cnt = i;
164         } else {
165                 struct omapfb_platform_data *conf;
166
167                 conf = fbdev->dev->platform_data;
168                 fbdev->mem_desc = conf->mem_desc;
169         }
170
171         if (!fbdev->mem_desc.region_cnt) {
172                 struct lcd_panel *panel = fbdev->panel;
173                 int def_size;
174                 int bpp = panel->bpp;
175
176                 /* 12 bpp is packed in 16 bits */
177                 if (bpp == 12)
178                         bpp = 16;
179                 def_size = def_vxres * def_vyres * bpp / 8;
180                 fbdev->mem_desc.region_cnt = 1;
181                 fbdev->mem_desc.region[0].size = PAGE_ALIGN(def_size);
182         }
183         r = fbdev->ctrl->init(fbdev, 0, &fbdev->mem_desc);
184         if (r < 0) {
185                 dev_err(fbdev->dev, "controller initialization failed (%d)\n",
186                         r);
187                 return r;
188         }
189
190 #ifdef DEBUG
191         for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
192                 dev_dbg(fbdev->dev, "region%d phys %08x virt %p size=%lu\n",
193                          i,
194                          fbdev->mem_desc.region[i].paddr,
195                          fbdev->mem_desc.region[i].vaddr,
196                          fbdev->mem_desc.region[i].size);
197         }
198 #endif
199         return 0;
200 }
201
202 static void ctrl_cleanup(struct omapfb_device *fbdev)
203 {
204         fbdev->ctrl->cleanup();
205 }
206
207 /* Must be called with fbdev->rqueue_mutex held. */
208 static int ctrl_change_mode(struct fb_info *fbi)
209 {
210         int r;
211         unsigned long offset;
212         struct omapfb_plane_struct *plane = fbi->par;
213         struct omapfb_device *fbdev = plane->fbdev;
214         struct fb_var_screeninfo *var = &fbi->var;
215
216         offset = var->yoffset * fbi->fix.line_length +
217                  var->xoffset * var->bits_per_pixel / 8;
218
219         if (fbdev->ctrl->sync)
220                 fbdev->ctrl->sync();
221         r = fbdev->ctrl->setup_plane(plane->idx, plane->info.channel_out,
222                                  offset, var->xres_virtual,
223                                  plane->info.pos_x, plane->info.pos_y,
224                                  var->xres, var->yres, plane->color_mode);
225         if (r < 0)
226                 return r;
227
228         if (fbdev->ctrl->set_rotate != NULL) {
229                 r = fbdev->ctrl->set_rotate(var->rotate);
230                 if (r < 0)
231                         return r;
232         }
233
234         if (fbdev->ctrl->set_scale != NULL)
235                 r = fbdev->ctrl->set_scale(plane->idx,
236                                    var->xres, var->yres,
237                                    plane->info.out_width,
238                                    plane->info.out_height);
239
240         return r;
241 }
242
243 /*
244  * ---------------------------------------------------------------------------
245  * fbdev framework callbacks and the ioctl interface
246  * ---------------------------------------------------------------------------
247  */
248 /* Called each time the omapfb device is opened */
249 static int omapfb_open(struct fb_info *info, int user)
250 {
251         return 0;
252 }
253
254 static void omapfb_sync(struct fb_info *info);
255
256 /* Called when the omapfb device is closed. We make sure that any pending
257  * gfx DMA operations are ended, before we return. */
258 static int omapfb_release(struct fb_info *info, int user)
259 {
260         omapfb_sync(info);
261         return 0;
262 }
263
264 /* Store a single color palette entry into a pseudo palette or the hardware
265  * palette if one is available. For now we support only 16bpp and thus store
266  * the entry only to the pseudo palette.
267  */
268 static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green,
269                         u_int blue, u_int transp, int update_hw_pal)
270 {
271         struct omapfb_plane_struct *plane = info->par;
272         struct omapfb_device *fbdev = plane->fbdev;
273         struct fb_var_screeninfo *var = &info->var;
274         int r = 0;
275
276         switch (plane->color_mode) {
277         case OMAPFB_COLOR_YUV422:
278         case OMAPFB_COLOR_YUV420:
279         case OMAPFB_COLOR_YUY422:
280                 r = -EINVAL;
281                 break;
282         case OMAPFB_COLOR_CLUT_8BPP:
283         case OMAPFB_COLOR_CLUT_4BPP:
284         case OMAPFB_COLOR_CLUT_2BPP:
285         case OMAPFB_COLOR_CLUT_1BPP:
286                 if (fbdev->ctrl->setcolreg)
287                         r = fbdev->ctrl->setcolreg(regno, red, green, blue,
288                                                         transp, update_hw_pal);
289                 /* Fallthrough */
290         case OMAPFB_COLOR_RGB565:
291         case OMAPFB_COLOR_RGB444:
292                 if (r != 0)
293                         break;
294
295                 if (regno < 0) {
296                         r = -EINVAL;
297                         break;
298                 }
299
300                 if (regno < 16) {
301                         u16 pal;
302                         pal = ((red >> (16 - var->red.length)) <<
303                                         var->red.offset) |
304                               ((green >> (16 - var->green.length)) <<
305                                         var->green.offset) |
306                               (blue >> (16 - var->blue.length));
307                         ((u32 *)(info->pseudo_palette))[regno] = pal;
308                 }
309                 break;
310         default:
311                 BUG();
312         }
313         return r;
314 }
315
316 static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
317                             u_int transp, struct fb_info *info)
318 {
319         return _setcolreg(info, regno, red, green, blue, transp, 1);
320 }
321
322 static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
323 {
324         int count, index, r;
325         u16 *red, *green, *blue, *transp;
326         u16 trans = 0xffff;
327
328         red     = cmap->red;
329         green   = cmap->green;
330         blue    = cmap->blue;
331         transp  = cmap->transp;
332         index   = cmap->start;
333
334         for (count = 0; count < cmap->len; count++) {
335                 if (transp)
336                         trans = *transp++;
337                 r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,
338                                 count == cmap->len - 1);
339                 if (r != 0)
340                         return r;
341         }
342
343         return 0;
344 }
345
346 static int omapfb_update_full_screen(struct fb_info *fbi);
347
348 static int omapfb_blank(int blank, struct fb_info *fbi)
349 {
350         struct omapfb_plane_struct *plane = fbi->par;
351         struct omapfb_device *fbdev = plane->fbdev;
352         int do_update = 0;
353         int r = 0;
354
355         omapfb_rqueue_lock(fbdev);
356         switch (blank) {
357         case FB_BLANK_UNBLANK:
358                 if (fbdev->state == OMAPFB_SUSPENDED) {
359                         if (fbdev->ctrl->resume)
360                                 fbdev->ctrl->resume();
361                         fbdev->panel->enable(fbdev->panel);
362                         fbdev->state = OMAPFB_ACTIVE;
363                         if (fbdev->ctrl->get_update_mode() ==
364                                         OMAPFB_MANUAL_UPDATE)
365                                 do_update = 1;
366                 }
367                 break;
368         case FB_BLANK_POWERDOWN:
369                 if (fbdev->state == OMAPFB_ACTIVE) {
370                         fbdev->panel->disable(fbdev->panel);
371                         if (fbdev->ctrl->suspend)
372                                 fbdev->ctrl->suspend();
373                         fbdev->state = OMAPFB_SUSPENDED;
374                 }
375                 break;
376         default:
377                 r = -EINVAL;
378         }
379         omapfb_rqueue_unlock(fbdev);
380
381         if (r == 0 && do_update)
382                 r = omapfb_update_full_screen(fbi);
383
384         return r;
385 }
386
387 static void omapfb_sync(struct fb_info *fbi)
388 {
389         struct omapfb_plane_struct *plane = fbi->par;
390         struct omapfb_device *fbdev = plane->fbdev;
391
392         omapfb_rqueue_lock(fbdev);
393         if (fbdev->ctrl->sync)
394                 fbdev->ctrl->sync();
395         omapfb_rqueue_unlock(fbdev);
396 }
397
398 /*
399  * Set fb_info.fix fields and also updates fbdev.
400  * When calling this fb_info.var must be set up already.
401  */
402 static void set_fb_fix(struct fb_info *fbi, int from_init)
403 {
404         struct fb_fix_screeninfo *fix = &fbi->fix;
405         struct fb_var_screeninfo *var = &fbi->var;
406         struct omapfb_plane_struct *plane = fbi->par;
407         struct omapfb_mem_region *rg;
408         int bpp;
409
410         rg = &plane->fbdev->mem_desc.region[plane->idx];
411         fbi->screen_base        = rg->vaddr;
412
413         if (!from_init) {
414                 mutex_lock(&fbi->mm_lock);
415                 fix->smem_start         = rg->paddr;
416                 fix->smem_len           = rg->size;
417                 mutex_unlock(&fbi->mm_lock);
418         } else {
419                 fix->smem_start         = rg->paddr;
420                 fix->smem_len           = rg->size;
421         }
422
423         fix->type = FB_TYPE_PACKED_PIXELS;
424         bpp = var->bits_per_pixel;
425         if (var->nonstd)
426                 fix->visual = FB_VISUAL_PSEUDOCOLOR;
427         else switch (var->bits_per_pixel) {
428         case 16:
429         case 12:
430                 fix->visual = FB_VISUAL_TRUECOLOR;
431                 /* 12bpp is stored in 16 bits */
432                 bpp = 16;
433                 break;
434         case 1:
435         case 2:
436         case 4:
437         case 8:
438                 fix->visual = FB_VISUAL_PSEUDOCOLOR;
439                 break;
440         }
441         fix->accel              = FB_ACCEL_OMAP1610;
442         fix->line_length        = var->xres_virtual * bpp / 8;
443 }
444
445 static int set_color_mode(struct omapfb_plane_struct *plane,
446                           struct fb_var_screeninfo *var)
447 {
448         switch (var->nonstd) {
449         case 0:
450                 break;
451         case OMAPFB_COLOR_YUV422:
452                 var->bits_per_pixel = 16;
453                 plane->color_mode = var->nonstd;
454                 return 0;
455         case OMAPFB_COLOR_YUV420:
456                 var->bits_per_pixel = 12;
457                 plane->color_mode = var->nonstd;
458                 return 0;
459         case OMAPFB_COLOR_YUY422:
460                 var->bits_per_pixel = 16;
461                 plane->color_mode = var->nonstd;
462                 return 0;
463         default:
464                 return -EINVAL;
465         }
466
467         switch (var->bits_per_pixel) {
468         case 1:
469                 plane->color_mode = OMAPFB_COLOR_CLUT_1BPP;
470                 return 0;
471         case 2:
472                 plane->color_mode = OMAPFB_COLOR_CLUT_2BPP;
473                 return 0;
474         case 4:
475                 plane->color_mode = OMAPFB_COLOR_CLUT_4BPP;
476                 return 0;
477         case 8:
478                 plane->color_mode = OMAPFB_COLOR_CLUT_8BPP;
479                 return 0;
480         case 12:
481                 var->bits_per_pixel = 16;
482                 plane->color_mode = OMAPFB_COLOR_RGB444;
483                 return 0;
484         case 16:
485                 plane->color_mode = OMAPFB_COLOR_RGB565;
486                 return 0;
487         default:
488                 return -EINVAL;
489         }
490 }
491
492 /*
493  * Check the values in var against our capabilities and in case of out of
494  * bound values try to adjust them.
495  */
496 static int set_fb_var(struct fb_info *fbi,
497                       struct fb_var_screeninfo *var)
498 {
499         int             bpp;
500         unsigned long   max_frame_size;
501         unsigned long   line_size;
502         int             xres_min, xres_max;
503         int             yres_min, yres_max;
504         struct omapfb_plane_struct *plane = fbi->par;
505         struct omapfb_device *fbdev = plane->fbdev;
506         struct lcd_panel *panel = fbdev->panel;
507
508         if (set_color_mode(plane, var) < 0)
509                 return -EINVAL;
510
511         bpp = var->bits_per_pixel;
512         if (plane->color_mode == OMAPFB_COLOR_RGB444)
513                 bpp = 16;
514
515         switch (var->rotate) {
516         case 0:
517         case 180:
518                 xres_min = OMAPFB_PLANE_XRES_MIN;
519                 xres_max = panel->x_res;
520                 yres_min = OMAPFB_PLANE_YRES_MIN;
521                 yres_max = panel->y_res;
522                 if (cpu_is_omap15xx()) {
523                         var->xres = panel->x_res;
524                         var->yres = panel->y_res;
525                 }
526                 break;
527         case 90:
528         case 270:
529                 xres_min = OMAPFB_PLANE_YRES_MIN;
530                 xres_max = panel->y_res;
531                 yres_min = OMAPFB_PLANE_XRES_MIN;
532                 yres_max = panel->x_res;
533                 if (cpu_is_omap15xx()) {
534                         var->xres = panel->y_res;
535                         var->yres = panel->x_res;
536                 }
537                 break;
538         default:
539                 return -EINVAL;
540         }
541
542         if (var->xres < xres_min)
543                 var->xres = xres_min;
544         if (var->yres < yres_min)
545                 var->yres = yres_min;
546         if (var->xres > xres_max)
547                 var->xres = xres_max;
548         if (var->yres > yres_max)
549                 var->yres = yres_max;
550
551         if (var->xres_virtual < var->xres)
552                 var->xres_virtual = var->xres;
553         if (var->yres_virtual < var->yres)
554                 var->yres_virtual = var->yres;
555         max_frame_size = fbdev->mem_desc.region[plane->idx].size;
556         line_size = var->xres_virtual * bpp / 8;
557         if (line_size * var->yres_virtual > max_frame_size) {
558                 /* Try to keep yres_virtual first */
559                 line_size = max_frame_size / var->yres_virtual;
560                 var->xres_virtual = line_size * 8 / bpp;
561                 if (var->xres_virtual < var->xres) {
562                         /* Still doesn't fit. Shrink yres_virtual too */
563                         var->xres_virtual = var->xres;
564                         line_size = var->xres * bpp / 8;
565                         var->yres_virtual = max_frame_size / line_size;
566                 }
567                 /* Recheck this, as the virtual size changed. */
568                 if (var->xres_virtual < var->xres)
569                         var->xres = var->xres_virtual;
570                 if (var->yres_virtual < var->yres)
571                         var->yres = var->yres_virtual;
572                 if (var->xres < xres_min || var->yres < yres_min)
573                         return -EINVAL;
574         }
575         if (var->xres + var->xoffset > var->xres_virtual)
576                 var->xoffset = var->xres_virtual - var->xres;
577         if (var->yres + var->yoffset > var->yres_virtual)
578                 var->yoffset = var->yres_virtual - var->yres;
579
580         if (plane->color_mode == OMAPFB_COLOR_RGB444) {
581                 var->red.offset   = 8; var->red.length   = 4;
582                                                 var->red.msb_right   = 0;
583                 var->green.offset = 4; var->green.length = 4;
584                                                 var->green.msb_right = 0;
585                 var->blue.offset  = 0; var->blue.length  = 4;
586                                                 var->blue.msb_right  = 0;
587         } else {
588                 var->red.offset  = 11; var->red.length   = 5;
589                                                 var->red.msb_right   = 0;
590                 var->green.offset = 5;  var->green.length = 6;
591                                                 var->green.msb_right = 0;
592                 var->blue.offset = 0;  var->blue.length  = 5;
593                                                 var->blue.msb_right  = 0;
594         }
595
596         var->height             = -1;
597         var->width              = -1;
598         var->grayscale          = 0;
599
600         /* pixclock in ps, the rest in pixclock */
601         var->pixclock           = 10000000 / (panel->pixel_clock / 100);
602         var->left_margin        = panel->hfp;
603         var->right_margin       = panel->hbp;
604         var->upper_margin       = panel->vfp;
605         var->lower_margin       = panel->vbp;
606         var->hsync_len          = panel->hsw;
607         var->vsync_len          = panel->vsw;
608
609         /* TODO: get these from panel->config */
610         var->vmode              = FB_VMODE_NONINTERLACED;
611         var->sync               = 0;
612
613         return 0;
614 }
615
616
617 /* Set rotation (0, 90, 180, 270 degree), and switch to the new mode. */
618 static void omapfb_rotate(struct fb_info *fbi, int rotate)
619 {
620         struct omapfb_plane_struct *plane = fbi->par;
621         struct omapfb_device *fbdev = plane->fbdev;
622
623         omapfb_rqueue_lock(fbdev);
624         if (rotate != fbi->var.rotate) {
625                 struct fb_var_screeninfo *new_var = &fbdev->new_var;
626
627                 memcpy(new_var, &fbi->var, sizeof(*new_var));
628                 new_var->rotate = rotate;
629                 if (set_fb_var(fbi, new_var) == 0 &&
630                     memcmp(new_var, &fbi->var, sizeof(*new_var))) {
631                         memcpy(&fbi->var, new_var, sizeof(*new_var));
632                         ctrl_change_mode(fbi);
633                 }
634         }
635         omapfb_rqueue_unlock(fbdev);
636 }
637
638 /*
639  * Set new x,y offsets in the virtual display for the visible area and switch
640  * to the new mode.
641  */
642 static int omapfb_pan_display(struct fb_var_screeninfo *var,
643                                struct fb_info *fbi)
644 {
645         struct omapfb_plane_struct *plane = fbi->par;
646         struct omapfb_device *fbdev = plane->fbdev;
647         int r = 0;
648
649         omapfb_rqueue_lock(fbdev);
650         if (var->xoffset != fbi->var.xoffset ||
651             var->yoffset != fbi->var.yoffset) {
652                 struct fb_var_screeninfo *new_var = &fbdev->new_var;
653
654                 memcpy(new_var, &fbi->var, sizeof(*new_var));
655                 new_var->xoffset = var->xoffset;
656                 new_var->yoffset = var->yoffset;
657                 if (set_fb_var(fbi, new_var))
658                         r = -EINVAL;
659                 else {
660                         memcpy(&fbi->var, new_var, sizeof(*new_var));
661                         ctrl_change_mode(fbi);
662                 }
663         }
664         omapfb_rqueue_unlock(fbdev);
665
666         return r;
667 }
668
669 /* Set mirror to vertical axis and switch to the new mode. */
670 static int omapfb_mirror(struct fb_info *fbi, int mirror)
671 {
672         struct omapfb_plane_struct *plane = fbi->par;
673         struct omapfb_device *fbdev = plane->fbdev;
674         int r = 0;
675
676         omapfb_rqueue_lock(fbdev);
677         mirror = mirror ? 1 : 0;
678         if (cpu_is_omap15xx())
679                 r = -EINVAL;
680         else if (mirror != plane->info.mirror) {
681                 plane->info.mirror = mirror;
682                 r = ctrl_change_mode(fbi);
683         }
684         omapfb_rqueue_unlock(fbdev);
685
686         return r;
687 }
688
689 /*
690  * Check values in var, try to adjust them in case of out of bound values if
691  * possible, or return error.
692  */
693 static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
694 {
695         struct omapfb_plane_struct *plane = fbi->par;
696         struct omapfb_device *fbdev = plane->fbdev;
697         int r;
698
699         omapfb_rqueue_lock(fbdev);
700         if (fbdev->ctrl->sync != NULL)
701                 fbdev->ctrl->sync();
702         r = set_fb_var(fbi, var);
703         omapfb_rqueue_unlock(fbdev);
704
705         return r;
706 }
707
708 /*
709  * Switch to a new mode. The parameters for it has been check already by
710  * omapfb_check_var.
711  */
712 static int omapfb_set_par(struct fb_info *fbi)
713 {
714         struct omapfb_plane_struct *plane = fbi->par;
715         struct omapfb_device *fbdev = plane->fbdev;
716         int r = 0;
717
718         omapfb_rqueue_lock(fbdev);
719         set_fb_fix(fbi, 0);
720         r = ctrl_change_mode(fbi);
721         omapfb_rqueue_unlock(fbdev);
722
723         return r;
724 }
725
726 int omapfb_update_window_async(struct fb_info *fbi,
727                                 struct omapfb_update_window *win,
728                                 void (*callback)(void *),
729                                 void *callback_data)
730 {
731         int xres, yres;
732         struct omapfb_plane_struct *plane = fbi->par;
733         struct omapfb_device *fbdev = plane->fbdev;
734         struct fb_var_screeninfo *var = &fbi->var;
735
736         switch (var->rotate) {
737         case 0:
738         case 180:
739                 xres = fbdev->panel->x_res;
740                 yres = fbdev->panel->y_res;
741                 break;
742         case 90:
743         case 270:
744                 xres = fbdev->panel->y_res;
745                 yres = fbdev->panel->x_res;
746                 break;
747         default:
748                 return -EINVAL;
749         }
750
751         if (win->x >= xres || win->y >= yres ||
752             win->out_x > xres || win->out_y > yres)
753                 return -EINVAL;
754
755         if (!fbdev->ctrl->update_window ||
756             fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
757                 return -ENODEV;
758
759         if (win->x + win->width > xres)
760                 win->width = xres - win->x;
761         if (win->y + win->height > yres)
762                 win->height = yres - win->y;
763         if (win->out_x + win->out_width > xres)
764                 win->out_width = xres - win->out_x;
765         if (win->out_y + win->out_height > yres)
766                 win->out_height = yres - win->out_y;
767         if (!win->width || !win->height || !win->out_width || !win->out_height)
768                 return 0;
769
770         return fbdev->ctrl->update_window(fbi, win, callback, callback_data);
771 }
772 EXPORT_SYMBOL(omapfb_update_window_async);
773
774 static int omapfb_update_win(struct fb_info *fbi,
775                                 struct omapfb_update_window *win)
776 {
777         struct omapfb_plane_struct *plane = fbi->par;
778         int ret;
779
780         omapfb_rqueue_lock(plane->fbdev);
781         ret = omapfb_update_window_async(fbi, win, NULL, NULL);
782         omapfb_rqueue_unlock(plane->fbdev);
783
784         return ret;
785 }
786
787 static int omapfb_update_full_screen(struct fb_info *fbi)
788 {
789         struct omapfb_plane_struct *plane = fbi->par;
790         struct omapfb_device *fbdev = plane->fbdev;
791         struct omapfb_update_window win;
792         int r;
793
794         if (!fbdev->ctrl->update_window ||
795             fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
796                 return -ENODEV;
797
798         win.x = 0;
799         win.y = 0;
800         win.width = fbi->var.xres;
801         win.height = fbi->var.yres;
802         win.out_x = 0;
803         win.out_y = 0;
804         win.out_width = fbi->var.xres;
805         win.out_height = fbi->var.yres;
806         win.format = 0;
807
808         omapfb_rqueue_lock(fbdev);
809         r = fbdev->ctrl->update_window(fbi, &win, NULL, NULL);
810         omapfb_rqueue_unlock(fbdev);
811
812         return r;
813 }
814
815 static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
816 {
817         struct omapfb_plane_struct *plane = fbi->par;
818         struct omapfb_device *fbdev = plane->fbdev;
819         struct lcd_panel *panel = fbdev->panel;
820         struct omapfb_plane_info old_info;
821         int r = 0;
822
823         if (pi->pos_x + pi->out_width > panel->x_res ||
824             pi->pos_y + pi->out_height > panel->y_res)
825                 return -EINVAL;
826
827         omapfb_rqueue_lock(fbdev);
828         if (pi->enabled && !fbdev->mem_desc.region[plane->idx].size) {
829                 /*
830                  * This plane's memory was freed, can't enable it
831                  * until it's reallocated.
832                  */
833                 r = -EINVAL;
834                 goto out;
835         }
836         old_info = plane->info;
837         plane->info = *pi;
838         if (pi->enabled) {
839                 r = ctrl_change_mode(fbi);
840                 if (r < 0) {
841                         plane->info = old_info;
842                         goto out;
843                 }
844         }
845         r = fbdev->ctrl->enable_plane(plane->idx, pi->enabled);
846         if (r < 0) {
847                 plane->info = old_info;
848                 goto out;
849         }
850 out:
851         omapfb_rqueue_unlock(fbdev);
852         return r;
853 }
854
855 static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
856 {
857         struct omapfb_plane_struct *plane = fbi->par;
858
859         *pi = plane->info;
860         return 0;
861 }
862
863 static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
864 {
865         struct omapfb_plane_struct *plane = fbi->par;
866         struct omapfb_device *fbdev = plane->fbdev;
867         struct omapfb_mem_region *rg = &fbdev->mem_desc.region[plane->idx];
868         size_t size;
869         int r = 0;
870
871         if (fbdev->ctrl->setup_mem == NULL)
872                 return -ENODEV;
873         if (mi->type > OMAPFB_MEMTYPE_MAX)
874                 return -EINVAL;
875
876         size = PAGE_ALIGN(mi->size);
877         omapfb_rqueue_lock(fbdev);
878         if (plane->info.enabled) {
879                 r = -EBUSY;
880                 goto out;
881         }
882         if (rg->size != size || rg->type != mi->type) {
883                 struct fb_var_screeninfo *new_var = &fbdev->new_var;
884                 unsigned long old_size = rg->size;
885                 u8            old_type = rg->type;
886                 unsigned long paddr;
887
888                 rg->size = size;
889                 rg->type = mi->type;
890                 /*
891                  * size == 0 is a special case, for which we
892                  * don't check / adjust the screen parameters.
893                  * This isn't a problem since the plane can't
894                  * be reenabled unless its size is > 0.
895                  */
896                 if (old_size != size && size) {
897                         if (size) {
898                                 memcpy(new_var, &fbi->var, sizeof(*new_var));
899                                 r = set_fb_var(fbi, new_var);
900                                 if (r < 0)
901                                         goto out;
902                         }
903                 }
904
905                 if (fbdev->ctrl->sync)
906                         fbdev->ctrl->sync();
907                 r = fbdev->ctrl->setup_mem(plane->idx, size, mi->type, &paddr);
908                 if (r < 0) {
909                         /* Revert changes. */
910                         rg->size = old_size;
911                         rg->type = old_type;
912                         goto out;
913                 }
914                 rg->paddr = paddr;
915
916                 if (old_size != size) {
917                         if (size) {
918                                 memcpy(&fbi->var, new_var, sizeof(fbi->var));
919                                 set_fb_fix(fbi, 0);
920                         } else {
921                                 /*
922                                  * Set these explicitly to indicate that the
923                                  * plane memory is dealloce'd, the other
924                                  * screen parameters in var / fix are invalid.
925                                  */
926                                 mutex_lock(&fbi->mm_lock);
927                                 fbi->fix.smem_start = 0;
928                                 fbi->fix.smem_len = 0;
929                                 mutex_unlock(&fbi->mm_lock);
930                         }
931                 }
932         }
933 out:
934         omapfb_rqueue_unlock(fbdev);
935
936         return r;
937 }
938
939 static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
940 {
941         struct omapfb_plane_struct *plane = fbi->par;
942         struct omapfb_device *fbdev = plane->fbdev;
943         struct omapfb_mem_region *rg;
944
945         rg = &fbdev->mem_desc.region[plane->idx];
946         memset(mi, 0, sizeof(*mi));
947         mi->size = rg->size;
948         mi->type = rg->type;
949
950         return 0;
951 }
952
953 static int omapfb_set_color_key(struct omapfb_device *fbdev,
954                                 struct omapfb_color_key *ck)
955 {
956         int r;
957
958         if (!fbdev->ctrl->set_color_key)
959                 return -ENODEV;
960
961         omapfb_rqueue_lock(fbdev);
962         r = fbdev->ctrl->set_color_key(ck);
963         omapfb_rqueue_unlock(fbdev);
964
965         return r;
966 }
967
968 static int omapfb_get_color_key(struct omapfb_device *fbdev,
969                                 struct omapfb_color_key *ck)
970 {
971         int r;
972
973         if (!fbdev->ctrl->get_color_key)
974                 return -ENODEV;
975
976         omapfb_rqueue_lock(fbdev);
977         r = fbdev->ctrl->get_color_key(ck);
978         omapfb_rqueue_unlock(fbdev);
979
980         return r;
981 }
982
983 static struct blocking_notifier_head omapfb_client_list[OMAPFB_PLANE_NUM];
984 static int notifier_inited;
985
986 static void omapfb_init_notifier(void)
987 {
988         int i;
989
990         for (i = 0; i < OMAPFB_PLANE_NUM; i++)
991                 BLOCKING_INIT_NOTIFIER_HEAD(&omapfb_client_list[i]);
992 }
993
994 int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb,
995                                 omapfb_notifier_callback_t callback,
996                                 void *callback_data)
997 {
998         int r;
999
1000         if ((unsigned)omapfb_nb->plane_idx > OMAPFB_PLANE_NUM)
1001                 return -EINVAL;
1002
1003         if (!notifier_inited) {
1004                 omapfb_init_notifier();
1005                 notifier_inited = 1;
1006         }
1007
1008         omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *,
1009                                         unsigned long, void *))callback;
1010         omapfb_nb->data = callback_data;
1011         r = blocking_notifier_chain_register(
1012                                 &omapfb_client_list[omapfb_nb->plane_idx],
1013                                 &omapfb_nb->nb);
1014         if (r)
1015                 return r;
1016         if (omapfb_dev != NULL &&
1017             omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) {
1018                 omapfb_dev->ctrl->bind_client(omapfb_nb);
1019         }
1020
1021         return 0;
1022 }
1023 EXPORT_SYMBOL(omapfb_register_client);
1024
1025 int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb)
1026 {
1027         return blocking_notifier_chain_unregister(
1028                 &omapfb_client_list[omapfb_nb->plane_idx], &omapfb_nb->nb);
1029 }
1030 EXPORT_SYMBOL(omapfb_unregister_client);
1031
1032 void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event)
1033 {
1034         int i;
1035
1036         if (!notifier_inited)
1037                 /* no client registered yet */
1038                 return;
1039
1040         for (i = 0; i < OMAPFB_PLANE_NUM; i++)
1041                 blocking_notifier_call_chain(&omapfb_client_list[i], event,
1042                                     fbdev->fb_info[i]);
1043 }
1044 EXPORT_SYMBOL(omapfb_notify_clients);
1045
1046 static int omapfb_set_update_mode(struct omapfb_device *fbdev,
1047                                    enum omapfb_update_mode mode)
1048 {
1049         int r;
1050
1051         omapfb_rqueue_lock(fbdev);
1052         r = fbdev->ctrl->set_update_mode(mode);
1053         omapfb_rqueue_unlock(fbdev);
1054
1055         return r;
1056 }
1057
1058 static enum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev)
1059 {
1060         int r;
1061
1062         omapfb_rqueue_lock(fbdev);
1063         r = fbdev->ctrl->get_update_mode();
1064         omapfb_rqueue_unlock(fbdev);
1065
1066         return r;
1067 }
1068
1069 static void omapfb_get_caps(struct omapfb_device *fbdev, int plane,
1070                                      struct omapfb_caps *caps)
1071 {
1072         memset(caps, 0, sizeof(*caps));
1073         fbdev->ctrl->get_caps(plane, caps);
1074         caps->ctrl |= fbdev->panel->get_caps(fbdev->panel);
1075 }
1076
1077 /* For lcd testing */
1078 void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval)
1079 {
1080         omapfb_rqueue_lock(fbdev);
1081         *(u16 *)fbdev->mem_desc.region[0].vaddr = pixval;
1082         if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) {
1083                 struct omapfb_update_window win;
1084
1085                 memset(&win, 0, sizeof(win));
1086                 win.width = 2;
1087                 win.height = 2;
1088                 win.out_width = 2;
1089                 win.out_height = 2;
1090                 fbdev->ctrl->update_window(fbdev->fb_info[0], &win, NULL, NULL);
1091         }
1092         omapfb_rqueue_unlock(fbdev);
1093 }
1094 EXPORT_SYMBOL(omapfb_write_first_pixel);
1095
1096 /*
1097  * Ioctl interface. Part of the kernel mode frame buffer API is duplicated
1098  * here to be accessible by user mode code.
1099  */
1100 static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd,
1101                         unsigned long arg)
1102 {
1103         struct omapfb_plane_struct *plane = fbi->par;
1104         struct omapfb_device    *fbdev = plane->fbdev;
1105         struct fb_ops           *ops = fbi->fbops;
1106         union {
1107                 struct omapfb_update_window     update_window;
1108                 struct omapfb_plane_info        plane_info;
1109                 struct omapfb_mem_info          mem_info;
1110                 struct omapfb_color_key         color_key;
1111                 enum omapfb_update_mode         update_mode;
1112                 struct omapfb_caps              caps;
1113                 unsigned int            mirror;
1114                 int                     plane_out;
1115                 int                     enable_plane;
1116         } p;
1117         int r = 0;
1118
1119         BUG_ON(!ops);
1120         switch (cmd) {
1121         case OMAPFB_MIRROR:
1122                 if (get_user(p.mirror, (int __user *)arg))
1123                         r = -EFAULT;
1124                 else
1125                         omapfb_mirror(fbi, p.mirror);
1126                 break;
1127         case OMAPFB_SYNC_GFX:
1128                 omapfb_sync(fbi);
1129                 break;
1130         case OMAPFB_VSYNC:
1131                 break;
1132         case OMAPFB_SET_UPDATE_MODE:
1133                 if (get_user(p.update_mode, (int __user *)arg))
1134                         r = -EFAULT;
1135                 else
1136                         r = omapfb_set_update_mode(fbdev, p.update_mode);
1137                 break;
1138         case OMAPFB_GET_UPDATE_MODE:
1139                 p.update_mode = omapfb_get_update_mode(fbdev);
1140                 if (put_user(p.update_mode,
1141                                         (enum omapfb_update_mode __user *)arg))
1142                         r = -EFAULT;
1143                 break;
1144         case OMAPFB_UPDATE_WINDOW_OLD:
1145                 if (copy_from_user(&p.update_window, (void __user *)arg,
1146                                    sizeof(struct omapfb_update_window_old)))
1147                         r = -EFAULT;
1148                 else {
1149                         struct omapfb_update_window *u = &p.update_window;
1150                         u->out_x = u->x;
1151                         u->out_y = u->y;
1152                         u->out_width = u->width;
1153                         u->out_height = u->height;
1154                         memset(u->reserved, 0, sizeof(u->reserved));
1155                         r = omapfb_update_win(fbi, u);
1156                 }
1157                 break;
1158         case OMAPFB_UPDATE_WINDOW:
1159                 if (copy_from_user(&p.update_window, (void __user *)arg,
1160                                    sizeof(p.update_window)))
1161                         r = -EFAULT;
1162                 else
1163                         r = omapfb_update_win(fbi, &p.update_window);
1164                 break;
1165         case OMAPFB_SETUP_PLANE:
1166                 if (copy_from_user(&p.plane_info, (void __user *)arg,
1167                                    sizeof(p.plane_info)))
1168                         r = -EFAULT;
1169                 else
1170                         r = omapfb_setup_plane(fbi, &p.plane_info);
1171                 break;
1172         case OMAPFB_QUERY_PLANE:
1173                 if ((r = omapfb_query_plane(fbi, &p.plane_info)) < 0)
1174                         break;
1175                 if (copy_to_user((void __user *)arg, &p.plane_info,
1176                                    sizeof(p.plane_info)))
1177                         r = -EFAULT;
1178                 break;
1179         case OMAPFB_SETUP_MEM:
1180                 if (copy_from_user(&p.mem_info, (void __user *)arg,
1181                                    sizeof(p.mem_info)))
1182                         r = -EFAULT;
1183                 else
1184                         r = omapfb_setup_mem(fbi, &p.mem_info);
1185                 break;
1186         case OMAPFB_QUERY_MEM:
1187                 if ((r = omapfb_query_mem(fbi, &p.mem_info)) < 0)
1188                         break;
1189                 if (copy_to_user((void __user *)arg, &p.mem_info,
1190                                    sizeof(p.mem_info)))
1191                         r = -EFAULT;
1192                 break;
1193         case OMAPFB_SET_COLOR_KEY:
1194                 if (copy_from_user(&p.color_key, (void __user *)arg,
1195                                    sizeof(p.color_key)))
1196                         r = -EFAULT;
1197                 else
1198                         r = omapfb_set_color_key(fbdev, &p.color_key);
1199                 break;
1200         case OMAPFB_GET_COLOR_KEY:
1201                 if ((r = omapfb_get_color_key(fbdev, &p.color_key)) < 0)
1202                         break;
1203                 if (copy_to_user((void __user *)arg, &p.color_key,
1204                                  sizeof(p.color_key)))
1205                         r = -EFAULT;
1206                 break;
1207         case OMAPFB_GET_CAPS:
1208                 omapfb_get_caps(fbdev, plane->idx, &p.caps);
1209                 if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
1210                         r = -EFAULT;
1211                 break;
1212         case OMAPFB_LCD_TEST:
1213                 {
1214                         int test_num;
1215
1216                         if (get_user(test_num, (int __user *)arg)) {
1217                                 r = -EFAULT;
1218                                 break;
1219                         }
1220                         if (!fbdev->panel->run_test) {
1221                                 r = -EINVAL;
1222                                 break;
1223                         }
1224                         r = fbdev->panel->run_test(fbdev->panel, test_num);
1225                         break;
1226                 }
1227         case OMAPFB_CTRL_TEST:
1228                 {
1229                         int test_num;
1230
1231                         if (get_user(test_num, (int __user *)arg)) {
1232                                 r = -EFAULT;
1233                                 break;
1234                         }
1235                         if (!fbdev->ctrl->run_test) {
1236                                 r = -EINVAL;
1237                                 break;
1238                         }
1239                         r = fbdev->ctrl->run_test(test_num);
1240                         break;
1241                 }
1242         default:
1243                 r = -EINVAL;
1244         }
1245
1246         return r;
1247 }
1248
1249 static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
1250 {
1251         struct omapfb_plane_struct *plane = info->par;
1252         struct omapfb_device *fbdev = plane->fbdev;
1253         int r;
1254
1255         omapfb_rqueue_lock(fbdev);
1256         r = fbdev->ctrl->mmap(info, vma);
1257         omapfb_rqueue_unlock(fbdev);
1258
1259         return r;
1260 }
1261
1262 /*
1263  * Callback table for the frame buffer framework. Some of these pointers
1264  * will be changed according to the current setting of fb_info->accel_flags.
1265  */
1266 static struct fb_ops omapfb_ops = {
1267         .owner          = THIS_MODULE,
1268         .fb_open        = omapfb_open,
1269         .fb_release     = omapfb_release,
1270         .fb_setcolreg   = omapfb_setcolreg,
1271         .fb_setcmap     = omapfb_setcmap,
1272         .fb_fillrect    = cfb_fillrect,
1273         .fb_copyarea    = cfb_copyarea,
1274         .fb_imageblit   = cfb_imageblit,
1275         .fb_blank       = omapfb_blank,
1276         .fb_ioctl       = omapfb_ioctl,
1277         .fb_check_var   = omapfb_check_var,
1278         .fb_set_par     = omapfb_set_par,
1279         .fb_rotate      = omapfb_rotate,
1280         .fb_pan_display = omapfb_pan_display,
1281 };
1282
1283 /*
1284  * ---------------------------------------------------------------------------
1285  * Sysfs interface
1286  * ---------------------------------------------------------------------------
1287  */
1288 /* omapfbX sysfs entries */
1289 static ssize_t omapfb_show_caps_num(struct device *dev,
1290                                     struct device_attribute *attr, char *buf)
1291 {
1292         struct omapfb_device *fbdev = dev_get_drvdata(dev);
1293         int plane;
1294         size_t size;
1295         struct omapfb_caps caps;
1296
1297         plane = 0;
1298         size = 0;
1299         while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) {
1300                 omapfb_get_caps(fbdev, plane, &caps);
1301                 size += snprintf(&buf[size], PAGE_SIZE - size,
1302                         "plane#%d %#010x %#010x %#010x\n",
1303                         plane, caps.ctrl, caps.plane_color, caps.wnd_color);
1304                 plane++;
1305         }
1306         return size;
1307 }
1308
1309 static ssize_t omapfb_show_caps_text(struct device *dev,
1310                                      struct device_attribute *attr, char *buf)
1311 {
1312         struct omapfb_device *fbdev = dev_get_drvdata(dev);
1313         int i;
1314         struct omapfb_caps caps;
1315         int plane;
1316         size_t size;
1317
1318         plane = 0;
1319         size = 0;
1320         while (size < PAGE_SIZE && plane < OMAPFB_PLANE_NUM) {
1321                 omapfb_get_caps(fbdev, plane, &caps);
1322                 size += snprintf(&buf[size], PAGE_SIZE - size,
1323                                  "plane#%d:\n", plane);
1324                 for (i = 0; i < ARRAY_SIZE(ctrl_caps) &&
1325                      size < PAGE_SIZE; i++) {
1326                         if (ctrl_caps[i].flag & caps.ctrl)
1327                                 size += snprintf(&buf[size], PAGE_SIZE - size,
1328                                         " %s\n", ctrl_caps[i].name);
1329                 }
1330                 size += snprintf(&buf[size], PAGE_SIZE - size,
1331                                  " plane colors:\n");
1332                 for (i = 0; i < ARRAY_SIZE(color_caps) &&
1333                      size < PAGE_SIZE; i++) {
1334                         if (color_caps[i].flag & caps.plane_color)
1335                                 size += snprintf(&buf[size], PAGE_SIZE - size,
1336                                         "  %s\n", color_caps[i].name);
1337                 }
1338                 size += snprintf(&buf[size], PAGE_SIZE - size,
1339                                  " window colors:\n");
1340                 for (i = 0; i < ARRAY_SIZE(color_caps) &&
1341                      size < PAGE_SIZE; i++) {
1342                         if (color_caps[i].flag & caps.wnd_color)
1343                                 size += snprintf(&buf[size], PAGE_SIZE - size,
1344                                         "  %s\n", color_caps[i].name);
1345                 }
1346
1347                 plane++;
1348         }
1349         return size;
1350 }
1351
1352 static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL);
1353 static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL);
1354
1355 /* panel sysfs entries */
1356 static ssize_t omapfb_show_panel_name(struct device *dev,
1357                                       struct device_attribute *attr, char *buf)
1358 {
1359         struct omapfb_device *fbdev = dev_get_drvdata(dev);
1360
1361         return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->panel->name);
1362 }
1363
1364 static ssize_t omapfb_show_bklight_level(struct device *dev,
1365                                          struct device_attribute *attr,
1366                                          char *buf)
1367 {
1368         struct omapfb_device *fbdev = dev_get_drvdata(dev);
1369         int r;
1370
1371         if (fbdev->panel->get_bklight_level) {
1372                 r = snprintf(buf, PAGE_SIZE, "%d\n",
1373                              fbdev->panel->get_bklight_level(fbdev->panel));
1374         } else
1375                 r = -ENODEV;
1376         return r;
1377 }
1378
1379 static ssize_t omapfb_store_bklight_level(struct device *dev,
1380                                           struct device_attribute *attr,
1381                                           const char *buf, size_t size)
1382 {
1383         struct omapfb_device *fbdev = dev_get_drvdata(dev);
1384         int r;
1385
1386         if (fbdev->panel->set_bklight_level) {
1387                 unsigned int level;
1388
1389                 if (sscanf(buf, "%10d", &level) == 1) {
1390                         r = fbdev->panel->set_bklight_level(fbdev->panel,
1391                                                             level);
1392                 } else
1393                         r = -EINVAL;
1394         } else
1395                 r = -ENODEV;
1396         return r ? r : size;
1397 }
1398
1399 static ssize_t omapfb_show_bklight_max(struct device *dev,
1400                                        struct device_attribute *attr, char *buf)
1401 {
1402         struct omapfb_device *fbdev = dev_get_drvdata(dev);
1403         int r;
1404
1405         if (fbdev->panel->get_bklight_level) {
1406                 r = snprintf(buf, PAGE_SIZE, "%d\n",
1407                              fbdev->panel->get_bklight_max(fbdev->panel));
1408         } else
1409                 r = -ENODEV;
1410         return r;
1411 }
1412
1413 static struct device_attribute dev_attr_panel_name =
1414         __ATTR(name, 0444, omapfb_show_panel_name, NULL);
1415 static DEVICE_ATTR(backlight_level, 0664,
1416                    omapfb_show_bklight_level, omapfb_store_bklight_level);
1417 static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL);
1418
1419 static struct attribute *panel_attrs[] = {
1420         &dev_attr_panel_name.attr,
1421         &dev_attr_backlight_level.attr,
1422         &dev_attr_backlight_max.attr,
1423         NULL,
1424 };
1425
1426 static struct attribute_group panel_attr_grp = {
1427         .name  = "panel",
1428         .attrs = panel_attrs,
1429 };
1430
1431 /* ctrl sysfs entries */
1432 static ssize_t omapfb_show_ctrl_name(struct device *dev,
1433                                      struct device_attribute *attr, char *buf)
1434 {
1435         struct omapfb_device *fbdev = dev_get_drvdata(dev);
1436
1437         return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->ctrl->name);
1438 }
1439
1440 static struct device_attribute dev_attr_ctrl_name =
1441         __ATTR(name, 0444, omapfb_show_ctrl_name, NULL);
1442
1443 static struct attribute *ctrl_attrs[] = {
1444         &dev_attr_ctrl_name.attr,
1445         NULL,
1446 };
1447
1448 static struct attribute_group ctrl_attr_grp = {
1449         .name  = "ctrl",
1450         .attrs = ctrl_attrs,
1451 };
1452
1453 static int omapfb_register_sysfs(struct omapfb_device *fbdev)
1454 {
1455         int r;
1456
1457         if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num)))
1458                 goto fail0;
1459
1460         if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text)))
1461                 goto fail1;
1462
1463         if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp)))
1464                 goto fail2;
1465
1466         if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp)))
1467                 goto fail3;
1468
1469         return 0;
1470 fail3:
1471         sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
1472 fail2:
1473         device_remove_file(fbdev->dev, &dev_attr_caps_text);
1474 fail1:
1475         device_remove_file(fbdev->dev, &dev_attr_caps_num);
1476 fail0:
1477         dev_err(fbdev->dev, "unable to register sysfs interface\n");
1478         return r;
1479 }
1480
1481 static void omapfb_unregister_sysfs(struct omapfb_device *fbdev)
1482 {
1483         sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp);
1484         sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);
1485         device_remove_file(fbdev->dev, &dev_attr_caps_num);
1486         device_remove_file(fbdev->dev, &dev_attr_caps_text);
1487 }
1488
1489 /*
1490  * ---------------------------------------------------------------------------
1491  * LDM callbacks
1492  * ---------------------------------------------------------------------------
1493  */
1494 /* Initialize system fb_info object and set the default video mode.
1495  * The frame buffer memory already allocated by lcddma_init
1496  */
1497 static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info)
1498 {
1499         struct fb_var_screeninfo        *var = &info->var;
1500         struct fb_fix_screeninfo        *fix = &info->fix;
1501         int                             r = 0;
1502
1503         info->fbops = &omapfb_ops;
1504         info->flags = FBINFO_FLAG_DEFAULT;
1505
1506         strncpy(fix->id, MODULE_NAME, sizeof(fix->id));
1507
1508         info->pseudo_palette = fbdev->pseudo_palette;
1509
1510         var->accel_flags  = def_accel ? FB_ACCELF_TEXT : 0;
1511         var->xres = def_vxres;
1512         var->yres = def_vyres;
1513         var->xres_virtual = def_vxres;
1514         var->yres_virtual = def_vyres;
1515         var->rotate       = def_rotate;
1516         var->bits_per_pixel = fbdev->panel->bpp;
1517
1518         set_fb_var(info, var);
1519         set_fb_fix(info, 1);
1520
1521         r = fb_alloc_cmap(&info->cmap, 16, 0);
1522         if (r != 0)
1523                 dev_err(fbdev->dev, "unable to allocate color map memory\n");
1524
1525         return r;
1526 }
1527
1528 /* Release the fb_info object */
1529 static void fbinfo_cleanup(struct omapfb_device *fbdev, struct fb_info *fbi)
1530 {
1531         fb_dealloc_cmap(&fbi->cmap);
1532 }
1533
1534 static void planes_cleanup(struct omapfb_device *fbdev)
1535 {
1536         int i;
1537
1538         for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
1539                 if (fbdev->fb_info[i] == NULL)
1540                         break;
1541                 fbinfo_cleanup(fbdev, fbdev->fb_info[i]);
1542                 framebuffer_release(fbdev->fb_info[i]);
1543         }
1544 }
1545
1546 static int planes_init(struct omapfb_device *fbdev)
1547 {
1548         struct fb_info *fbi;
1549         int i;
1550         int r;
1551
1552         for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
1553                 struct omapfb_plane_struct *plane;
1554                 fbi = framebuffer_alloc(sizeof(struct omapfb_plane_struct),
1555                                         fbdev->dev);
1556                 if (fbi == NULL) {
1557                         dev_err(fbdev->dev,
1558                                 "unable to allocate memory for plane info\n");
1559                         planes_cleanup(fbdev);
1560                         return -ENOMEM;
1561                 }
1562                 plane = fbi->par;
1563                 plane->idx = i;
1564                 plane->fbdev = fbdev;
1565                 plane->info.mirror = def_mirror;
1566                 fbdev->fb_info[i] = fbi;
1567
1568                 if ((r = fbinfo_init(fbdev, fbi)) < 0) {
1569                         framebuffer_release(fbi);
1570                         planes_cleanup(fbdev);
1571                         return r;
1572                 }
1573                 plane->info.out_width = fbi->var.xres;
1574                 plane->info.out_height = fbi->var.yres;
1575         }
1576         return 0;
1577 }
1578
1579 /*
1580  * Free driver resources. Can be called to rollback an aborted initialization
1581  * sequence.
1582  */
1583 static void omapfb_free_resources(struct omapfb_device *fbdev, int state)
1584 {
1585         int i;
1586
1587         switch (state) {
1588         case OMAPFB_ACTIVE:
1589                 for (i = 0; i < fbdev->mem_desc.region_cnt; i++)
1590                         unregister_framebuffer(fbdev->fb_info[i]);
1591         case 7:
1592                 omapfb_unregister_sysfs(fbdev);
1593         case 6:
1594                 fbdev->panel->disable(fbdev->panel);
1595         case 5:
1596                 omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED);
1597         case 4:
1598                 planes_cleanup(fbdev);
1599         case 3:
1600                 ctrl_cleanup(fbdev);
1601         case 2:
1602                 fbdev->panel->cleanup(fbdev->panel);
1603         case 1:
1604                 dev_set_drvdata(fbdev->dev, NULL);
1605                 kfree(fbdev);
1606         case 0:
1607                 /* nothing to free */
1608                 break;
1609         default:
1610                 BUG();
1611         }
1612 }
1613
1614 static int omapfb_find_ctrl(struct omapfb_device *fbdev)
1615 {
1616         struct omapfb_platform_data *conf;
1617         char name[17];
1618         int i;
1619
1620         conf = fbdev->dev->platform_data;
1621
1622         fbdev->ctrl = NULL;
1623
1624         strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1);
1625         name[sizeof(name) - 1] = '\0';
1626
1627         if (strcmp(name, "internal") == 0) {
1628                 fbdev->ctrl = fbdev->int_ctrl;
1629                 return 0;
1630         }
1631
1632         for (i = 0; i < ARRAY_SIZE(ctrls); i++) {
1633                 dev_dbg(fbdev->dev, "ctrl %s\n", ctrls[i]->name);
1634                 if (strcmp(ctrls[i]->name, name) == 0) {
1635                         fbdev->ctrl = ctrls[i];
1636                         break;
1637                 }
1638         }
1639
1640         if (fbdev->ctrl == NULL) {
1641                 dev_dbg(fbdev->dev, "ctrl %s not supported\n", name);
1642                 return -1;
1643         }
1644
1645         return 0;
1646 }
1647
1648 static void check_required_callbacks(struct omapfb_device *fbdev)
1649 {
1650 #define _C(x) (fbdev->ctrl->x != NULL)
1651 #define _P(x) (fbdev->panel->x != NULL)
1652         BUG_ON(fbdev->ctrl == NULL || fbdev->panel == NULL);
1653         BUG_ON(!(_C(init) && _C(cleanup) && _C(get_caps) &&
1654                  _C(set_update_mode) && _C(setup_plane) && _C(enable_plane) &&
1655                  _P(init) && _P(cleanup) && _P(enable) && _P(disable) &&
1656                  _P(get_caps)));
1657 #undef _P
1658 #undef _C
1659 }
1660
1661 /*
1662  * Called by LDM binding to probe and attach a new device.
1663  * Initialization sequence:
1664  *   1. allocate system omapfb_device structure
1665  *   2. select controller type according to platform configuration
1666  *      init LCD panel
1667  *   3. init LCD controller and LCD DMA
1668  *   4. init system fb_info structure for all planes
1669  *   5. setup video mode for first plane and enable it
1670  *   6. enable LCD panel
1671  *   7. register sysfs attributes
1672  *   OMAPFB_ACTIVE: register system fb_info structure for all planes
1673  */
1674 static int omapfb_do_probe(struct platform_device *pdev,
1675                                 struct lcd_panel *panel)
1676 {
1677         struct omapfb_device    *fbdev = NULL;
1678         int                     init_state;
1679         unsigned long           phz, hhz, vhz;
1680         unsigned long           vram;
1681         int                     i;
1682         int                     r = 0;
1683
1684         init_state = 0;
1685
1686         if (pdev->num_resources != 0) {
1687                 dev_err(&pdev->dev, "probed for an unknown device\n");
1688                 r = -ENODEV;
1689                 goto cleanup;
1690         }
1691
1692         if (pdev->dev.platform_data == NULL) {
1693                 dev_err(&pdev->dev, "missing platform data\n");
1694                 r = -ENOENT;
1695                 goto cleanup;
1696         }
1697
1698         fbdev = kzalloc(sizeof(struct omapfb_device), GFP_KERNEL);
1699         if (fbdev == NULL) {
1700                 dev_err(&pdev->dev,
1701                         "unable to allocate memory for device info\n");
1702                 r = -ENOMEM;
1703                 goto cleanup;
1704         }
1705         init_state++;
1706
1707         fbdev->dev = &pdev->dev;
1708         fbdev->panel = panel;
1709         fbdev->dssdev = &omapdss_device;
1710         platform_set_drvdata(pdev, fbdev);
1711
1712         mutex_init(&fbdev->rqueue_mutex);
1713
1714 #ifdef CONFIG_ARCH_OMAP1
1715         fbdev->int_ctrl = &omap1_int_ctrl;
1716 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
1717         fbdev->ext_if = &omap1_ext_if;
1718 #endif
1719 #else   /* OMAP2 */
1720         fbdev->int_ctrl = &omap2_int_ctrl;
1721 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
1722         fbdev->ext_if = &omap2_ext_if;
1723 #endif
1724 #endif
1725         if (omapfb_find_ctrl(fbdev) < 0) {
1726                 dev_err(fbdev->dev,
1727                         "LCD controller not found, board not supported\n");
1728                 r = -ENODEV;
1729                 goto cleanup;
1730         }
1731
1732         r = fbdev->panel->init(fbdev->panel, fbdev);
1733         if (r)
1734                 goto cleanup;
1735
1736         pr_info("omapfb: configured for panel %s\n", fbdev->panel->name);
1737
1738         def_vxres = def_vxres ? def_vxres : fbdev->panel->x_res;
1739         def_vyres = def_vyres ? def_vyres : fbdev->panel->y_res;
1740
1741         init_state++;
1742
1743         r = ctrl_init(fbdev);
1744         if (r)
1745                 goto cleanup;
1746         if (fbdev->ctrl->mmap != NULL)
1747                 omapfb_ops.fb_mmap = omapfb_mmap;
1748         init_state++;
1749
1750         check_required_callbacks(fbdev);
1751
1752         r = planes_init(fbdev);
1753         if (r)
1754                 goto cleanup;
1755         init_state++;
1756
1757 #ifdef CONFIG_FB_OMAP_DMA_TUNE
1758         /* Set DMA priority for EMIFF access to highest */
1759         if (cpu_class_is_omap1())
1760                 omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15);
1761 #endif
1762
1763         r = ctrl_change_mode(fbdev->fb_info[0]);
1764         if (r) {
1765                 dev_err(fbdev->dev, "mode setting failed\n");
1766                 goto cleanup;
1767         }
1768
1769         /* GFX plane is enabled by default */
1770         r = fbdev->ctrl->enable_plane(OMAPFB_PLANE_GFX, 1);
1771         if (r)
1772                 goto cleanup;
1773
1774         omapfb_set_update_mode(fbdev, manual_update ?
1775                                    OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE);
1776         init_state++;
1777
1778         r = fbdev->panel->enable(fbdev->panel);
1779         if (r)
1780                 goto cleanup;
1781         init_state++;
1782
1783         r = omapfb_register_sysfs(fbdev);
1784         if (r)
1785                 goto cleanup;
1786         init_state++;
1787
1788         vram = 0;
1789         for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {
1790                 r = register_framebuffer(fbdev->fb_info[i]);
1791                 if (r != 0) {
1792                         dev_err(fbdev->dev,
1793                                 "registering framebuffer %d failed\n", i);
1794                         goto cleanup;
1795                 }
1796                 vram += fbdev->mem_desc.region[i].size;
1797         }
1798
1799         fbdev->state = OMAPFB_ACTIVE;
1800
1801         panel = fbdev->panel;
1802         phz = panel->pixel_clock * 1000;
1803         hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw);
1804         vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw);
1805
1806         omapfb_dev = fbdev;
1807
1808         pr_info("omapfb: Framebuffer initialized. Total vram %lu planes %d\n",
1809                         vram, fbdev->mem_desc.region_cnt);
1810         pr_info("omapfb: Pixclock %lu kHz hfreq %lu.%lu kHz "
1811                         "vfreq %lu.%lu Hz\n",
1812                         phz / 1000, hhz / 10000, hhz % 10, vhz / 10, vhz % 10);
1813
1814         return 0;
1815
1816 cleanup:
1817         omapfb_free_resources(fbdev, init_state);
1818
1819         return r;
1820 }
1821
1822 static int omapfb_probe(struct platform_device *pdev)
1823 {
1824         int r;
1825
1826         BUG_ON(fbdev_pdev != NULL);
1827
1828         r = platform_device_register(&omapdss_device);
1829         if (r) {
1830                 dev_err(&pdev->dev, "can't register omapdss device\n");
1831                 return r;
1832         }
1833
1834         /* Delay actual initialization until the LCD is registered */
1835         fbdev_pdev = pdev;
1836         if (fbdev_panel != NULL)
1837                 omapfb_do_probe(fbdev_pdev, fbdev_panel);
1838         return 0;
1839 }
1840
1841 void omapfb_register_panel(struct lcd_panel *panel)
1842 {
1843         BUG_ON(fbdev_panel != NULL);
1844
1845         fbdev_panel = panel;
1846         if (fbdev_pdev != NULL)
1847                 omapfb_do_probe(fbdev_pdev, fbdev_panel);
1848 }
1849
1850 /* Called when the device is being detached from the driver */
1851 static int omapfb_remove(struct platform_device *pdev)
1852 {
1853         struct omapfb_device *fbdev = platform_get_drvdata(pdev);
1854         enum omapfb_state saved_state = fbdev->state;
1855
1856         /* FIXME: wait till completion of pending events */
1857
1858         fbdev->state = OMAPFB_DISABLED;
1859         omapfb_free_resources(fbdev, saved_state);
1860
1861         platform_device_unregister(&omapdss_device);
1862         fbdev->dssdev = NULL;
1863
1864         return 0;
1865 }
1866
1867 /* PM suspend */
1868 static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg)
1869 {
1870         struct omapfb_device *fbdev = platform_get_drvdata(pdev);
1871
1872         if (fbdev != NULL)
1873                 omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]);
1874         return 0;
1875 }
1876
1877 /* PM resume */
1878 static int omapfb_resume(struct platform_device *pdev)
1879 {
1880         struct omapfb_device *fbdev = platform_get_drvdata(pdev);
1881
1882         if (fbdev != NULL)
1883                 omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]);
1884         return 0;
1885 }
1886
1887 static struct platform_driver omapfb_driver = {
1888         .probe          = omapfb_probe,
1889         .remove         = omapfb_remove,
1890         .suspend        = omapfb_suspend,
1891         .resume         = omapfb_resume,
1892         .driver         = {
1893                 .name   = MODULE_NAME,
1894                 .owner  = THIS_MODULE,
1895         },
1896 };
1897
1898 #ifndef MODULE
1899
1900 /* Process kernel command line parameters */
1901 static int __init omapfb_setup(char *options)
1902 {
1903         char *this_opt = NULL;
1904         int r = 0;
1905
1906         pr_debug("omapfb: options %s\n", options);
1907
1908         if (!options || !*options)
1909                 return 0;
1910
1911         while (!r && (this_opt = strsep(&options, ",")) != NULL) {
1912                 if (!strncmp(this_opt, "accel", 5))
1913                         def_accel = 1;
1914                 else if (!strncmp(this_opt, "vram:", 5)) {
1915                         char *suffix;
1916                         unsigned long vram;
1917                         vram = (simple_strtoul(this_opt + 5, &suffix, 0));
1918                         switch (suffix[0]) {
1919                         case '\0':
1920                                 break;
1921                         case 'm':
1922                         case 'M':
1923                                 vram *= 1024;
1924                                 /* Fall through */
1925                         case 'k':
1926                         case 'K':
1927                                 vram *= 1024;
1928                                 break;
1929                         default:
1930                                 pr_debug("omapfb: invalid vram suffix %c\n",
1931                                          suffix[0]);
1932                                 r = -1;
1933                         }
1934                         def_vram[def_vram_cnt++] = vram;
1935                 }
1936                 else if (!strncmp(this_opt, "vxres:", 6))
1937                         def_vxres = simple_strtoul(this_opt + 6, NULL, 0);
1938                 else if (!strncmp(this_opt, "vyres:", 6))
1939                         def_vyres = simple_strtoul(this_opt + 6, NULL, 0);
1940                 else if (!strncmp(this_opt, "rotate:", 7))
1941                         def_rotate = (simple_strtoul(this_opt + 7, NULL, 0));
1942                 else if (!strncmp(this_opt, "mirror:", 7))
1943                         def_mirror = (simple_strtoul(this_opt + 7, NULL, 0));
1944                 else if (!strncmp(this_opt, "manual_update", 13))
1945                         manual_update = 1;
1946                 else {
1947                         pr_debug("omapfb: invalid option\n");
1948                         r = -1;
1949                 }
1950         }
1951
1952         return r;
1953 }
1954
1955 #endif
1956
1957 /* Register both the driver and the device */
1958 static int __init omapfb_init(void)
1959 {
1960 #ifndef MODULE
1961         char *option;
1962
1963         if (fb_get_options("omapfb", &option))
1964                 return -ENODEV;
1965         omapfb_setup(option);
1966 #endif
1967         /* Register the driver with LDM */
1968         if (platform_driver_register(&omapfb_driver)) {
1969                 pr_debug("failed to register omapfb driver\n");
1970                 return -ENODEV;
1971         }
1972
1973         return 0;
1974 }
1975
1976 static void __exit omapfb_cleanup(void)
1977 {
1978         platform_driver_unregister(&omapfb_driver);
1979 }
1980
1981 module_param_named(accel, def_accel, uint, 0664);
1982 module_param_array_named(vram, def_vram, ulong, &def_vram_cnt, 0664);
1983 module_param_named(vxres, def_vxres, long, 0664);
1984 module_param_named(vyres, def_vyres, long, 0664);
1985 module_param_named(rotate, def_rotate, uint, 0664);
1986 module_param_named(mirror, def_mirror, uint, 0664);
1987 module_param_named(manual_update, manual_update, bool, 0664);
1988
1989 module_init(omapfb_init);
1990 module_exit(omapfb_cleanup);
1991
1992 MODULE_DESCRIPTION("TI OMAP framebuffer driver");
1993 MODULE_AUTHOR("Imre Deak <imre.deak@nokia.com>");
1994 MODULE_LICENSE("GPL");