Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / video / fbdev / mmp / fb / mmpfb.c
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 = 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
516 static 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
549 static void fb_info_clear(struct fb_info *info)
550 {
551         fb_dealloc_cmap(&info->cmap);
552 }
553
554 static int mmpfb_probe(struct platform_device *pdev)
555 {
556         struct mmp_buffer_driver_mach_info *mi;
557         struct fb_info *info;
558         struct mmpfb_info *fbi;
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
573         /* init fb */
574         fbi->fb_info = info;
575         platform_set_drvdata(pdev, fbi);
576         fbi->dev = &pdev->dev;
577         fbi->name = mi->name;
578         fbi->pix_fmt = mi->default_pixfmt;
579         pixfmt_to_var(&info->var, fbi->pix_fmt);
580         mutex_init(&fbi->access_ok);
581
582         /* get display path by name */
583         fbi->path = mmp_get_path(mi->path_name);
584         if (!fbi->path) {
585                 dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
586                 ret = -EINVAL;
587                 goto failed_destroy_mutex;
588         }
589
590         dev_info(fbi->dev, "path %s get\n", fbi->path->name);
591
592         /* get overlay */
593         fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
594         if (!fbi->overlay) {
595                 ret = -EINVAL;
596                 goto failed_destroy_mutex;
597         }
598         /* set fetch used */
599         mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
600
601         modes_num = modes_setup(fbi);
602         if (modes_num < 0) {
603                 ret = modes_num;
604                 goto failed_destroy_mutex;
605         }
606
607         /*
608          * if get modes success, means not hotplug panels, use caculated buffer
609          * or use default size
610          */
611         if (modes_num > 0) {
612                 /* fix to 2* yres */
613                 info->var.yres_virtual = info->var.yres * 2;
614
615                 /* Allocate framebuffer memory: size = modes xy *4 */
616                 fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
617                                 * info->var.bits_per_pixel / 8;
618         } else {
619                 fbi->fb_size = MMPFB_DEFAULT_SIZE;
620         }
621
622         fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
623                                 &fbi->fb_start_dma, GFP_KERNEL);
624         if (fbi->fb_start == NULL) {
625                 dev_err(&pdev->dev, "can't alloc framebuffer\n");
626                 ret = -ENOMEM;
627                 goto failed_destroy_mutex;
628         }
629         memset(fbi->fb_start, 0, fbi->fb_size);
630         dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
631
632         /* fb power on */
633         if (modes_num > 0)
634                 mmpfb_power(fbi, 1);
635
636         ret = fb_info_setup(info, fbi);
637         if (ret < 0)
638                 goto failed_free_buff;
639
640         ret = register_framebuffer(info);
641         if (ret < 0) {
642                 dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
643                 ret = -ENXIO;
644                 goto failed_clear_info;
645         }
646
647         dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
648                 info->node, info->fix.id);
649
650 #ifdef CONFIG_LOGO
651         if (fbi->fb_start) {
652                 fb_prepare_logo(info, 0);
653                 fb_show_logo(info, 0);
654         }
655 #endif
656
657         return 0;
658
659 failed_clear_info:
660         fb_info_clear(info);
661 failed_free_buff:
662         dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
663                 fbi->fb_start_dma);
664 failed_destroy_mutex:
665         mutex_destroy(&fbi->access_ok);
666         dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
667
668         framebuffer_release(info);
669
670         return ret;
671 }
672
673 static struct platform_driver mmpfb_driver = {
674         .driver         = {
675                 .name   = "mmp-fb",
676         },
677         .probe          = mmpfb_probe,
678 };
679
680 static int mmpfb_init(void)
681 {
682         return platform_driver_register(&mmpfb_driver);
683 }
684 module_init(mmpfb_init);
685
686 MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
687 MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
688 MODULE_LICENSE("GPL");