aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/mmp/fb
diff options
context:
space:
mode:
authorZhou Zhu <zzhu3@marvell.com>2013-02-21 19:42:12 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 20:22:17 -0500
commitd2e8bae6d72fa139fe7da7739c30b81c4122866f (patch)
treeae19e0df48fc55cfd4a80226104735172b55c775 /drivers/video/mmp/fb
parent59393bb94c103fca48c29348d2415cc67d772045 (diff)
video: mmp fb support
Add fb support for Marvell mmp display subsystem. This driver is configured using "buffer driver mach info". With configured name of path, this driver get path using using exported interface of mmp display driver. Then this driver get overlay using configured id and operates on this overlay to show buffers on display devices. Signed-off-by: Zhou Zhu <zzhu3@marvell.com> Signed-off-by: Lisa Du <cldu@marvell.com> Cc: Guoqing Li <ligq@marvell.com> Acked-by: Haojian Zhuang <haojian.zhuang@gmail.com> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/mmp/fb')
-rw-r--r--drivers/video/mmp/fb/Kconfig13
-rw-r--r--drivers/video/mmp/fb/Makefile1
-rw-r--r--drivers/video/mmp/fb/mmpfb.c684
-rw-r--r--drivers/video/mmp/fb/mmpfb.h54
4 files changed, 752 insertions, 0 deletions
diff --git a/drivers/video/mmp/fb/Kconfig b/drivers/video/mmp/fb/Kconfig
new file mode 100644
index 000000000000..9b0141f105f5
--- /dev/null
+++ b/drivers/video/mmp/fb/Kconfig
@@ -0,0 +1,13 @@
1if MMP_DISP
2
3config MMP_FB
4 bool "fb driver for Marvell MMP Display Subsystem"
5 depends on FB
6 select FB_CFB_FILLRECT
7 select FB_CFB_COPYAREA
8 select FB_CFB_IMAGEBLIT
9 default y
10 help
11 fb driver for Marvell MMP Display Subsystem
12
13endif
diff --git a/drivers/video/mmp/fb/Makefile b/drivers/video/mmp/fb/Makefile
new file mode 100644
index 000000000000..709fd1f76abe
--- /dev/null
+++ b/drivers/video/mmp/fb/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_MMP_FB) += mmpfb.o
diff --git a/drivers/video/mmp/fb/mmpfb.c b/drivers/video/mmp/fb/mmpfb.c
new file mode 100644
index 000000000000..f34a3a907f1b
--- /dev/null
+++ b/drivers/video/mmp/fb/mmpfb.c
@@ -0,0 +1,684 @@
1/*
2 * linux/drivers/video/mmp/fb/mmpfb.c
3 * Framebuffer driver for Marvell Display controller.
4 *
5 * Copyright (C) 2012 Marvell Technology Group Ltd.
6 * Authors: Zhou Zhu <zzhu3@marvell.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22#include <linux/module.h>
23#include <linux/dma-mapping.h>
24#include "mmpfb.h"
25
26static int var_to_pixfmt(struct fb_var_screeninfo *var)
27{
28 /*
29 * Pseudocolor mode?
30 */
31 if (var->bits_per_pixel == 8)
32 return PIXFMT_PSEUDOCOLOR;
33
34 /*
35 * Check for YUV422PLANAR.
36 */
37 if (var->bits_per_pixel == 16 && var->red.length == 8 &&
38 var->green.length == 4 && var->blue.length == 4) {
39 if (var->green.offset >= var->blue.offset)
40 return PIXFMT_YUV422P;
41 else
42 return PIXFMT_YVU422P;
43 }
44
45 /*
46 * Check for YUV420PLANAR.
47 */
48 if (var->bits_per_pixel == 12 && var->red.length == 8 &&
49 var->green.length == 2 && var->blue.length == 2) {
50 if (var->green.offset >= var->blue.offset)
51 return PIXFMT_YUV420P;
52 else
53 return PIXFMT_YVU420P;
54 }
55
56 /*
57 * Check for YUV422PACK.
58 */
59 if (var->bits_per_pixel == 16 && var->red.length == 16 &&
60 var->green.length == 16 && var->blue.length == 16) {
61 if (var->red.offset == 0)
62 return PIXFMT_YUYV;
63 else if (var->green.offset >= var->blue.offset)
64 return PIXFMT_UYVY;
65 else
66 return PIXFMT_VYUY;
67 }
68
69 /*
70 * Check for 565/1555.
71 */
72 if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
73 var->green.length <= 6 && var->blue.length <= 5) {
74 if (var->transp.length == 0) {
75 if (var->red.offset >= var->blue.offset)
76 return PIXFMT_RGB565;
77 else
78 return PIXFMT_BGR565;
79 }
80 }
81
82 /*
83 * Check for 888/A888.
84 */
85 if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
86 var->green.length <= 8 && var->blue.length <= 8) {
87 if (var->bits_per_pixel == 24 && var->transp.length == 0) {
88 if (var->red.offset >= var->blue.offset)
89 return PIXFMT_RGB888PACK;
90 else
91 return PIXFMT_BGR888PACK;
92 }
93
94 if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
95 if (var->red.offset >= var->blue.offset)
96 return PIXFMT_RGBA888;
97 else
98 return PIXFMT_BGRA888;
99 } else {
100 if (var->red.offset >= var->blue.offset)
101 return PIXFMT_RGB888UNPACK;
102 else
103 return PIXFMT_BGR888UNPACK;
104 }
105
106 /* fall through */
107 }
108
109 return -EINVAL;
110}
111
112static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
113{
114 switch (pix_fmt) {
115 case PIXFMT_RGB565:
116 var->bits_per_pixel = 16;
117 var->red.offset = 11; var->red.length = 5;
118 var->green.offset = 5; var->green.length = 6;
119 var->blue.offset = 0; var->blue.length = 5;
120 var->transp.offset = 0; var->transp.length = 0;
121 break;
122 case PIXFMT_BGR565:
123 var->bits_per_pixel = 16;
124 var->red.offset = 0; var->red.length = 5;
125 var->green.offset = 5; var->green.length = 6;
126 var->blue.offset = 11; var->blue.length = 5;
127 var->transp.offset = 0; var->transp.length = 0;
128 break;
129 case PIXFMT_RGB888UNPACK:
130 var->bits_per_pixel = 32;
131 var->red.offset = 16; var->red.length = 8;
132 var->green.offset = 8; var->green.length = 8;
133 var->blue.offset = 0; var->blue.length = 8;
134 var->transp.offset = 0; var->transp.length = 0;
135 break;
136 case PIXFMT_BGR888UNPACK:
137 var->bits_per_pixel = 32;
138 var->red.offset = 0; var->red.length = 8;
139 var->green.offset = 8; var->green.length = 8;
140 var->blue.offset = 16; var->blue.length = 8;
141 var->transp.offset = 0; var->transp.length = 0;
142 break;
143 case PIXFMT_RGBA888:
144 var->bits_per_pixel = 32;
145 var->red.offset = 16; var->red.length = 8;
146 var->green.offset = 8; var->green.length = 8;
147 var->blue.offset = 0; var->blue.length = 8;
148 var->transp.offset = 24; var->transp.length = 8;
149 break;
150 case PIXFMT_BGRA888:
151 var->bits_per_pixel = 32;
152 var->red.offset = 0; var->red.length = 8;
153 var->green.offset = 8; var->green.length = 8;
154 var->blue.offset = 16; var->blue.length = 8;
155 var->transp.offset = 24; var->transp.length = 8;
156 break;
157 case PIXFMT_RGB888PACK:
158 var->bits_per_pixel = 24;
159 var->red.offset = 16; var->red.length = 8;
160 var->green.offset = 8; var->green.length = 8;
161 var->blue.offset = 0; var->blue.length = 8;
162 var->transp.offset = 0; var->transp.length = 0;
163 break;
164 case PIXFMT_BGR888PACK:
165 var->bits_per_pixel = 24;
166 var->red.offset = 0; var->red.length = 8;
167 var->green.offset = 8; var->green.length = 8;
168 var->blue.offset = 16; var->blue.length = 8;
169 var->transp.offset = 0; var->transp.length = 0;
170 break;
171 case PIXFMT_YUV420P:
172 var->bits_per_pixel = 12;
173 var->red.offset = 4; var->red.length = 8;
174 var->green.offset = 2; var->green.length = 2;
175 var->blue.offset = 0; var->blue.length = 2;
176 var->transp.offset = 0; var->transp.length = 0;
177 break;
178 case PIXFMT_YVU420P:
179 var->bits_per_pixel = 12;
180 var->red.offset = 4; var->red.length = 8;
181 var->green.offset = 0; var->green.length = 2;
182 var->blue.offset = 2; var->blue.length = 2;
183 var->transp.offset = 0; var->transp.length = 0;
184 break;
185 case PIXFMT_YUV422P:
186 var->bits_per_pixel = 16;
187 var->red.offset = 8; var->red.length = 8;
188 var->green.offset = 4; var->green.length = 4;
189 var->blue.offset = 0; var->blue.length = 4;
190 var->transp.offset = 0; var->transp.length = 0;
191 break;
192 case PIXFMT_YVU422P:
193 var->bits_per_pixel = 16;
194 var->red.offset = 8; var->red.length = 8;
195 var->green.offset = 0; var->green.length = 4;
196 var->blue.offset = 4; var->blue.length = 4;
197 var->transp.offset = 0; var->transp.length = 0;
198 break;
199 case PIXFMT_UYVY:
200 var->bits_per_pixel = 16;
201 var->red.offset = 8; var->red.length = 16;
202 var->green.offset = 4; var->green.length = 16;
203 var->blue.offset = 0; var->blue.length = 16;
204 var->transp.offset = 0; var->transp.length = 0;
205 break;
206 case PIXFMT_VYUY:
207 var->bits_per_pixel = 16;
208 var->red.offset = 8; var->red.length = 16;
209 var->green.offset = 0; var->green.length = 16;
210 var->blue.offset = 4; var->blue.length = 16;
211 var->transp.offset = 0; var->transp.length = 0;
212 break;
213 case PIXFMT_YUYV:
214 var->bits_per_pixel = 16;
215 var->red.offset = 0; var->red.length = 16;
216 var->green.offset = 4; var->green.length = 16;
217 var->blue.offset = 8; var->blue.length = 16;
218 var->transp.offset = 0; var->transp.length = 0;
219 break;
220 case PIXFMT_PSEUDOCOLOR:
221 var->bits_per_pixel = 8;
222 var->red.offset = 0; var->red.length = 8;
223 var->green.offset = 0; var->green.length = 8;
224 var->blue.offset = 0; var->blue.length = 8;
225 var->transp.offset = 0; var->transp.length = 0;
226 break;
227 }
228}
229
230/*
231 * fb framework has its limitation:
232 * 1. input color/output color is not seprated
233 * 2. fb_videomode not include output color
234 * so for fb usage, we keep a output format which is not changed
235 * then it's added for mmpmode
236 */
237static void fbmode_to_mmpmode(struct mmp_mode *mode,
238 struct fb_videomode *videomode, int output_fmt)
239{
240 u64 div_result = 1000000000000ll;
241 mode->name = videomode->name;
242 mode->refresh = videomode->refresh;
243 mode->xres = videomode->xres;
244 mode->yres = videomode->yres;
245
246 do_div(div_result, videomode->pixclock);
247 mode->pixclock_freq = (u32)div_result;
248
249 mode->left_margin = videomode->left_margin;
250 mode->right_margin = videomode->right_margin;
251 mode->upper_margin = videomode->upper_margin;
252 mode->lower_margin = videomode->lower_margin;
253 mode->hsync_len = videomode->hsync_len;
254 mode->vsync_len = videomode->vsync_len;
255 mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
256 mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
257 /* no defined flag in fb, use vmode>>3*/
258 mode->invert_pixclock = !!(videomode->vmode & 8);
259 mode->pix_fmt_out = output_fmt;
260}
261
262static void mmpmode_to_fbmode(struct fb_videomode *videomode,
263 struct mmp_mode *mode)
264{
265 u64 div_result = 1000000000000ll;
266
267 videomode->name = mode->name;
268 videomode->refresh = mode->refresh;
269 videomode->xres = mode->xres;
270 videomode->yres = mode->yres;
271
272 do_div(div_result, mode->pixclock_freq);
273 videomode->pixclock = (u32)div_result;
274
275 videomode->left_margin = mode->left_margin;
276 videomode->right_margin = mode->right_margin;
277 videomode->upper_margin = mode->upper_margin;
278 videomode->lower_margin = mode->lower_margin;
279 videomode->hsync_len = mode->hsync_len;
280 videomode->vsync_len = mode->vsync_len;
281 videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
282 | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
283 videomode->vmode = mode->invert_pixclock ? 8 : 0;
284}
285
286static int mmpfb_check_var(struct fb_var_screeninfo *var,
287 struct fb_info *info)
288{
289 struct mmpfb_info *fbi = info->par;
290
291 if (var->bits_per_pixel == 8)
292 return -EINVAL;
293 /*
294 * Basic geometry sanity checks.
295 */
296 if (var->xoffset + var->xres > var->xres_virtual)
297 return -EINVAL;
298 if (var->yoffset + var->yres > var->yres_virtual)
299 return -EINVAL;
300
301 /*
302 * Check size of framebuffer.
303 */
304 if (var->xres_virtual * var->yres_virtual *
305 (var->bits_per_pixel >> 3) > fbi->fb_size)
306 return -EINVAL;
307
308 return 0;
309}
310
311static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
312{
313 return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
314}
315
316static u32 to_rgb(u16 red, u16 green, u16 blue)
317{
318 red >>= 8;
319 green >>= 8;
320 blue >>= 8;
321
322 return (red << 16) | (green << 8) | blue;
323}
324
325static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
326 unsigned int green, unsigned int blue,
327 unsigned int trans, struct fb_info *info)
328{
329 struct mmpfb_info *fbi = info->par;
330 u32 val;
331
332 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
333 val = chan_to_field(red, &info->var.red);
334 val |= chan_to_field(green, &info->var.green);
335 val |= chan_to_field(blue , &info->var.blue);
336 fbi->pseudo_palette[regno] = val;
337 }
338
339 if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
340 val = to_rgb(red, green, blue);
341 /* TODO */
342 }
343
344 return 0;
345}
346
347static int mmpfb_pan_display(struct fb_var_screeninfo *var,
348 struct fb_info *info)
349{
350 struct mmpfb_info *fbi = info->par;
351 struct mmp_addr addr;
352
353 memset(&addr, 0, sizeof(addr));
354 addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
355 * var->bits_per_pixel / 8 + fbi->fb_start_dma;
356 mmp_overlay_set_addr(fbi->overlay, &addr);
357
358 return 0;
359}
360
361static int var_update(struct fb_info *info)
362{
363 struct mmpfb_info *fbi = info->par;
364 struct fb_var_screeninfo *var = &info->var;
365 struct fb_videomode *m;
366 int pix_fmt;
367
368 /* set pix_fmt */
369 pix_fmt = var_to_pixfmt(var);
370 if (pix_fmt < 0)
371 return -EINVAL;
372 pixfmt_to_var(var, pix_fmt);
373 fbi->pix_fmt = pix_fmt;
374
375 /* set var according to best video mode*/
376 m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
377 if (!m) {
378 dev_err(fbi->dev, "set par: no match mode, use best mode\n");
379 m = (struct fb_videomode *)fb_find_best_mode(var,
380 &info->modelist);
381 fb_videomode_to_var(var, m);
382 }
383 memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
384
385 /* fix to 2* yres */
386 var->yres_virtual = var->yres * 2;
387 info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
388 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
389 info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
390 info->fix.ypanstep = var->yres;
391 return 0;
392}
393
394static int mmpfb_set_par(struct fb_info *info)
395{
396 struct mmpfb_info *fbi = info->par;
397 struct fb_var_screeninfo *var = &info->var;
398 struct mmp_addr addr;
399 struct mmp_win win;
400 struct mmp_mode mode;
401 int ret;
402
403 ret = var_update(info);
404 if (ret != 0)
405 return ret;
406
407 /* set window/path according to new videomode */
408 fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
409 mmp_path_set_mode(fbi->path, &mode);
410
411 memset(&win, 0, sizeof(win));
412 win.xsrc = win.xdst = fbi->mode.xres;
413 win.ysrc = win.ydst = fbi->mode.yres;
414 win.pix_fmt = fbi->pix_fmt;
415 mmp_overlay_set_win(fbi->overlay, &win);
416
417 /* set address always */
418 memset(&addr, 0, sizeof(addr));
419 addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
420 * var->bits_per_pixel / 8 + fbi->fb_start_dma;
421 mmp_overlay_set_addr(fbi->overlay, &addr);
422
423 return 0;
424}
425
426static void mmpfb_power(struct mmpfb_info *fbi, int power)
427{
428 struct mmp_addr addr;
429 struct mmp_win win;
430 struct fb_var_screeninfo *var = &fbi->fb_info->var;
431
432 /* for power on, always set address/window again */
433 if (power) {
434 memset(&win, 0, sizeof(win));
435 win.xsrc = win.xdst = fbi->mode.xres;
436 win.ysrc = win.ydst = fbi->mode.yres;
437 win.pix_fmt = fbi->pix_fmt;
438 mmp_overlay_set_win(fbi->overlay, &win);
439
440 /* set address always */
441 memset(&addr, 0, sizeof(addr));
442 addr.phys[0] = fbi->fb_start_dma +
443 (var->yoffset * var->xres_virtual + var->xoffset)
444 * var->bits_per_pixel / 8;
445 mmp_overlay_set_addr(fbi->overlay, &addr);
446 }
447 mmp_overlay_set_onoff(fbi->overlay, power);
448}
449
450static int mmpfb_blank(int blank, struct fb_info *info)
451{
452 struct mmpfb_info *fbi = info->par;
453
454 mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
455
456 return 0;
457}
458
459static struct fb_ops mmpfb_ops = {
460 .owner = THIS_MODULE,
461 .fb_blank = mmpfb_blank,
462 .fb_check_var = mmpfb_check_var,
463 .fb_set_par = mmpfb_set_par,
464 .fb_setcolreg = mmpfb_setcolreg,
465 .fb_pan_display = mmpfb_pan_display,
466 .fb_fillrect = cfb_fillrect,
467 .fb_copyarea = cfb_copyarea,
468 .fb_imageblit = cfb_imageblit,
469};
470
471static int modes_setup(struct mmpfb_info *fbi)
472{
473 struct fb_videomode *videomodes;
474 struct mmp_mode *mmp_modes;
475 struct fb_info *info = fbi->fb_info;
476 int videomode_num, i;
477
478 /* get videomodes from path */
479 videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
480 if (!videomode_num) {
481 dev_warn(fbi->dev, "can't get videomode num\n");
482 return 0;
483 }
484 /* put videomode list to info structure */
485 videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num,
486 GFP_KERNEL);
487 if (!videomodes) {
488 dev_err(fbi->dev, "can't malloc video modes\n");
489 return -ENOMEM;
490 }
491 for (i = 0; i < videomode_num; i++)
492 mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
493 fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
494
495 /* set videomode[0] as default mode */
496 memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
497 fbi->output_fmt = mmp_modes[0].pix_fmt_out;
498 fb_videomode_to_var(&info->var, &fbi->mode);
499 mmp_path_set_mode(fbi->path, &mmp_modes[0]);
500
501 kfree(videomodes);
502 return videomode_num;
503}
504
505static int fb_info_setup(struct fb_info *info,
506 struct mmpfb_info *fbi)
507{
508 int ret = 0;
509 /* Initialise static fb parameters.*/
510 info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
511 FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
512 info->node = -1;
513 strcpy(info->fix.id, fbi->name);
514 info->fix.type = FB_TYPE_PACKED_PIXELS;
515 info->fix.type_aux = 0;
516 info->fix.xpanstep = 0;
517 info->fix.ypanstep = info->var.yres;
518 info->fix.ywrapstep = 0;
519 info->fix.accel = FB_ACCEL_NONE;
520 info->fix.smem_start = fbi->fb_start_dma;
521 info->fix.smem_len = fbi->fb_size;
522 info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
523 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
524 info->fix.line_length = info->var.xres_virtual *
525 info->var.bits_per_pixel / 8;
526 info->fbops = &mmpfb_ops;
527 info->pseudo_palette = fbi->pseudo_palette;
528 info->screen_base = fbi->fb_start;
529 info->screen_size = fbi->fb_size;
530
531 /* For FB framework: Allocate color map and Register framebuffer*/
532 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
533 ret = -ENOMEM;
534
535 return ret;
536}
537
538static void fb_info_clear(struct fb_info *info)
539{
540 fb_dealloc_cmap(&info->cmap);
541}
542
543static int mmpfb_probe(struct platform_device *pdev)
544{
545 struct mmp_buffer_driver_mach_info *mi;
546 struct fb_info *info = 0;
547 struct mmpfb_info *fbi = 0;
548 int ret, modes_num;
549
550 mi = pdev->dev.platform_data;
551 if (mi == NULL) {
552 dev_err(&pdev->dev, "no platform data defined\n");
553 return -EINVAL;
554 }
555
556 /* initialize fb */
557 info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
558 if (info == NULL)
559 return -ENOMEM;
560 fbi = info->par;
561 if (!fbi) {
562 ret = -EINVAL;
563 goto failed;
564 }
565
566 /* init fb */
567 fbi->fb_info = info;
568 platform_set_drvdata(pdev, fbi);
569 fbi->dev = &pdev->dev;
570 fbi->name = mi->name;
571 fbi->pix_fmt = mi->default_pixfmt;
572 pixfmt_to_var(&info->var, fbi->pix_fmt);
573 mutex_init(&fbi->access_ok);
574
575 /* get display path by name */
576 fbi->path = mmp_get_path(mi->path_name);
577 if (!fbi->path) {
578 dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
579 ret = -EINVAL;
580 goto failed_destroy_mutex;
581 }
582
583 dev_info(fbi->dev, "path %s get\n", fbi->path->name);
584
585 /* get overlay */
586 fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
587 if (!fbi->overlay) {
588 ret = -EINVAL;
589 goto failed_destroy_mutex;
590 }
591 /* set fetch used */
592 mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
593
594 modes_num = modes_setup(fbi);
595 if (modes_num < 0) {
596 ret = modes_num;
597 goto failed_destroy_mutex;
598 }
599
600 /*
601 * if get modes success, means not hotplug panels, use caculated buffer
602 * or use default size
603 */
604 if (modes_num > 0) {
605 /* fix to 2* yres */
606 info->var.yres_virtual = info->var.yres * 2;
607
608 /* Allocate framebuffer memory: size = modes xy *4 */
609 fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
610 * info->var.bits_per_pixel / 8;
611 } else {
612 fbi->fb_size = MMPFB_DEFAULT_SIZE;
613 }
614
615 fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
616 &fbi->fb_start_dma, GFP_KERNEL);
617 if (fbi->fb_start == NULL) {
618 dev_err(&pdev->dev, "can't alloc framebuffer\n");
619 ret = -ENOMEM;
620 goto failed_destroy_mutex;
621 }
622 memset(fbi->fb_start, 0, fbi->fb_size);
623 dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
624
625 /* fb power on */
626 if (modes_num > 0)
627 mmpfb_power(fbi, 1);
628
629 ret = fb_info_setup(info, fbi);
630 if (ret < 0)
631 goto failed_free_buff;
632
633 ret = register_framebuffer(info);
634 if (ret < 0) {
635 dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
636 ret = -ENXIO;
637 goto failed_clear_info;
638 }
639
640 dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
641 info->node, info->fix.id);
642
643#ifdef CONFIG_LOGO
644 if (fbi->fb_start) {
645 fb_prepare_logo(info, 0);
646 fb_show_logo(info, 0);
647 }
648#endif
649
650 return 0;
651
652failed_clear_info:
653 fb_info_clear(info);
654failed_free_buff:
655 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
656 fbi->fb_start_dma);
657failed_destroy_mutex:
658 mutex_destroy(&fbi->access_ok);
659failed:
660 dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
661 platform_set_drvdata(pdev, NULL);
662
663 framebuffer_release(info);
664
665 return ret;
666}
667
668static struct platform_driver mmpfb_driver = {
669 .driver = {
670 .name = "mmp-fb",
671 .owner = THIS_MODULE,
672 },
673 .probe = mmpfb_probe,
674};
675
676static int mmpfb_init(void)
677{
678 return platform_driver_register(&mmpfb_driver);
679}
680module_init(mmpfb_init);
681
682MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
683MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
684MODULE_LICENSE("GPL");
diff --git a/drivers/video/mmp/fb/mmpfb.h b/drivers/video/mmp/fb/mmpfb.h
new file mode 100644
index 000000000000..88c23c10a9ec
--- /dev/null
+++ b/drivers/video/mmp/fb/mmpfb.h
@@ -0,0 +1,54 @@
1/*
2 * linux/drivers/video/mmp/fb/mmpfb.h
3 * Framebuffer driver for Marvell Display controller.
4 *
5 * Copyright (C) 2012 Marvell Technology Group Ltd.
6 * Authors: Zhou Zhu <zzhu3@marvell.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#ifndef _MMP_FB_H_
24#define _MMP_FB_H_
25
26#include <video/mmp_disp.h>
27#include <linux/fb.h>
28
29/* LCD controller private state. */
30struct mmpfb_info {
31 struct device *dev;
32 int id;
33 const char *name;
34
35 struct fb_info *fb_info;
36 /* basicaly videomode is for output */
37 struct fb_videomode mode;
38 int pix_fmt;
39
40 void *fb_start;
41 int fb_size;
42 dma_addr_t fb_start_dma;
43
44 struct mmp_overlay *overlay;
45 struct mmp_path *path;
46
47 struct mutex access_ok;
48
49 unsigned int pseudo_palette[16];
50 int output_fmt;
51};
52
53#define MMPFB_DEFAULT_SIZE (PAGE_ALIGN(1920 * 1080 * 4 * 2))
54#endif /* _MMP_FB_H_ */