1 /* linux/drivers/media/video/samsung/tvout/s5p_tvout_fb.c
3 * Copyright (c) 2009 Samsung Electronics
4 * http://www.samsung.com/
6 * Frame buffer ftn. file for Samsung TVOUT driver
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
14 #include <linux/dma-mapping.h>
15 #include <linux/uaccess.h>
17 #include "s5p_tvout_common_lib.h"
18 #include "s5p_tvout_ctrl.h"
19 #include "s5p_tvout_v4l2.h"
21 #ifdef CONFIG_UMP_VCM_ALLOC
22 #include "ump_kernel_interface.h"
23 #include "ump_kernel_interface_ref_drv.h"
24 #include "ump_kernel_interface_vcm.h"
27 #define S5PTVFB_NAME "s5ptvfb"
29 #define S5PTV_FB_LAYER0_MINOR 10
30 #define S5PTV_FB_LAYER1_MINOR 11
32 #define FB_INDEX(id) (id - S5PTV_FB_LAYER0_MINOR)
34 #define S5PTVFB_CHROMA(r, g, b) \
35 (((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b & 0xff) << 0))
37 #define S5PTVFB_WIN_POSITION \
38 _IOW('F', 213, struct s5ptvfb_user_window)
39 #define S5PTVFB_WIN_SET_PLANE_ALPHA \
40 _IOW('F', 214, struct s5ptvfb_user_plane_alpha)
41 #define S5PTVFB_WIN_SET_CHROMA \
42 _IOW('F', 215, struct s5ptvfb_user_chroma)
43 #define S5PTVFB_WIN_SET_ADDR \
45 #define S5PTVFB_SCALING \
46 _IOW('F', 222, struct s5ptvfb_user_scaling)
48 struct s5ptvfb_window {
50 struct device *dev_fb;
55 enum s5ptvfb_data_path_t path;
58 unsigned int pseudo_pal[16];
59 struct s5ptvfb_alpha alpha;
60 struct s5ptvfb_chroma chroma;
61 int (*suspend_fifo)(void);
62 int (*resume_fifo)(void);
65 struct s5ptvfb_lcd_timing {
76 struct s5ptvfb_lcd_polarity {
88 struct s5ptvfb_lcd_timing timing;
89 struct s5ptvfb_lcd_polarity polarity;
91 void (*init_ldi)(void);
94 static struct mutex fb_lock;
96 static struct fb_info *fb[S5PTV_FB_CNT];
97 static struct s5ptvfb_lcd lcd = {
122 static inline unsigned int s5p_tvout_fb_chan_to_field(unsigned int chan,
123 struct fb_bitfield bf)
126 chan >>= 16 - bf.length;
128 return chan << bf.offset;
131 static int s5p_tvout_fb_set_alpha_info(struct fb_var_screeninfo *var,
132 struct s5ptvfb_window *win)
134 if (var->transp.length > 0)
135 win->alpha.mode = PIXEL_BLENDING;
137 win->alpha.mode = NONE_BLENDING;
141 #if 0 /* This function will be used in the future */
142 static int s5p_tvout_fb_map_video_memory(int id)
144 enum s5p_mixer_layer layer;
145 struct s5ptvfb_window *win = fb[FB_INDEX(id)]->par;
146 struct fb_fix_screeninfo *fix = &fb[FB_INDEX(id)]->fix;
148 if (win->path == DATA_PATH_FIFO)
151 fb[FB_INDEX(id)]->screen_base = dma_alloc_writecombine(win->dev_fb,
152 PAGE_ALIGN(fix->smem_len),
153 (unsigned int *) &fix->smem_start, GFP_KERNEL);
156 case S5PTV_FB_LAYER0_MINOR:
157 layer = MIXER_GPR0_LAYER;
159 case S5PTV_FB_LAYER1_MINOR:
160 layer = MIXER_GPR1_LAYER;
163 tvout_err("invalid layer\n");
166 s5p_mixer_ctrl_init_fb_addr_phy(layer, fix->smem_start);
168 if (!fb[FB_INDEX(id)]->screen_base)
171 tvout_dbg("[fb%d] dma: 0x%08x, cpu: 0x%08x,size: 0x%08x\n",
172 win->id, (unsigned int) fix->smem_start,
173 (unsigned int) fb[FB_INDEX(id)]->screen_base,
176 memset(fb[FB_INDEX(id)]->screen_base, 0, fix->smem_len);
181 static int s5p_tvout_fb_set_bitfield(struct fb_var_screeninfo *var)
183 switch (var->bits_per_pixel) {
185 if (var->transp.length == 1) {
186 var->red.offset = 10;
188 var->green.offset = 5;
189 var->green.length = 5;
190 var->blue.offset = 0;
191 var->blue.length = 5;
192 var->transp.offset = 15;
193 } else if (var->transp.length == 4) {
196 var->green.offset = 4;
197 var->green.length = 4;
198 var->blue.offset = 0;
199 var->blue.length = 4;
200 var->transp.offset = 12;
202 var->red.offset = 11;
204 var->green.offset = 5;
205 var->green.length = 6;
206 var->blue.offset = 0;
207 var->blue.length = 5;
208 var->transp.offset = 0;
213 var->red.offset = 16;
215 var->green.offset = 8;
216 var->green.length = 8;
217 var->blue.offset = 0;
218 var->blue.length = 8;
219 var->transp.offset = 0;
220 var->transp.length = 0;
224 var->red.offset = 16;
226 var->green.offset = 8;
227 var->green.length = 8;
228 var->blue.offset = 0;
229 var->blue.length = 8;
230 var->transp.offset = 24;
237 static int s5p_tvout_fb_setcolreg(unsigned int regno, unsigned int red,
238 unsigned int green, unsigned int blue,
239 unsigned int transp, struct fb_info *fb)
241 unsigned int *pal = (unsigned int *) fb->pseudo_palette;
242 unsigned int val = 0;
245 /* fake palette of 16 colors */
246 val |= s5p_tvout_fb_chan_to_field(red, fb->var.red);
247 val |= s5p_tvout_fb_chan_to_field(green, fb->var.green);
248 val |= s5p_tvout_fb_chan_to_field(blue, fb->var.blue);
249 val |= s5p_tvout_fb_chan_to_field(transp, fb->var.transp);
257 static int s5p_tvout_fb_pan_display(struct fb_var_screeninfo *var,
260 dma_addr_t start_addr;
261 enum s5p_mixer_layer layer;
262 struct fb_fix_screeninfo *fix = &fb->fix;
264 if (var->yoffset + var->yres > var->yres_virtual) {
265 tvout_err("invalid y offset value\n");
269 fb->var.yoffset = var->yoffset;
272 case S5PTV_FB_LAYER0_MINOR:
273 layer = MIXER_GPR0_LAYER;
275 case S5PTV_FB_LAYER1_MINOR:
276 layer = MIXER_GPR1_LAYER;
279 tvout_err("invalid layer\n");
283 start_addr = fix->smem_start + (var->xres_virtual *
284 (var->bits_per_pixel / 8) * var->yoffset);
286 s5p_mixer_ctrl_set_buffer_address(layer, start_addr);
291 static int s5p_tvout_fb_blank(int blank_mode, struct fb_info *fb)
293 enum s5p_mixer_layer layer = MIXER_GPR0_LAYER;
295 tvout_dbg("change blank mode\n");
298 case S5PTV_FB_LAYER0_MINOR:
299 layer = MIXER_GPR0_LAYER;
301 case S5PTV_FB_LAYER1_MINOR:
302 layer = MIXER_GPR1_LAYER;
305 tvout_err("not supported layer\n");
309 switch (blank_mode) {
310 case FB_BLANK_UNBLANK:
311 if (fb->fix.smem_start)
312 s5p_mixer_ctrl_enable_layer(layer);
314 tvout_dbg("[fb%d] no alloc memory for unblank\n",
318 case FB_BLANK_POWERDOWN:
319 s5p_mixer_ctrl_disable_layer(layer);
323 tvout_err("not supported blank mode\n");
330 static int s5p_tvout_fb_set_par(struct fb_info *fb)
333 u32 src_x, src_y, w, h;
334 struct s5ptvfb_window *win = fb->par;
335 enum s5p_mixer_layer layer = MIXER_GPR0_LAYER;
337 tvout_dbg("[fb%d] set_par\n", win->id);
339 if (!fb->fix.smem_start) {
340 #ifndef CONFIG_USER_ALLOC_TVOUT
341 printk(KERN_INFO " The frame buffer is allocated here\n");
342 s5p_tvout_fb_map_video_memory(win->id);
345 "[Warning] The frame buffer should be allocated by ioctl\n");
349 bpp = fb->var.bits_per_pixel;
350 trans_len = fb->var.transp.length;
353 src_x = fb->var.xoffset;
354 src_y = fb->var.yoffset;
357 case S5PTV_FB_LAYER0_MINOR:
358 layer = MIXER_GPR0_LAYER;
360 case S5PTV_FB_LAYER1_MINOR:
361 layer = MIXER_GPR1_LAYER;
365 s5p_mixer_ctrl_init_grp_layer(layer);
366 s5p_mixer_ctrl_set_pixel_format(layer, bpp, trans_len);
367 s5p_mixer_ctrl_set_src_win_pos(layer, src_x, src_y, w, h);
368 s5p_mixer_ctrl_set_alpha_blending(layer, win->alpha.mode,
373 static int s5p_tvout_fb_check_var(struct fb_var_screeninfo *var,
376 struct fb_fix_screeninfo *fix = &fb->fix;
377 struct s5ptvfb_window *win = fb->par;
379 tvout_dbg("[fb%d] check_var\n", win->id);
381 if (var->bits_per_pixel != 16 && var->bits_per_pixel != 24 &&
382 var->bits_per_pixel != 32) {
383 tvout_err("invalid bits per pixel\n");
387 if (var->xres > lcd.width)
388 var->xres = lcd.width;
390 if (var->yres > lcd.height)
391 var->yres = lcd.height;
393 if (var->xres_virtual != var->xres)
394 var->xres_virtual = var->xres;
396 if (var->yres_virtual > var->yres * (fb->fix.ypanstep + 1))
397 var->yres_virtual = var->yres * (fb->fix.ypanstep + 1);
399 if (var->xoffset != 0)
402 if (var->yoffset + var->yres > var->yres_virtual)
403 var->yoffset = var->yres_virtual - var->yres;
405 if (win->x + var->xres > lcd.width)
406 win->x = lcd.width - var->xres;
408 if (win->y + var->yres > lcd.height)
409 win->y = lcd.height - var->yres;
411 /* modify the fix info */
412 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
413 fix->smem_len = fix->line_length * var->yres_virtual;
415 s5p_tvout_fb_set_bitfield(var);
416 s5p_tvout_fb_set_alpha_info(var, win);
421 static int s5p_tvout_fb_release(struct fb_info *fb, int user)
423 struct s5ptvfb_window *win = fb->par;
425 atomic_dec(&win->in_use);
430 static int s5p_tvout_fb_ioctl(struct fb_info *fb, unsigned int cmd,
433 dma_addr_t start_addr;
434 enum s5p_mixer_layer layer;
435 struct fb_var_screeninfo *var = &fb->var;
436 struct s5ptvfb_window *win = fb->par;
438 void *argp = (void *) arg;
439 #if defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_UMP_VCM_ALLOC)
440 struct vcm_res *vcm_res;
444 struct s5ptvfb_user_window user_window;
445 struct s5ptvfb_user_plane_alpha user_alpha;
446 struct s5ptvfb_user_chroma user_chroma;
447 struct s5ptvfb_user_scaling user_scaling;
452 case S5PTV_FB_LAYER0_MINOR:
453 layer = MIXER_GPR0_LAYER;
455 case S5PTV_FB_LAYER1_MINOR:
456 layer = MIXER_GPR1_LAYER;
459 printk(KERN_ERR "[Error] invalid layer\n");
464 case S5PTVFB_WIN_POSITION:
465 if (copy_from_user(&p.user_window,
466 (struct s5ptvfb_user_window __user *) arg,
467 sizeof(p.user_window)))
470 s5p_mixer_ctrl_set_dst_win_pos(layer, p.user_window.x,
471 p.user_window.y, var->xres, var->yres);
475 case S5PTVFB_WIN_SET_PLANE_ALPHA:
476 if (copy_from_user(&p.user_alpha,
477 (struct s5ptvfb_user_plane_alpha __user *) arg,
478 sizeof(p.user_alpha)))
481 win->alpha.mode = LAYER_BLENDING;
482 win->alpha.value = p.user_alpha.alpha;
483 s5p_mixer_ctrl_set_alpha_blending(layer,
484 win->alpha.mode, win->alpha.value);
487 case S5PTVFB_WIN_SET_CHROMA:
488 if (copy_from_user(&p.user_chroma,
489 (struct s5ptvfb_user_chroma __user *) arg,
490 sizeof(p.user_chroma)))
493 win->chroma.enabled = p.user_chroma.enabled;
494 win->chroma.key = S5PTVFB_CHROMA(p.user_chroma.red,
498 s5p_mixer_ctrl_set_chroma_key(layer, win->chroma);
502 case S5PTVFB_WIN_SET_ADDR:
503 #if defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_UMP_VCM_ALLOC)
504 vcm_res = (struct vcm_res *)
505 ump_dd_meminfo_get((unsigned int)argp, (void*)VCM_DEV_TV);
507 fb->fix.smem_start = vcm_res->start;
511 fb->fix.smem_start = (unsigned long)argp;
513 start_addr = fb->fix.smem_start + (var->xres_virtual *
514 (var->bits_per_pixel / 8) * var->yoffset);
516 s5p_mixer_ctrl_set_buffer_address(layer, start_addr);
518 case S5PTVFB_SCALING:
519 if (copy_from_user(&p.user_scaling,
520 (struct s5ptvfb_user_scaling __user *) arg,
521 sizeof(p.user_scaling)))
524 s5p_mixer_ctrl_scaling(layer, p.user_scaling);
531 static int s5p_tvout_fb_open(struct fb_info *fb, int user)
533 struct s5ptvfb_window *win = fb->par;
536 mutex_lock(&fb_lock);
538 if (atomic_read(&win->in_use)) {
539 tvout_dbg("do not allow multiple open for tvout framebuffer\n");
542 atomic_inc(&win->in_use);
544 mutex_unlock(&fb_lock);
549 struct fb_ops s5ptvfb_ops = {
550 .owner = THIS_MODULE,
551 .fb_fillrect = cfb_fillrect,
552 .fb_copyarea = cfb_copyarea,
553 .fb_imageblit = cfb_imageblit,
554 .fb_check_var = s5p_tvout_fb_check_var,
555 .fb_set_par = s5p_tvout_fb_set_par,
556 .fb_blank = s5p_tvout_fb_blank,
557 .fb_pan_display = s5p_tvout_fb_pan_display,
558 .fb_setcolreg = s5p_tvout_fb_setcolreg,
559 .fb_ioctl = s5p_tvout_fb_ioctl,
560 .fb_open = s5p_tvout_fb_open,
561 .fb_release = s5p_tvout_fb_release,
564 static int s5p_tvout_fb_init_fbinfo(int id, struct device *dev_fb)
566 struct fb_fix_screeninfo *fix = &fb[FB_INDEX(id)]->fix;
567 struct fb_var_screeninfo *var = &fb[FB_INDEX(id)]->var;
568 struct s5ptvfb_window *win = fb[FB_INDEX(id)]->par;
569 struct s5ptvfb_alpha *alpha = &win->alpha;
571 memset(win, 0, sizeof(struct s5ptvfb_window));
573 platform_set_drvdata(to_platform_device(dev_fb), fb[FB_INDEX(id)]);
575 strcpy(fix->id, S5PTVFB_NAME);
579 win->path = DATA_PATH_DMA;
581 win->dev_fb = dev_fb;
582 alpha->mode = LAYER_BLENDING;
586 fb[FB_INDEX(id)]->fbops = &s5ptvfb_ops;
587 fb[FB_INDEX(id)]->flags = FBINFO_FLAG_DEFAULT;
588 fb[FB_INDEX(id)]->pseudo_palette = &win->pseudo_pal;
591 fix->type = FB_TYPE_PACKED_PIXELS;
592 fix->accel = FB_ACCEL_NONE;
593 fix->visual = FB_VISUAL_TRUECOLOR;
594 var->xres = lcd.width;
595 var->yres = lcd.height;
596 var->xres_virtual = var->xres;
597 var->yres_virtual = var->yres + (var->yres * fix->ypanstep);
598 var->bits_per_pixel = 32;
603 var->transp.length = 0;
605 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
606 fix->smem_len = fix->line_length * var->yres_virtual;
609 var->activate = FB_ACTIVATE_NOW;
610 var->vmode = FB_VMODE_NONINTERLACED;
611 var->hsync_len = lcd.timing.h_sw;
612 var->vsync_len = lcd.timing.v_sw;
613 var->left_margin = lcd.timing.h_fp;
614 var->right_margin = lcd.timing.h_bp;
615 var->upper_margin = lcd.timing.v_fp;
616 var->lower_margin = lcd.timing.v_bp;
618 var->pixclock = lcd.freq * (var->left_margin + var->right_margin +
619 var->hsync_len + var->xres) *
620 (var->upper_margin + var->lower_margin +
621 var->vsync_len + var->yres);
623 tvout_dbg("pixclock: %d\n", var->pixclock);
625 s5p_tvout_fb_set_bitfield(var);
626 s5p_tvout_fb_set_alpha_info(var, win);
628 mutex_init(&fb_lock);
633 int s5p_tvout_fb_alloc_framebuffer(struct device *dev_fb)
637 /* alloc for each framebuffer */
638 for (i = 0; i < S5PTV_FB_CNT; i++) {
639 fb[i] = framebuffer_alloc(sizeof(struct s5ptvfb_window),
642 tvout_err("not enough memory\n");
647 ret = s5p_tvout_fb_init_fbinfo(i + S5PTV_FB_LAYER0_MINOR,
650 tvout_err("fail to allocate memory for tv fb\n");
655 #ifndef CONFIG_USER_ALLOC_TVOUT
656 if (s5p_tvout_fb_map_video_memory(i + S5PTV_FB_LAYER0_MINOR)) {
657 tvout_err("fail to map video mem for default window\n");
667 for (i = 0; i < S5PTV_FB_CNT; i++) {
669 framebuffer_release(fb[i]);
675 int s5p_tvout_fb_register_framebuffer(struct device *dev_fb)
680 ret = register_framebuffer(fb[0]);
682 tvout_err("fail to register framebuffer device\n");
685 } while (fb[0]->node < S5PTV_FB_LAYER0_MINOR);
687 for (i = 1; i < S5PTV_FB_CNT; i++) {
688 ret = register_framebuffer(fb[i]);
690 tvout_err("fail to register framebuffer device\n");
695 for (i = 0; i < S5PTV_FB_CNT; i++)
696 tvout_dbg("fb[%d] = %d\n", i, fb[i]->node);
698 for (i = 0; i < S5PTV_FB_CNT; i++) {
699 #ifndef CONFIG_FRAMEBUFFER_CONSOLE
700 #ifndef CONFIG_USER_ALLOC_TVOUT
701 s5p_tvout_fb_check_var(&fb[i]->var, fb[i]);
702 s5p_tvout_fb_set_par(fb[i]);