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