blob: 7ab31eb76a8c3530e119589550afff1fde69e580 [file] [log] [blame]
Zhou Zhud2e8bae2013-02-21 16:42:12 -08001/*
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>
Arnd Bergmann24cb87a2013-02-21 16:42:14 -080024#include <linux/platform_device.h>
Zhou Zhud2e8bae2013-02-21 16:42:12 -080025#include "mmpfb.h"
26
27static int var_to_pixfmt(struct fb_var_screeninfo *var)
28{
29 /*
30 * Pseudocolor mode?
31 */
32 if (var->bits_per_pixel == 8)
33 return PIXFMT_PSEUDOCOLOR;
34
35 /*
36 * Check for YUV422PLANAR.
37 */
38 if (var->bits_per_pixel == 16 && var->red.length == 8 &&
39 var->green.length == 4 && var->blue.length == 4) {
40 if (var->green.offset >= var->blue.offset)
41 return PIXFMT_YUV422P;
42 else
43 return PIXFMT_YVU422P;
44 }
45
46 /*
47 * Check for YUV420PLANAR.
48 */
49 if (var->bits_per_pixel == 12 && var->red.length == 8 &&
50 var->green.length == 2 && var->blue.length == 2) {
51 if (var->green.offset >= var->blue.offset)
52 return PIXFMT_YUV420P;
53 else
54 return PIXFMT_YVU420P;
55 }
56
57 /*
58 * Check for YUV422PACK.
59 */
60 if (var->bits_per_pixel == 16 && var->red.length == 16 &&
61 var->green.length == 16 && var->blue.length == 16) {
62 if (var->red.offset == 0)
63 return PIXFMT_YUYV;
64 else if (var->green.offset >= var->blue.offset)
65 return PIXFMT_UYVY;
66 else
67 return PIXFMT_VYUY;
68 }
69
70 /*
71 * Check for 565/1555.
72 */
73 if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
74 var->green.length <= 6 && var->blue.length <= 5) {
75 if (var->transp.length == 0) {
76 if (var->red.offset >= var->blue.offset)
77 return PIXFMT_RGB565;
78 else
79 return PIXFMT_BGR565;
80 }
81 }
82
83 /*
84 * Check for 888/A888.
85 */
86 if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
87 var->green.length <= 8 && var->blue.length <= 8) {
88 if (var->bits_per_pixel == 24 && var->transp.length == 0) {
89 if (var->red.offset >= var->blue.offset)
90 return PIXFMT_RGB888PACK;
91 else
92 return PIXFMT_BGR888PACK;
93 }
94
95 if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
96 if (var->red.offset >= var->blue.offset)
97 return PIXFMT_RGBA888;
98 else
99 return PIXFMT_BGRA888;
100 } else {
101 if (var->red.offset >= var->blue.offset)
102 return PIXFMT_RGB888UNPACK;
103 else
104 return PIXFMT_BGR888UNPACK;
105 }
106
107 /* fall through */
108 }
109
110 return -EINVAL;
111}
112
113static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
114{
115 switch (pix_fmt) {
116 case PIXFMT_RGB565:
117 var->bits_per_pixel = 16;
118 var->red.offset = 11; var->red.length = 5;
119 var->green.offset = 5; var->green.length = 6;
120 var->blue.offset = 0; var->blue.length = 5;
121 var->transp.offset = 0; var->transp.length = 0;
122 break;
123 case PIXFMT_BGR565:
124 var->bits_per_pixel = 16;
125 var->red.offset = 0; var->red.length = 5;
126 var->green.offset = 5; var->green.length = 6;
127 var->blue.offset = 11; var->blue.length = 5;
128 var->transp.offset = 0; var->transp.length = 0;
129 break;
130 case PIXFMT_RGB888UNPACK:
131 var->bits_per_pixel = 32;
132 var->red.offset = 16; var->red.length = 8;
133 var->green.offset = 8; var->green.length = 8;
134 var->blue.offset = 0; var->blue.length = 8;
135 var->transp.offset = 0; var->transp.length = 0;
136 break;
137 case PIXFMT_BGR888UNPACK:
138 var->bits_per_pixel = 32;
139 var->red.offset = 0; var->red.length = 8;
140 var->green.offset = 8; var->green.length = 8;
141 var->blue.offset = 16; var->blue.length = 8;
142 var->transp.offset = 0; var->transp.length = 0;
143 break;
144 case PIXFMT_RGBA888:
145 var->bits_per_pixel = 32;
146 var->red.offset = 16; var->red.length = 8;
147 var->green.offset = 8; var->green.length = 8;
148 var->blue.offset = 0; var->blue.length = 8;
149 var->transp.offset = 24; var->transp.length = 8;
150 break;
151 case PIXFMT_BGRA888:
152 var->bits_per_pixel = 32;
153 var->red.offset = 0; var->red.length = 8;
154 var->green.offset = 8; var->green.length = 8;
155 var->blue.offset = 16; var->blue.length = 8;
156 var->transp.offset = 24; var->transp.length = 8;
157 break;
158 case PIXFMT_RGB888PACK:
159 var->bits_per_pixel = 24;
160 var->red.offset = 16; var->red.length = 8;
161 var->green.offset = 8; var->green.length = 8;
162 var->blue.offset = 0; var->blue.length = 8;
163 var->transp.offset = 0; var->transp.length = 0;
164 break;
165 case PIXFMT_BGR888PACK:
166 var->bits_per_pixel = 24;
167 var->red.offset = 0; var->red.length = 8;
168 var->green.offset = 8; var->green.length = 8;
169 var->blue.offset = 16; var->blue.length = 8;
170 var->transp.offset = 0; var->transp.length = 0;
171 break;
172 case PIXFMT_YUV420P:
173 var->bits_per_pixel = 12;
174 var->red.offset = 4; var->red.length = 8;
175 var->green.offset = 2; var->green.length = 2;
176 var->blue.offset = 0; var->blue.length = 2;
177 var->transp.offset = 0; var->transp.length = 0;
178 break;
179 case PIXFMT_YVU420P:
180 var->bits_per_pixel = 12;
181 var->red.offset = 4; var->red.length = 8;
182 var->green.offset = 0; var->green.length = 2;
183 var->blue.offset = 2; var->blue.length = 2;
184 var->transp.offset = 0; var->transp.length = 0;
185 break;
186 case PIXFMT_YUV422P:
187 var->bits_per_pixel = 16;
188 var->red.offset = 8; var->red.length = 8;
189 var->green.offset = 4; var->green.length = 4;
190 var->blue.offset = 0; var->blue.length = 4;
191 var->transp.offset = 0; var->transp.length = 0;
192 break;
193 case PIXFMT_YVU422P:
194 var->bits_per_pixel = 16;
195 var->red.offset = 8; var->red.length = 8;
196 var->green.offset = 0; var->green.length = 4;
197 var->blue.offset = 4; var->blue.length = 4;
198 var->transp.offset = 0; var->transp.length = 0;
199 break;
200 case PIXFMT_UYVY:
201 var->bits_per_pixel = 16;
202 var->red.offset = 8; var->red.length = 16;
203 var->green.offset = 4; var->green.length = 16;
204 var->blue.offset = 0; var->blue.length = 16;
205 var->transp.offset = 0; var->transp.length = 0;
206 break;
207 case PIXFMT_VYUY:
208 var->bits_per_pixel = 16;
209 var->red.offset = 8; var->red.length = 16;
210 var->green.offset = 0; var->green.length = 16;
211 var->blue.offset = 4; var->blue.length = 16;
212 var->transp.offset = 0; var->transp.length = 0;
213 break;
214 case PIXFMT_YUYV:
215 var->bits_per_pixel = 16;
216 var->red.offset = 0; var->red.length = 16;
217 var->green.offset = 4; var->green.length = 16;
218 var->blue.offset = 8; var->blue.length = 16;
219 var->transp.offset = 0; var->transp.length = 0;
220 break;
221 case PIXFMT_PSEUDOCOLOR:
222 var->bits_per_pixel = 8;
223 var->red.offset = 0; var->red.length = 8;
224 var->green.offset = 0; var->green.length = 8;
225 var->blue.offset = 0; var->blue.length = 8;
226 var->transp.offset = 0; var->transp.length = 0;
227 break;
228 }
229}
230
231/*
232 * fb framework has its limitation:
233 * 1. input color/output color is not seprated
234 * 2. fb_videomode not include output color
235 * so for fb usage, we keep a output format which is not changed
236 * then it's added for mmpmode
237 */
238static void fbmode_to_mmpmode(struct mmp_mode *mode,
239 struct fb_videomode *videomode, int output_fmt)
240{
241 u64 div_result = 1000000000000ll;
242 mode->name = videomode->name;
243 mode->refresh = videomode->refresh;
244 mode->xres = videomode->xres;
245 mode->yres = videomode->yres;
246
247 do_div(div_result, videomode->pixclock);
248 mode->pixclock_freq = (u32)div_result;
249
250 mode->left_margin = videomode->left_margin;
251 mode->right_margin = videomode->right_margin;
252 mode->upper_margin = videomode->upper_margin;
253 mode->lower_margin = videomode->lower_margin;
254 mode->hsync_len = videomode->hsync_len;
255 mode->vsync_len = videomode->vsync_len;
256 mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
257 mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
258 /* no defined flag in fb, use vmode>>3*/
259 mode->invert_pixclock = !!(videomode->vmode & 8);
260 mode->pix_fmt_out = output_fmt;
261}
262
263static void mmpmode_to_fbmode(struct fb_videomode *videomode,
264 struct mmp_mode *mode)
265{
266 u64 div_result = 1000000000000ll;
267
268 videomode->name = mode->name;
269 videomode->refresh = mode->refresh;
270 videomode->xres = mode->xres;
271 videomode->yres = mode->yres;
272
273 do_div(div_result, mode->pixclock_freq);
274 videomode->pixclock = (u32)div_result;
275
276 videomode->left_margin = mode->left_margin;
277 videomode->right_margin = mode->right_margin;
278 videomode->upper_margin = mode->upper_margin;
279 videomode->lower_margin = mode->lower_margin;
280 videomode->hsync_len = mode->hsync_len;
281 videomode->vsync_len = mode->vsync_len;
282 videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
283 | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
284 videomode->vmode = mode->invert_pixclock ? 8 : 0;
285}
286
287static int mmpfb_check_var(struct fb_var_screeninfo *var,
288 struct fb_info *info)
289{
290 struct mmpfb_info *fbi = info->par;
291
292 if (var->bits_per_pixel == 8)
293 return -EINVAL;
294 /*
295 * Basic geometry sanity checks.
296 */
297 if (var->xoffset + var->xres > var->xres_virtual)
298 return -EINVAL;
299 if (var->yoffset + var->yres > var->yres_virtual)
300 return -EINVAL;
301
302 /*
303 * Check size of framebuffer.
304 */
305 if (var->xres_virtual * var->yres_virtual *
306 (var->bits_per_pixel >> 3) > fbi->fb_size)
307 return -EINVAL;
308
309 return 0;
310}
311
312static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
313{
314 return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
315}
316
317static u32 to_rgb(u16 red, u16 green, u16 blue)
318{
319 red >>= 8;
320 green >>= 8;
321 blue >>= 8;
322
323 return (red << 16) | (green << 8) | blue;
324}
325
326static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
327 unsigned int green, unsigned int blue,
328 unsigned int trans, struct fb_info *info)
329{
330 struct mmpfb_info *fbi = info->par;
331 u32 val;
332
333 if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
334 val = chan_to_field(red, &info->var.red);
335 val |= chan_to_field(green, &info->var.green);
336 val |= chan_to_field(blue , &info->var.blue);
337 fbi->pseudo_palette[regno] = val;
338 }
339
340 if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
341 val = to_rgb(red, green, blue);
342 /* TODO */
343 }
344
345 return 0;
346}
347
348static int mmpfb_pan_display(struct fb_var_screeninfo *var,
349 struct fb_info *info)
350{
351 struct mmpfb_info *fbi = info->par;
352 struct mmp_addr addr;
353
354 memset(&addr, 0, sizeof(addr));
355 addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
356 * var->bits_per_pixel / 8 + fbi->fb_start_dma;
357 mmp_overlay_set_addr(fbi->overlay, &addr);
358
359 return 0;
360}
361
362static int var_update(struct fb_info *info)
363{
364 struct mmpfb_info *fbi = info->par;
365 struct fb_var_screeninfo *var = &info->var;
366 struct fb_videomode *m;
367 int pix_fmt;
368
369 /* set pix_fmt */
370 pix_fmt = var_to_pixfmt(var);
371 if (pix_fmt < 0)
372 return -EINVAL;
373 pixfmt_to_var(var, pix_fmt);
374 fbi->pix_fmt = pix_fmt;
375
376 /* set var according to best video mode*/
377 m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
378 if (!m) {
379 dev_err(fbi->dev, "set par: no match mode, use best mode\n");
380 m = (struct fb_videomode *)fb_find_best_mode(var,
381 &info->modelist);
382 fb_videomode_to_var(var, m);
383 }
384 memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
385
386 /* fix to 2* yres */
387 var->yres_virtual = var->yres * 2;
388 info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
389 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
390 info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
391 info->fix.ypanstep = var->yres;
392 return 0;
393}
394
Jing Xiangcc50bc52013-09-13 14:59:48 +0800395static void mmpfb_set_win(struct fb_info *info)
396{
397 struct mmpfb_info *fbi = info->par;
Jing Xiang24586d82013-09-13 14:59:49 +0800398 struct fb_var_screeninfo *var = &info->var;
Jing Xiangcc50bc52013-09-13 14:59:48 +0800399 struct mmp_win win;
Jing Xiang24586d82013-09-13 14:59:49 +0800400 u32 stride;
Jing Xiangcc50bc52013-09-13 14:59:48 +0800401
402 memset(&win, 0, sizeof(win));
403 win.xsrc = win.xdst = fbi->mode.xres;
404 win.ysrc = win.ydst = fbi->mode.yres;
405 win.pix_fmt = fbi->pix_fmt;
Jing Xiang24586d82013-09-13 14:59:49 +0800406 stride = pixfmt_to_stride(win.pix_fmt);
407 win.pitch[0] = var->xres_virtual * stride;
408 win.pitch[1] = win.pitch[2] =
409 (stride == 1) ? (var->xres_virtual >> 1) : 0;
Jing Xiangcc50bc52013-09-13 14:59:48 +0800410 mmp_overlay_set_win(fbi->overlay, &win);
411}
412
Zhou Zhud2e8bae2013-02-21 16:42:12 -0800413static int mmpfb_set_par(struct fb_info *info)
414{
415 struct mmpfb_info *fbi = info->par;
416 struct fb_var_screeninfo *var = &info->var;
417 struct mmp_addr addr;
Zhou Zhud2e8bae2013-02-21 16:42:12 -0800418 struct mmp_mode mode;
419 int ret;
420
421 ret = var_update(info);
422 if (ret != 0)
423 return ret;
424
425 /* set window/path according to new videomode */
426 fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
427 mmp_path_set_mode(fbi->path, &mode);
428
Jing Xiangcc50bc52013-09-13 14:59:48 +0800429 /* set window related info */
430 mmpfb_set_win(info);
Zhou Zhud2e8bae2013-02-21 16:42:12 -0800431
432 /* set address always */
433 memset(&addr, 0, sizeof(addr));
434 addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
435 * var->bits_per_pixel / 8 + fbi->fb_start_dma;
436 mmp_overlay_set_addr(fbi->overlay, &addr);
437
438 return 0;
439}
440
441static void mmpfb_power(struct mmpfb_info *fbi, int power)
442{
443 struct mmp_addr addr;
Zhou Zhud2e8bae2013-02-21 16:42:12 -0800444 struct fb_var_screeninfo *var = &fbi->fb_info->var;
445
446 /* for power on, always set address/window again */
447 if (power) {
Jing Xiangcc50bc52013-09-13 14:59:48 +0800448 /* set window related info */
449 mmpfb_set_win(fbi->fb_info);
Zhou Zhud2e8bae2013-02-21 16:42:12 -0800450
451 /* set address always */
452 memset(&addr, 0, sizeof(addr));
453 addr.phys[0] = fbi->fb_start_dma +
454 (var->yoffset * var->xres_virtual + var->xoffset)
455 * var->bits_per_pixel / 8;
456 mmp_overlay_set_addr(fbi->overlay, &addr);
457 }
458 mmp_overlay_set_onoff(fbi->overlay, power);
459}
460
461static int mmpfb_blank(int blank, struct fb_info *info)
462{
463 struct mmpfb_info *fbi = info->par;
464
465 mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
466
467 return 0;
468}
469
470static struct fb_ops mmpfb_ops = {
471 .owner = THIS_MODULE,
472 .fb_blank = mmpfb_blank,
473 .fb_check_var = mmpfb_check_var,
474 .fb_set_par = mmpfb_set_par,
475 .fb_setcolreg = mmpfb_setcolreg,
476 .fb_pan_display = mmpfb_pan_display,
477 .fb_fillrect = cfb_fillrect,
478 .fb_copyarea = cfb_copyarea,
479 .fb_imageblit = cfb_imageblit,
480};
481
482static int modes_setup(struct mmpfb_info *fbi)
483{
484 struct fb_videomode *videomodes;
485 struct mmp_mode *mmp_modes;
486 struct fb_info *info = fbi->fb_info;
487 int videomode_num, i;
488
489 /* get videomodes from path */
490 videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
491 if (!videomode_num) {
492 dev_warn(fbi->dev, "can't get videomode num\n");
493 return 0;
494 }
495 /* put videomode list to info structure */
496 videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num,
497 GFP_KERNEL);
498 if (!videomodes) {
499 dev_err(fbi->dev, "can't malloc video modes\n");
500 return -ENOMEM;
501 }
502 for (i = 0; i < videomode_num; i++)
503 mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
504 fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
505
506 /* set videomode[0] as default mode */
507 memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
508 fbi->output_fmt = mmp_modes[0].pix_fmt_out;
509 fb_videomode_to_var(&info->var, &fbi->mode);
510 mmp_path_set_mode(fbi->path, &mmp_modes[0]);
511
512 kfree(videomodes);
513 return videomode_num;
514}
515
516static int fb_info_setup(struct fb_info *info,
517 struct mmpfb_info *fbi)
518{
519 int ret = 0;
520 /* Initialise static fb parameters.*/
521 info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
522 FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
523 info->node = -1;
524 strcpy(info->fix.id, fbi->name);
525 info->fix.type = FB_TYPE_PACKED_PIXELS;
526 info->fix.type_aux = 0;
527 info->fix.xpanstep = 0;
528 info->fix.ypanstep = info->var.yres;
529 info->fix.ywrapstep = 0;
530 info->fix.accel = FB_ACCEL_NONE;
531 info->fix.smem_start = fbi->fb_start_dma;
532 info->fix.smem_len = fbi->fb_size;
533 info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
534 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
535 info->fix.line_length = info->var.xres_virtual *
536 info->var.bits_per_pixel / 8;
537 info->fbops = &mmpfb_ops;
538 info->pseudo_palette = fbi->pseudo_palette;
539 info->screen_base = fbi->fb_start;
540 info->screen_size = fbi->fb_size;
541
542 /* For FB framework: Allocate color map and Register framebuffer*/
543 if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
544 ret = -ENOMEM;
545
546 return ret;
547}
548
549static void fb_info_clear(struct fb_info *info)
550{
551 fb_dealloc_cmap(&info->cmap);
552}
553
554static int mmpfb_probe(struct platform_device *pdev)
555{
556 struct mmp_buffer_driver_mach_info *mi;
557 struct fb_info *info = 0;
558 struct mmpfb_info *fbi = 0;
559 int ret, modes_num;
560
561 mi = pdev->dev.platform_data;
562 if (mi == NULL) {
563 dev_err(&pdev->dev, "no platform data defined\n");
564 return -EINVAL;
565 }
566
567 /* initialize fb */
568 info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
569 if (info == NULL)
570 return -ENOMEM;
571 fbi = info->par;
572 if (!fbi) {
573 ret = -EINVAL;
574 goto failed;
575 }
576
577 /* init fb */
578 fbi->fb_info = info;
579 platform_set_drvdata(pdev, fbi);
580 fbi->dev = &pdev->dev;
581 fbi->name = mi->name;
582 fbi->pix_fmt = mi->default_pixfmt;
583 pixfmt_to_var(&info->var, fbi->pix_fmt);
584 mutex_init(&fbi->access_ok);
585
586 /* get display path by name */
587 fbi->path = mmp_get_path(mi->path_name);
588 if (!fbi->path) {
589 dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
590 ret = -EINVAL;
591 goto failed_destroy_mutex;
592 }
593
594 dev_info(fbi->dev, "path %s get\n", fbi->path->name);
595
596 /* get overlay */
597 fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
598 if (!fbi->overlay) {
599 ret = -EINVAL;
600 goto failed_destroy_mutex;
601 }
602 /* set fetch used */
603 mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
604
605 modes_num = modes_setup(fbi);
606 if (modes_num < 0) {
607 ret = modes_num;
608 goto failed_destroy_mutex;
609 }
610
611 /*
612 * if get modes success, means not hotplug panels, use caculated buffer
613 * or use default size
614 */
615 if (modes_num > 0) {
616 /* fix to 2* yres */
617 info->var.yres_virtual = info->var.yres * 2;
618
619 /* Allocate framebuffer memory: size = modes xy *4 */
620 fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
621 * info->var.bits_per_pixel / 8;
622 } else {
623 fbi->fb_size = MMPFB_DEFAULT_SIZE;
624 }
625
626 fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
627 &fbi->fb_start_dma, GFP_KERNEL);
628 if (fbi->fb_start == NULL) {
629 dev_err(&pdev->dev, "can't alloc framebuffer\n");
630 ret = -ENOMEM;
631 goto failed_destroy_mutex;
632 }
633 memset(fbi->fb_start, 0, fbi->fb_size);
634 dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
635
636 /* fb power on */
637 if (modes_num > 0)
638 mmpfb_power(fbi, 1);
639
640 ret = fb_info_setup(info, fbi);
641 if (ret < 0)
642 goto failed_free_buff;
643
644 ret = register_framebuffer(info);
645 if (ret < 0) {
646 dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
647 ret = -ENXIO;
648 goto failed_clear_info;
649 }
650
651 dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
652 info->node, info->fix.id);
653
654#ifdef CONFIG_LOGO
655 if (fbi->fb_start) {
656 fb_prepare_logo(info, 0);
657 fb_show_logo(info, 0);
658 }
659#endif
660
661 return 0;
662
663failed_clear_info:
664 fb_info_clear(info);
665failed_free_buff:
666 dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
667 fbi->fb_start_dma);
668failed_destroy_mutex:
669 mutex_destroy(&fbi->access_ok);
670failed:
671 dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
Zhou Zhud2e8bae2013-02-21 16:42:12 -0800672
673 framebuffer_release(info);
674
675 return ret;
676}
677
678static struct platform_driver mmpfb_driver = {
679 .driver = {
680 .name = "mmp-fb",
681 .owner = THIS_MODULE,
682 },
683 .probe = mmpfb_probe,
684};
685
686static int mmpfb_init(void)
687{
688 return platform_driver_register(&mmpfb_driver);
689}
690module_init(mmpfb_init);
691
692MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
693MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
694MODULE_LICENSE("GPL");