xref: /openbmc/linux/drivers/video/fbdev/mmp/fb/mmpfb.c (revision ba61bb17)
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 <linux/platform_device.h>
25 #include "mmpfb.h"
26 
27 static 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 
113 static 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  */
238 static 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 
263 static 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 
287 static 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 
312 static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
313 {
314 	return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
315 }
316 
317 static 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 
326 static 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 
348 static 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 
362 static 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 
395 static void mmpfb_set_win(struct fb_info *info)
396 {
397 	struct mmpfb_info *fbi = info->par;
398 	struct fb_var_screeninfo *var = &info->var;
399 	struct mmp_win win;
400 	u32 stride;
401 
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;
406 	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;
410 	mmp_overlay_set_win(fbi->overlay, &win);
411 }
412 
413 static 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;
418 	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 
429 	/* set window related info */
430 	mmpfb_set_win(info);
431 
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 
441 static void mmpfb_power(struct mmpfb_info *fbi, int power)
442 {
443 	struct mmp_addr addr;
444 	struct fb_var_screeninfo *var = &fbi->fb_info->var;
445 
446 	/* for power on, always set address/window again */
447 	if (power) {
448 		/* set window related info */
449 		mmpfb_set_win(fbi->fb_info);
450 
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 
461 static 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 
470 static 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 
482 static 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 = kcalloc(videomode_num, sizeof(struct fb_videomode),
497 			     GFP_KERNEL);
498 	if (!videomodes)
499 		return -ENOMEM;
500 
501 	for (i = 0; i < videomode_num; i++)
502 		mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
503 	fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
504 
505 	/* set videomode[0] as default mode */
506 	memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
507 	fbi->output_fmt = mmp_modes[0].pix_fmt_out;
508 	fb_videomode_to_var(&info->var, &fbi->mode);
509 	mmp_path_set_mode(fbi->path, &mmp_modes[0]);
510 
511 	kfree(videomodes);
512 	return videomode_num;
513 }
514 
515 static int fb_info_setup(struct fb_info *info,
516 			struct mmpfb_info *fbi)
517 {
518 	int ret = 0;
519 	/* Initialise static fb parameters.*/
520 	info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
521 		FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
522 	info->node = -1;
523 	strcpy(info->fix.id, fbi->name);
524 	info->fix.type = FB_TYPE_PACKED_PIXELS;
525 	info->fix.type_aux = 0;
526 	info->fix.xpanstep = 0;
527 	info->fix.ypanstep = info->var.yres;
528 	info->fix.ywrapstep = 0;
529 	info->fix.accel = FB_ACCEL_NONE;
530 	info->fix.smem_start = fbi->fb_start_dma;
531 	info->fix.smem_len = fbi->fb_size;
532 	info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
533 		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
534 	info->fix.line_length = info->var.xres_virtual *
535 		info->var.bits_per_pixel / 8;
536 	info->fbops = &mmpfb_ops;
537 	info->pseudo_palette = fbi->pseudo_palette;
538 	info->screen_base = fbi->fb_start;
539 	info->screen_size = fbi->fb_size;
540 
541 	/* For FB framework: Allocate color map and Register framebuffer*/
542 	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
543 		ret = -ENOMEM;
544 
545 	return ret;
546 }
547 
548 static void fb_info_clear(struct fb_info *info)
549 {
550 	fb_dealloc_cmap(&info->cmap);
551 }
552 
553 static int mmpfb_probe(struct platform_device *pdev)
554 {
555 	struct mmp_buffer_driver_mach_info *mi;
556 	struct fb_info *info;
557 	struct mmpfb_info *fbi;
558 	int ret, modes_num;
559 
560 	mi = pdev->dev.platform_data;
561 	if (mi == NULL) {
562 		dev_err(&pdev->dev, "no platform data defined\n");
563 		return -EINVAL;
564 	}
565 
566 	/* initialize fb */
567 	info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
568 	if (info == NULL)
569 		return -ENOMEM;
570 	fbi = info->par;
571 
572 	/* init fb */
573 	fbi->fb_info = info;
574 	platform_set_drvdata(pdev, fbi);
575 	fbi->dev = &pdev->dev;
576 	fbi->name = mi->name;
577 	fbi->pix_fmt = mi->default_pixfmt;
578 	pixfmt_to_var(&info->var, fbi->pix_fmt);
579 	mutex_init(&fbi->access_ok);
580 
581 	/* get display path by name */
582 	fbi->path = mmp_get_path(mi->path_name);
583 	if (!fbi->path) {
584 		dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
585 		ret = -EINVAL;
586 		goto failed_destroy_mutex;
587 	}
588 
589 	dev_info(fbi->dev, "path %s get\n", fbi->path->name);
590 
591 	/* get overlay */
592 	fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
593 	if (!fbi->overlay) {
594 		ret = -EINVAL;
595 		goto failed_destroy_mutex;
596 	}
597 	/* set fetch used */
598 	mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
599 
600 	modes_num = modes_setup(fbi);
601 	if (modes_num < 0) {
602 		ret = modes_num;
603 		goto failed_destroy_mutex;
604 	}
605 
606 	/*
607 	 * if get modes success, means not hotplug panels, use caculated buffer
608 	 * or use default size
609 	 */
610 	if (modes_num > 0) {
611 		/* fix to 2* yres */
612 		info->var.yres_virtual = info->var.yres * 2;
613 
614 		/* Allocate framebuffer memory: size = modes xy *4 */
615 		fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
616 				* info->var.bits_per_pixel / 8;
617 	} else {
618 		fbi->fb_size = MMPFB_DEFAULT_SIZE;
619 	}
620 
621 	fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
622 				&fbi->fb_start_dma, GFP_KERNEL);
623 	if (fbi->fb_start == NULL) {
624 		dev_err(&pdev->dev, "can't alloc framebuffer\n");
625 		ret = -ENOMEM;
626 		goto failed_destroy_mutex;
627 	}
628 	memset(fbi->fb_start, 0, fbi->fb_size);
629 	dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
630 
631 	/* fb power on */
632 	if (modes_num > 0)
633 		mmpfb_power(fbi, 1);
634 
635 	ret = fb_info_setup(info, fbi);
636 	if (ret < 0)
637 		goto failed_free_buff;
638 
639 	ret = register_framebuffer(info);
640 	if (ret < 0) {
641 		dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
642 		ret = -ENXIO;
643 		goto failed_clear_info;
644 	}
645 
646 	dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
647 		info->node, info->fix.id);
648 
649 #ifdef CONFIG_LOGO
650 	if (fbi->fb_start) {
651 		fb_prepare_logo(info, 0);
652 		fb_show_logo(info, 0);
653 	}
654 #endif
655 
656 	return 0;
657 
658 failed_clear_info:
659 	fb_info_clear(info);
660 failed_free_buff:
661 	dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
662 		fbi->fb_start_dma);
663 failed_destroy_mutex:
664 	mutex_destroy(&fbi->access_ok);
665 	dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
666 
667 	framebuffer_release(info);
668 
669 	return ret;
670 }
671 
672 static struct platform_driver mmpfb_driver = {
673 	.driver		= {
674 		.name	= "mmp-fb",
675 	},
676 	.probe		= mmpfb_probe,
677 };
678 
679 static int mmpfb_init(void)
680 {
681 	return platform_driver_register(&mmpfb_driver);
682 }
683 module_init(mmpfb_init);
684 
685 MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
686 MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
687 MODULE_LICENSE("GPL");
688