2 * linux/drivers/video/mmp/fb/mmpfb.c
3 * Framebuffer driver for Marvell Display controller.
5 * Copyright (C) 2012 Marvell Technology Group Ltd.
6 * Authors: Zhou Zhu <zzhu3@marvell.com>
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.
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
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/>.
22 #include <linux/module.h>
23 #include <linux/dma-mapping.h>
24 #include <linux/platform_device.h>
27 static int var_to_pixfmt(struct fb_var_screeninfo
*var
)
32 if (var
->bits_per_pixel
== 8)
33 return PIXFMT_PSEUDOCOLOR
;
36 * Check for YUV422PLANAR.
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
;
43 return PIXFMT_YVU422P
;
47 * Check for YUV420PLANAR.
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
;
54 return PIXFMT_YVU420P
;
58 * Check for YUV422PACK.
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)
64 else if (var
->green
.offset
>= var
->blue
.offset
)
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
)
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
;
92 return PIXFMT_BGR888PACK
;
95 if (var
->bits_per_pixel
== 32 && var
->transp
.offset
== 24) {
96 if (var
->red
.offset
>= var
->blue
.offset
)
97 return PIXFMT_RGBA888
;
99 return PIXFMT_BGRA888
;
101 if (var
->red
.offset
>= var
->blue
.offset
)
102 return PIXFMT_RGB888UNPACK
;
104 return PIXFMT_BGR888UNPACK
;
113 static void pixfmt_to_var(struct fb_var_screeninfo
*var
, int pix_fmt
)
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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
238 static void fbmode_to_mmpmode(struct mmp_mode
*mode
,
239 struct fb_videomode
*videomode
, int output_fmt
)
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
;
247 do_div(div_result
, videomode
->pixclock
);
248 mode
->pixclock_freq
= (u32
)div_result
;
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
;
263 static void mmpmode_to_fbmode(struct fb_videomode
*videomode
,
264 struct mmp_mode
*mode
)
266 u64 div_result
= 1000000000000ll;
268 videomode
->name
= mode
->name
;
269 videomode
->refresh
= mode
->refresh
;
270 videomode
->xres
= mode
->xres
;
271 videomode
->yres
= mode
->yres
;
273 do_div(div_result
, mode
->pixclock_freq
);
274 videomode
->pixclock
= (u32
)div_result
;
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;
287 static int mmpfb_check_var(struct fb_var_screeninfo
*var
,
288 struct fb_info
*info
)
290 struct mmpfb_info
*fbi
= info
->par
;
292 if (var
->bits_per_pixel
== 8)
295 * Basic geometry sanity checks.
297 if (var
->xoffset
+ var
->xres
> var
->xres_virtual
)
299 if (var
->yoffset
+ var
->yres
> var
->yres_virtual
)
303 * Check size of framebuffer.
305 if (var
->xres_virtual
* var
->yres_virtual
*
306 (var
->bits_per_pixel
>> 3) > fbi
->fb_size
)
312 static unsigned int chan_to_field(unsigned int chan
, struct fb_bitfield
*bf
)
314 return ((chan
& 0xffff) >> (16 - bf
->length
)) << bf
->offset
;
317 static u32
to_rgb(u16 red
, u16 green
, u16 blue
)
323 return (red
<< 16) | (green
<< 8) | blue
;
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
)
330 struct mmpfb_info
*fbi
= info
->par
;
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
;
340 if (info
->fix
.visual
== FB_VISUAL_PSEUDOCOLOR
&& regno
< 256) {
341 val
= to_rgb(red
, green
, blue
);
348 static int mmpfb_pan_display(struct fb_var_screeninfo
*var
,
349 struct fb_info
*info
)
351 struct mmpfb_info
*fbi
= info
->par
;
352 struct mmp_addr addr
;
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
);
362 static int var_update(struct fb_info
*info
)
364 struct mmpfb_info
*fbi
= info
->par
;
365 struct fb_var_screeninfo
*var
= &info
->var
;
366 struct fb_videomode
*m
;
370 pix_fmt
= var_to_pixfmt(var
);
373 pixfmt_to_var(var
, pix_fmt
);
374 fbi
->pix_fmt
= pix_fmt
;
376 /* set var according to best video mode*/
377 m
= (struct fb_videomode
*)fb_match_mode(var
, &info
->modelist
);
379 dev_err(fbi
->dev
, "set par: no match mode, use best mode\n");
380 m
= (struct fb_videomode
*)fb_find_best_mode(var
,
382 fb_videomode_to_var(var
, m
);
384 memcpy(&fbi
->mode
, m
, sizeof(struct fb_videomode
));
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
;
395 static void mmpfb_set_win(struct fb_info
*info
)
397 struct mmpfb_info
*fbi
= info
->par
;
398 struct fb_var_screeninfo
*var
= &info
->var
;
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
);
413 static int mmpfb_set_par(struct fb_info
*info
)
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
;
421 ret
= var_update(info
);
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
);
429 /* set window related info */
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
);
441 static void mmpfb_power(struct mmpfb_info
*fbi
, int power
)
443 struct mmp_addr addr
;
444 struct fb_var_screeninfo
*var
= &fbi
->fb_info
->var
;
446 /* for power on, always set address/window again */
448 /* set window related info */
449 mmpfb_set_win(fbi
->fb_info
);
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
);
458 mmp_overlay_set_onoff(fbi
->overlay
, power
);
461 static int mmpfb_blank(int blank
, struct fb_info
*info
)
463 struct mmpfb_info
*fbi
= info
->par
;
465 mmpfb_power(fbi
, (blank
== FB_BLANK_UNBLANK
));
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
,
482 static int modes_setup(struct mmpfb_info
*fbi
)
484 struct fb_videomode
*videomodes
;
485 struct mmp_mode
*mmp_modes
;
486 struct fb_info
*info
= fbi
->fb_info
;
487 int videomode_num
, i
;
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");
495 /* put videomode list to info structure */
496 videomodes
= kzalloc(sizeof(struct fb_videomode
) * videomode_num
,
499 dev_err(fbi
->dev
, "can't malloc video modes\n");
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
);
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]);
513 return videomode_num
;
516 static int fb_info_setup(struct fb_info
*info
,
517 struct mmpfb_info
*fbi
)
520 /* Initialise static fb parameters.*/
521 info
->flags
= FBINFO_DEFAULT
| FBINFO_PARTIAL_PAN_OK
|
522 FBINFO_HWACCEL_XPAN
| FBINFO_HWACCEL_YPAN
;
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
;
542 /* For FB framework: Allocate color map and Register framebuffer*/
543 if (fb_alloc_cmap(&info
->cmap
, 256, 0) < 0)
549 static void fb_info_clear(struct fb_info
*info
)
551 fb_dealloc_cmap(&info
->cmap
);
554 static int mmpfb_probe(struct platform_device
*pdev
)
556 struct mmp_buffer_driver_mach_info
*mi
;
557 struct fb_info
*info
;
558 struct mmpfb_info
*fbi
;
561 mi
= pdev
->dev
.platform_data
;
563 dev_err(&pdev
->dev
, "no platform data defined\n");
568 info
= framebuffer_alloc(sizeof(struct mmpfb_info
), &pdev
->dev
);
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
);
582 /* get display path by name */
583 fbi
->path
= mmp_get_path(mi
->path_name
);
585 dev_err(&pdev
->dev
, "can't get the path %s\n", mi
->path_name
);
587 goto failed_destroy_mutex
;
590 dev_info(fbi
->dev
, "path %s get\n", fbi
->path
->name
);
593 fbi
->overlay
= mmp_path_get_overlay(fbi
->path
, mi
->overlay_id
);
596 goto failed_destroy_mutex
;
599 mmp_overlay_set_fetch(fbi
->overlay
, mi
->dmafetch_id
);
601 modes_num
= modes_setup(fbi
);
604 goto failed_destroy_mutex
;
608 * if get modes success, means not hotplug panels, use caculated buffer
609 * or use default size
613 info
->var
.yres_virtual
= info
->var
.yres
* 2;
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;
619 fbi
->fb_size
= MMPFB_DEFAULT_SIZE
;
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");
627 goto failed_destroy_mutex
;
629 memset(fbi
->fb_start
, 0, fbi
->fb_size
);
630 dev_info(fbi
->dev
, "fb %dk allocated\n", fbi
->fb_size
/1024);
636 ret
= fb_info_setup(info
, fbi
);
638 goto failed_free_buff
;
640 ret
= register_framebuffer(info
);
642 dev_err(&pdev
->dev
, "Failed to register fb: %d\n", ret
);
644 goto failed_clear_info
;
647 dev_info(fbi
->dev
, "loaded to /dev/fb%d <%s>.\n",
648 info
->node
, info
->fix
.id
);
652 fb_prepare_logo(info
, 0);
653 fb_show_logo(info
, 0);
662 dma_free_coherent(&pdev
->dev
, PAGE_ALIGN(fbi
->fb_size
), fbi
->fb_start
,
664 failed_destroy_mutex
:
665 mutex_destroy(&fbi
->access_ok
);
666 dev_err(fbi
->dev
, "mmp-fb: frame buffer device init failed\n");
668 framebuffer_release(info
);
673 static struct platform_driver mmpfb_driver
= {
677 .probe
= mmpfb_probe
,
680 static int mmpfb_init(void)
682 return platform_driver_register(&mmpfb_driver
);
684 module_init(mmpfb_init
);
686 MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
687 MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
688 MODULE_LICENSE("GPL");