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