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