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