f34a3a907f1b5b24bd5214df09c1eeec4215e828
[~shefty/rdma-dev.git] / drivers / video / 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 "mmpfb.h"
25
26 static 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
112 static 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  */
237 static 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
262 static 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
286 static 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
311 static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
312 {
313         return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
314 }
315
316 static 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
325 static 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
347 static 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
361 static 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
394 static 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
426 static 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
450 static 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
459 static 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
471 static 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
505 static 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
538 static void fb_info_clear(struct fb_info *info)
539 {
540         fb_dealloc_cmap(&info->cmap);
541 }
542
543 static 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
652 failed_clear_info:
653         fb_info_clear(info);
654 failed_free_buff:
655         dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
656                 fbi->fb_start_dma);
657 failed_destroy_mutex:
658         mutex_destroy(&fbi->access_ok);
659 failed:
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
668 static struct platform_driver mmpfb_driver = {
669         .driver         = {
670                 .name   = "mmp-fb",
671                 .owner  = THIS_MODULE,
672         },
673         .probe          = mmpfb_probe,
674 };
675
676 static int mmpfb_init(void)
677 {
678         return platform_driver_register(&mmpfb_driver);
679 }
680 module_init(mmpfb_init);
681
682 MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
683 MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
684 MODULE_LICENSE("GPL");