| Index: drivers/media/video/samsung/tv20/s5p_stda_grp.c
|
| diff --git a/drivers/media/video/samsung/tv20/s5p_stda_grp.c b/drivers/media/video/samsung/tv20/s5p_stda_grp.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..dbc892a0aafbff1a003f0d4497e8a212a769d577
|
| --- /dev/null
|
| +++ b/drivers/media/video/samsung/tv20/s5p_stda_grp.c
|
| @@ -0,0 +1,1071 @@
|
| +/* linux/drivers/media/video/samsung/tv20/s5p_stda_grp.c
|
| +*
|
| +* Copyright (c) 2010 Samsung Electronics Co., Ltd.
|
| +* http://www.samsung.com/
|
| +*
|
| +* S5PV210 - Graphic Layer ftn. file for Samsung TVOut driver
|
| +*
|
| +* This program is free software; you can redistribute it and/or modify
|
| +* it under the terms of the GNU General Public License version 2 as
|
| +* published by the Free Software Foundation.
|
| +*/
|
| +
|
| +#include <linux/module.h>
|
| +#include <linux/kernel.h>
|
| +#include <linux/stddef.h>
|
| +#include <linux/ioctl.h>
|
| +#include <linux/dma-mapping.h>
|
| +
|
| +#include <linux/io.h>
|
| +#include <linux/uaccess.h>
|
| +
|
| +#include "s5p_tv.h"
|
| +
|
| +#ifdef CONFIG_TVOUT_DBG
|
| +#define S5P_GRP_DEBUG 1
|
| +#endif
|
| +
|
| +#ifdef S5P_GRP_DEBUG
|
| +#define GRPPRINTK(fmt, args...) \
|
| + printk(KERN_INFO "\t[STDA_GRP] %s: " fmt, __func__ , ## args)
|
| +#else
|
| +#define GRPPRINTK(fmt, args...)
|
| +#endif
|
| +
|
| +bool tv_grp_start(enum s5p_tv_vmx_layer vm_layer)
|
| +{
|
| + enum s5p_tv_vmx_err merr;
|
| + struct s5p_tv_status *st = &s5ptv_status;
|
| +
|
| + if (!(st->grp_layer_enable[0] || st->grp_layer_enable[1])) {
|
| +
|
| + merr = tv_vm_init_status_reg(st->grp_burst,
|
| + st->grp_endian);
|
| +
|
| + if (merr != VMIXER_NO_ERROR)
|
| + return false;
|
| + }
|
| +
|
| + merr = tv_vm_init_layer(s5ptv_status.tvout_param.disp_mode,
|
| + vm_layer,
|
| + true,
|
| + s5ptv_overlay[vm_layer].win_blending,
|
| + s5ptv_overlay[vm_layer].win.global_alpha,
|
| + s5ptv_overlay[vm_layer].priority,
|
| + s5ptv_overlay[vm_layer].fb.fmt.pixelformat,
|
| + s5ptv_overlay[vm_layer].blank_change,
|
| + s5ptv_overlay[vm_layer].pixel_blending,
|
| + s5ptv_overlay[vm_layer].pre_mul,
|
| + s5ptv_overlay[vm_layer].blank_color,
|
| + s5ptv_overlay[vm_layer].base_addr,
|
| + s5ptv_overlay[vm_layer].fb.fmt.bytesperline,
|
| + s5ptv_overlay[vm_layer].win.w.width,
|
| + s5ptv_overlay[vm_layer].win.w.height,
|
| + s5ptv_overlay[vm_layer].win.w.left,
|
| + s5ptv_overlay[vm_layer].win.w.top,
|
| + s5ptv_overlay[vm_layer].dst_rect.left,
|
| + s5ptv_overlay[vm_layer].dst_rect.top,
|
| + s5ptv_overlay[vm_layer].dst_rect.width,
|
| + s5ptv_overlay[vm_layer].dst_rect.height);
|
| +
|
| + if (merr != VMIXER_NO_ERROR) {
|
| + GRPPRINTK("can't initialize layer(%d)\n\r", merr);
|
| + return false;
|
| + }
|
| +
|
| + tv_vm_start();
|
| +
|
| +
|
| + st->grp_layer_enable[vm_layer] = true;
|
| +
|
| + GRPPRINTK("()\n\r");
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool tv_grp_stop(enum s5p_tv_vmx_layer vm_layer)
|
| +{
|
| + enum s5p_tv_vmx_err merr;
|
| + struct s5p_tv_status *st = &s5ptv_status;
|
| +
|
| + GRPPRINTK("()\n\r");
|
| +
|
| + merr = tv_vm_set_layer_show(vm_layer, false);
|
| +
|
| + if (merr != VMIXER_NO_ERROR)
|
| + return false;
|
| +
|
| + merr = tv_vm_set_layer_priority(vm_layer, 0);
|
| +
|
| + if (merr != VMIXER_NO_ERROR)
|
| + return false;
|
| +
|
| + tv_vm_start();
|
| +
|
| +
|
| + st->grp_layer_enable[vm_layer] = false;
|
| +
|
| + GRPPRINTK("()\n\r");
|
| +
|
| + return true;
|
| +}
|
| +
|
| +int s5ptvfb_set_output(struct s5p_tv_status *ctrl) { return 0; }
|
| +
|
| +int s5ptvfb_set_display_mode(struct s5p_tv_status *ctrl)
|
| +{
|
| + enum s5p_tv_vmx_layer layer = VM_GPR0_LAYER;
|
| + bool premul = false;
|
| + bool pixel_blending = false;
|
| + bool blank_change = false;
|
| + bool win_blending = false;
|
| + u32 blank_color = 0x0;
|
| + enum s5p_tv_vmx_color_fmt color;
|
| + u32 bpp;
|
| + u32 alpha = 0;
|
| +
|
| + bpp = ((struct fb_var_screeninfo)(ctrl->fb->var)).bits_per_pixel;
|
| +
|
| + if (bpp == 32)
|
| + color = VM_DIRECT_RGB8888;
|
| + else
|
| + color = VM_DIRECT_RGB565;
|
| +
|
| + tv_vm_set_ctrl(layer, premul, pixel_blending, blank_change,
|
| + win_blending, color, alpha, blank_color);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_display_on(struct s5p_tv_status *ctrl)
|
| +{
|
| + tv_vm_set_layer_priority(VM_GPR0_LAYER, 10);
|
| + tv_vm_set_layer_show(VM_GPR0_LAYER, true);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_display_off(struct s5p_tv_status *ctrl)
|
| +{
|
| + tv_vm_set_layer_priority(VM_GPR0_LAYER, 10);
|
| + tv_vm_set_layer_show(VM_GPR0_LAYER, false);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_frame_off(struct s5p_tv_status *ctrl) { return 0; }
|
| +int s5ptvfb_set_clock(struct s5p_tv_status *ctrl) { return 0; }
|
| +int s5ptvfb_set_polarity(struct s5p_tv_status *ctrl) { return 0; }
|
| +int s5ptvfb_set_timing(struct s5p_tv_status *ctrl) { return 0; }
|
| +int s5ptvfb_set_lcd_size(struct s5p_tv_status *ctrl) { return 0; }
|
| +int s5ptvfb_window_on(struct s5p_tv_status *ctrl, int id)
|
| +{
|
| + tv_vm_set_layer_show(VM_GPR0_LAYER, true);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_window_off(struct s5p_tv_status *ctrl, int id)
|
| +{
|
| + tv_vm_set_layer_show(VM_GPR0_LAYER, false);
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_set_window_control(struct s5p_tv_status *ctrl, int id) { return 0; }
|
| +int s5ptvfb_set_alpha_blending(struct s5p_tv_status *ctrl, int id) { return 0; }
|
| +int s5ptvfb_set_window_position(struct s5p_tv_status *ctrl, int id)
|
| +{
|
| + u32 off_x, off_y;
|
| + u32 w_t, h_t;
|
| + u32 w, h;
|
| +
|
| + struct fb_var_screeninfo *var = &ctrl->fb->var;
|
| + struct s5ptvfb_window *win = ctrl->fb->par;
|
| +
|
| + off_x = (u32)win->x;
|
| + off_y = (u32)win->y;
|
| +
|
| + w = var->xres;
|
| + h = var->yres;
|
| +
|
| + /*
|
| + * When tvout resolution was overscanned, there is no
|
| + * adjust method in H/W. So, framebuffer should be resized.
|
| + * In this case - TV w/h is greater than FB w/h, grp layer's
|
| + * dst offset must be changed to fix tv screen.
|
| + */
|
| +
|
| + switch (ctrl->tvout_param.disp_mode) {
|
| +
|
| + case TVOUT_NTSC_M:
|
| + case TVOUT_480P_60_16_9:
|
| + case TVOUT_480P_60_4_3:
|
| + case TVOUT_480P_59:
|
| + w_t = 720;
|
| + h_t = 480;
|
| + break;
|
| +
|
| + case TVOUT_576P_50_16_9:
|
| + case TVOUT_576P_50_4_3:
|
| + w_t = 720;
|
| + h_t = 576;
|
| + break;
|
| +
|
| + case TVOUT_720P_60:
|
| + case TVOUT_720P_59:
|
| + case TVOUT_720P_50:
|
| + w_t = 1280;
|
| + h_t = 720;
|
| + break;
|
| +
|
| + case TVOUT_1080I_60:
|
| + case TVOUT_1080I_59:
|
| + case TVOUT_1080I_50:
|
| + case TVOUT_1080P_60:
|
| + case TVOUT_1080P_59:
|
| + case TVOUT_1080P_50:
|
| + case TVOUT_1080P_30:
|
| + w_t = 1920;
|
| + h_t = 1080;
|
| + break;
|
| +
|
| + default:
|
| + w_t = 0;
|
| + h_t = 0;
|
| + break;
|
| + }
|
| +
|
| + if (w_t > w)
|
| + off_x = (w_t - w) / 2;
|
| +
|
| + if (h_t > h)
|
| + off_y = (h_t - h) / 2;
|
| +
|
| + tv_vm_set_grp_layer_position(VM_GPR0_LAYER, off_x, off_y);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_set_window_size(struct s5p_tv_status *ctrl, int id)
|
| +{
|
| + struct fb_var_screeninfo *var = &ctrl->fb->var;
|
| + int w, h, xo, yo;
|
| +
|
| + w = var->xres;
|
| + h = var->yres;
|
| + xo = var->xoffset;
|
| + yo = var->yoffset;
|
| +
|
| + tv_vm_set_grp_layer_size(VM_GPR0_LAYER, w, w, h, xo, yo);
|
| +
|
| +
|
| + dev_dbg(ctrl->dev_fb, "[fb%d] resolution: %d x %d\n", id,
|
| + var->xres, var->yres);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_set_buffer_address(struct s5p_tv_status *ctrl, int id)
|
| +{
|
| + struct fb_fix_screeninfo *fix = &ctrl->fb->fix;
|
| + struct fb_var_screeninfo *var = &ctrl->fb->var;
|
| + dma_addr_t start_addr = 0, end_addr = 0;
|
| +
|
| + if (fix->smem_start) {
|
| + start_addr = fix->smem_start + (var->xres_virtual *
|
| + (var->bits_per_pixel / 8) * var->yoffset);
|
| +
|
| + end_addr = start_addr + (var->xres_virtual *
|
| + (var->bits_per_pixel / 8) * var->yres);
|
| + }
|
| +
|
| + tv_vm_set_grp_base_address(VM_GPR0_LAYER, start_addr);
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_set_buffer_size(struct s5p_tv_status *ctrl, int id) { return 0; }
|
| +
|
| +int s5ptvfb_set_chroma_key(struct s5p_tv_status *ctrl, int id)
|
| +{
|
| + struct s5ptvfb_window *win = ctrl->fb->par;
|
| + struct s5ptvfb_chroma *chroma = &win->chroma;
|
| +
|
| + enum s5p_tv_vmx_layer layer = VM_GPR0_LAYER;
|
| +
|
| + bool blank_change = (chroma->enabled) ? true : false;
|
| + u32 blank_color = chroma->key;
|
| +
|
| + bool win_blending = (chroma->blended) ? true : false;
|
| + bool alpha = chroma->alpha;
|
| +
|
| + enum s5p_tv_vmx_color_fmt color = VM_DIRECT_RGB8888;
|
| +
|
| + tv_vm_set_ctrl(layer, false, false, blank_change,
|
| + win_blending, color, alpha, blank_color);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_wait_for_vsync(void)
|
| +{
|
| + sleep_on_timeout(&s5ptv_wq, HZ / 10);
|
| + return 0;
|
| +}
|
| +
|
| +static inline unsigned int chan_to_field(unsigned int chan,
|
| + struct fb_bitfield bf)
|
| +{
|
| + chan &= 0xffff;
|
| + chan >>= 16 - bf.length;
|
| +
|
| + return chan << bf.offset;
|
| +}
|
| +
|
| +static int s5ptvfb_set_alpha_info(struct fb_var_screeninfo *var,
|
| + struct s5ptvfb_window *win)
|
| +{
|
| + if (var->transp.length > 0)
|
| + win->alpha.mode = PIXEL_BLENDING;
|
| + else {
|
| + win->alpha.mode = PLANE_BLENDING;
|
| + win->alpha.channel = 0;
|
| + win->alpha.value = S5PTVFB_AVALUE(0xf, 0xf, 0xf);
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +static int s5ptvfb_enable_window(int id)
|
| +{
|
| + struct s5ptvfb_window *win = s5ptv_status.fb->par;
|
| +
|
| + if (s5ptvfb_window_on(&s5ptv_status, id)) {
|
| + win->enabled = 0;
|
| + return -EFAULT;
|
| + } else {
|
| + win->enabled = 1;
|
| + return 0;
|
| + }
|
| +}
|
| +
|
| +
|
| +static int s5ptvfb_disable_window(int id)
|
| +{
|
| + struct s5ptvfb_window *win = s5ptv_status.fb->par;
|
| +
|
| + if (s5ptvfb_window_off(&s5ptv_status, id)) {
|
| + win->enabled = 1;
|
| + return -EFAULT;
|
| + } else {
|
| + win->enabled = 0;
|
| + return 0;
|
| + }
|
| +}
|
| +
|
| +int s5ptvfb_unmap_video_memory(struct fb_info *fb)
|
| +{
|
| + struct fb_fix_screeninfo *fix = &fb->fix;
|
| + struct s5ptvfb_window *win = fb->par;
|
| +
|
| + if (fix->smem_start) {
|
| + dma_free_writecombine(s5ptv_status.dev_fb, fix->smem_len,
|
| + fb->screen_base, fix->smem_start);
|
| + fix->smem_start = 0;
|
| + fix->smem_len = 0;
|
| + dev_info(s5ptv_status.dev_fb,
|
| + "[fb%d] video memory released\n", win->id);
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +static int s5ptvfb_release_window(struct fb_info *fb)
|
| +{
|
| + struct s5ptvfb_window *win = fb->par;
|
| +
|
| + win->x = 0;
|
| + win->y = 0;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_map_video_memory(struct fb_info *fb)
|
| +{
|
| + struct fb_fix_screeninfo *fix = &fb->fix;
|
| + struct s5ptvfb_window *win = fb->par;
|
| +
|
| + if (win->path == DATA_PATH_FIFO)
|
| + return 0;
|
| +
|
| + fb->screen_base = dma_alloc_writecombine(s5ptv_status.dev_fb,
|
| + PAGE_ALIGN(fix->smem_len),
|
| + (unsigned int *) &fix->smem_start, GFP_KERNEL);
|
| + if (!fb->screen_base)
|
| + return -ENOMEM;
|
| + else
|
| + dev_info(s5ptv_status.dev_fb,
|
| + "[fb%d] dma: 0x%08x, cpu: 0x%08x, "
|
| + "size: 0x%08x\n", win->id,
|
| + (unsigned int) fix->smem_start,
|
| + (unsigned int) fb->screen_base,
|
| + fix->smem_len);
|
| +
|
| + memset(fb->screen_base, 0, fix->smem_len);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +static int s5ptvfb_set_bitfield(struct fb_var_screeninfo *var)
|
| +{
|
| + switch (var->bits_per_pixel) {
|
| + case 16:
|
| + if (var->transp.length == 1) {
|
| + var->red.offset = 10;
|
| + var->red.length = 5;
|
| + var->green.offset = 5;
|
| + var->green.length = 5;
|
| + var->blue.offset = 0;
|
| + var->blue.length = 5;
|
| + var->transp.offset = 15;
|
| + } else if (var->transp.length == 4) {
|
| + var->red.offset = 8;
|
| + var->red.length = 4;
|
| + var->green.offset = 4;
|
| + var->green.length = 4;
|
| + var->blue.offset = 0;
|
| + var->blue.length = 4;
|
| + var->transp.offset = 12;
|
| + } else {
|
| + var->red.offset = 11;
|
| + var->red.length = 5;
|
| + var->green.offset = 5;
|
| + var->green.length = 6;
|
| + var->blue.offset = 0;
|
| + var->blue.length = 5;
|
| + var->transp.offset = 0;
|
| + }
|
| + break;
|
| +
|
| + case 24:
|
| + var->red.offset = 16;
|
| + var->red.length = 8;
|
| + var->green.offset = 8;
|
| + var->green.length = 8;
|
| + var->blue.offset = 0;
|
| + var->blue.length = 8;
|
| + var->transp.offset = 0;
|
| + var->transp.length = 0;
|
| + break;
|
| +
|
| + case 32:
|
| + var->red.offset = 16;
|
| + var->red.length = 8;
|
| + var->green.offset = 8;
|
| + var->green.length = 8;
|
| + var->blue.offset = 0;
|
| + var->blue.length = 8;
|
| + var->transp.offset = 24;
|
| + break;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +static int s5ptvfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
|
| +{
|
| + /* nothing to do for removing cursor */
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +static int s5ptvfb_setcolreg(unsigned int regno, unsigned int red,
|
| + unsigned int green, unsigned int blue,
|
| + unsigned int transp, struct fb_info *fb)
|
| +{
|
| + unsigned int *pal = (unsigned int *) fb->pseudo_palette;
|
| + unsigned int val = 0;
|
| +
|
| + if (regno < 16) {
|
| + /* fake palette of 16 colors */
|
| + val |= chan_to_field(red, fb->var.red);
|
| + val |= chan_to_field(green, fb->var.green);
|
| + val |= chan_to_field(blue, fb->var.blue);
|
| + val |= chan_to_field(transp, fb->var.transp);
|
| +
|
| + pal[regno] = val;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +static int s5ptvfb_pan_display(struct fb_var_screeninfo *var,
|
| + struct fb_info *fb)
|
| +{
|
| + struct s5ptvfb_window *win = fb->par;
|
| +
|
| + if (var->yoffset + var->yres > var->yres_virtual) {
|
| + dev_err(s5ptv_status.dev_fb, "invalid yoffset value\n");
|
| + return -EINVAL;
|
| + }
|
| +
|
| + fb->var.yoffset = var->yoffset;
|
| +
|
| + dev_dbg(s5ptv_status.dev_fb,
|
| + "[fb%d] yoffset for pan display: %d\n", win->id,
|
| + var->yoffset);
|
| +
|
| + s5ptvfb_set_buffer_address(&s5ptv_status, win->id);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +static int s5ptvfb_blank(int blank_mode, struct fb_info *fb)
|
| +{
|
| + struct s5ptvfb_window *win = fb->par;
|
| +
|
| + dev_dbg(s5ptv_status.dev_fb, "change blank mode\n");
|
| +
|
| + switch (blank_mode) {
|
| + case FB_BLANK_UNBLANK:
|
| + if (fb->fix.smem_start) {
|
| + s5ptvfb_display_on(&s5ptv_status);
|
| + s5ptvfb_enable_window(win->id);
|
| + } else
|
| + dev_info(s5ptv_status.dev_fb,
|
| + "[fb%d] no allocated memory for unblank\n",
|
| + win->id);
|
| +
|
| + break;
|
| +
|
| + case FB_BLANK_POWERDOWN:
|
| + s5ptvfb_display_off(&s5ptv_status);
|
| + s5ptvfb_disable_window(win->id);
|
| + break;
|
| +
|
| + default:
|
| + dev_dbg(s5ptv_status.dev_fb, "unsupported blank mode\n");
|
| + break;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_set_par(struct fb_info *fb)
|
| +{
|
| + struct s5ptvfb_window *win = fb->par;
|
| +
|
| + dev_dbg(s5ptv_status.dev_fb, "[fb%d] set_par\n", win->id);
|
| +
|
| + if (!fb->fix.smem_start) {
|
| + printk(KERN_ERR "[Warning]fb addr should be \
|
| + allocated before enabling HDMI\n");
|
| + }
|
| +
|
| +
|
| + /* For setting input color format */
|
| + ((struct fb_var_screeninfo) (s5ptv_status.fb->var)).bits_per_pixel =
|
| + ((struct fb_var_screeninfo) (fb->var)).bits_per_pixel;
|
| +
|
| + s5ptvfb_set_display_mode(&s5ptv_status);
|
| +
|
| + s5ptvfb_set_window_control(&s5ptv_status, win->id);
|
| + s5ptvfb_set_window_position(&s5ptv_status, win->id);
|
| + s5ptvfb_set_window_size(&s5ptv_status, win->id);
|
| + s5ptvfb_set_buffer_address(&s5ptv_status, win->id);
|
| + s5ptvfb_set_buffer_size(&s5ptv_status, win->id);
|
| +
|
| + if (win->id > 0)
|
| + s5ptvfb_set_alpha_blending(&s5ptv_status, win->id);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int s5ptvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
|
| +{
|
| + struct fb_fix_screeninfo *fix = &fb->fix;
|
| + struct s5ptvfb_window *win = fb->par;
|
| + struct s5ptvfb_lcd *lcd = s5ptv_status.lcd;
|
| +
|
| + dev_dbg(s5ptv_status.dev_fb, "[fb%d] check_var\n", win->id);
|
| +
|
| + if (var->bits_per_pixel != 16 && var->bits_per_pixel != 24 &&
|
| + var->bits_per_pixel != 32) {
|
| + dev_err(s5ptv_status.dev_fb, "invalid bits per pixel\n");
|
| + return -EINVAL;
|
| + }
|
| +
|
| + if (var->xres > lcd->width)
|
| + var->xres = lcd->width;
|
| +
|
| + if (var->yres > lcd->height)
|
| + var->yres = lcd->height;
|
| +
|
| + if (var->xres_virtual != var->xres)
|
| + var->xres_virtual = var->xres;
|
| +
|
| + if (var->yres_virtual > var->yres * (fb->fix.ypanstep + 1))
|
| + var->yres_virtual = var->yres * (fb->fix.ypanstep + 1);
|
| +
|
| + if (var->xoffset != 0)
|
| + var->xoffset = 0;
|
| +
|
| + if (var->yoffset + var->yres > var->yres_virtual)
|
| + var->yoffset = var->yres_virtual - var->yres;
|
| +
|
| + if (win->x + var->xres > lcd->width)
|
| + win->x = lcd->width - var->xres;
|
| +
|
| + if (win->y + var->yres > lcd->height)
|
| + win->y = lcd->height - var->yres;
|
| +
|
| + /* modify the fix info */
|
| + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
|
| + fix->smem_len = fix->line_length * var->yres_virtual;
|
| +
|
| +
|
| + s5ptvfb_set_bitfield(var);
|
| + s5ptvfb_set_alpha_info(var, win);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +static int s5ptvfb_release(struct fb_info *fb, int user)
|
| +{
|
| + int ret;
|
| + struct s5ptvfb_window *win = fb->par;
|
| +
|
| + s5ptv_status.hdcp_en = false;
|
| +
|
| + s5ptvfb_release_window(fb);
|
| +
|
| + tv_vlayer_stop();
|
| + tv_if_stop();
|
| +
|
| + s5ptv_status.hdcp_en = false;
|
| +
|
| + s5ptv_status.tvout_output_enable = false;
|
| +
|
| + /*
|
| + * drv. release
|
| + * - just check drv. state reg. or not.
|
| + */
|
| +
|
| + ret = s5p_tv_clk_gate(false);
|
| + if (ret < 0) {
|
| + printk(KERN_ERR "[Error]Cannot release\n");
|
| + return -1;
|
| + }
|
| + tv_phy_power(false);
|
| +
|
| + mutex_lock(&s5ptv_status.fb_lock);
|
| + atomic_dec(&win->in_use);
|
| + mutex_unlock(&s5ptv_status.fb_lock);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +static int s5ptvfb_ioctl(struct fb_info *fb, unsigned int cmd,
|
| + unsigned long arg)
|
| +{
|
| + struct fb_var_screeninfo *var = &fb->var;
|
| + struct s5ptvfb_window *win = fb->par;
|
| + struct s5ptvfb_lcd *lcd = s5ptv_status.lcd;
|
| + int ret = 0;
|
| + void *argp = (void *) arg;
|
| +
|
| + union {
|
| + struct s5ptvfb_user_window user_window;
|
| + struct s5ptvfb_user_plane_alpha user_alpha;
|
| + struct s5ptvfb_user_chroma user_chroma;
|
| + int vsync;
|
| + } p;
|
| +
|
| + switch (cmd) {
|
| +
|
| + case FBIO_ALLOC:
|
| + win->path = (enum s5ptvfb_data_path_t) argp;
|
| + break;
|
| +
|
| + case FBIOGET_FSCREENINFO:
|
| + ret = memcpy(argp, &fb->fix, sizeof(fb->fix)) ? 0 : -EFAULT;
|
| + break;
|
| +
|
| + case FBIOGET_VSCREENINFO:
|
| + ret = memcpy(argp, &fb->var, sizeof(fb->var)) ? 0 : -EFAULT;
|
| + break;
|
| +
|
| + case FBIOPUT_VSCREENINFO:
|
| + ret = s5ptvfb_check_var((struct fb_var_screeninfo *) argp, fb);
|
| + if (ret) {
|
| + dev_err(s5ptv_status.dev_fb, "invalid vscreeninfo\n");
|
| + break;
|
| + }
|
| +
|
| + ret = memcpy(&fb->var, (struct fb_var_screeninfo *) argp,
|
| + sizeof(fb->var)) ? 0 : -EFAULT;
|
| + if (ret) {
|
| + dev_err(s5ptv_status.dev_fb,
|
| + "failed to put new vscreeninfo\n");
|
| + break;
|
| + }
|
| +
|
| + ret = s5ptvfb_set_par(fb);
|
| + break;
|
| +
|
| + case S5PTVFB_WIN_POSITION:
|
| + if (copy_from_user(&p.user_window,
|
| + (struct s5ptvfb_user_window __user *) arg,
|
| + sizeof(p.user_window)))
|
| + ret = -EFAULT;
|
| + else {
|
| + if (p.user_window.x < 0)
|
| + p.user_window.x = 0;
|
| +
|
| + if (p.user_window.y < 0)
|
| + p.user_window.y = 0;
|
| +
|
| + if (p.user_window.x + var->xres > lcd->width)
|
| + win->x = lcd->width - var->xres;
|
| + else
|
| + win->x = p.user_window.x;
|
| +
|
| + if (p.user_window.y + var->yres > lcd->height)
|
| + win->y = lcd->height - var->yres;
|
| + else
|
| + win->y = p.user_window.y;
|
| +
|
| + s5ptvfb_set_window_position(&s5ptv_status, win->id);
|
| + }
|
| + break;
|
| +
|
| + case S5PTVFB_WIN_SET_PLANE_ALPHA:
|
| + if (copy_from_user(&p.user_alpha,
|
| + (struct s5ptvfb_user_plane_alpha __user *) arg,
|
| + sizeof(p.user_alpha)))
|
| + ret = -EFAULT;
|
| + else {
|
| + win->alpha.mode = PLANE_BLENDING;
|
| + win->alpha.channel = p.user_alpha.channel;
|
| + win->alpha.value =
|
| + S5PTVFB_AVALUE(p.user_alpha.red,
|
| + p.user_alpha.green,
|
| + p.user_alpha.blue);
|
| +
|
| + s5ptvfb_set_alpha_blending(&s5ptv_status, win->id);
|
| + }
|
| + break;
|
| +
|
| + case S5PTVFB_WIN_SET_CHROMA:
|
| + if (copy_from_user(&p.user_chroma,
|
| + (struct s5ptvfb_user_chroma __user *) arg,
|
| + sizeof(p.user_chroma)))
|
| + ret = -EFAULT;
|
| + else {
|
| + win->chroma.enabled = p.user_chroma.enabled;
|
| + win->chroma.key = S5PTVFB_CHROMA(p.user_chroma.red,
|
| + p.user_chroma.green,
|
| + p.user_chroma.blue);
|
| +
|
| + s5ptvfb_set_chroma_key(&s5ptv_status, win->id);
|
| + }
|
| + break;
|
| +
|
| + case S5PTVFB_SET_VSYNC_INT:
|
| + tv_vm_set_vsync_interrupt((int)argp);
|
| + break;
|
| +
|
| + case S5PTVFB_WAITFORVSYNC:
|
| + s5ptvfb_wait_for_vsync();
|
| + break;
|
| +
|
| + case S5PTVFB_WIN_SET_ADDR:
|
| + fb->fix.smem_start = (unsigned long)argp;
|
| + s5ptvfb_set_buffer_address(&s5ptv_status, win->id);
|
| + break;
|
| +
|
| + case S5PTVFB_SET_WIN_ON:
|
| + s5ptvfb_display_on(&s5ptv_status);
|
| + s5ptvfb_enable_window(0);
|
| + break;
|
| +
|
| + case S5PTVFB_SET_WIN_OFF:
|
| + s5ptvfb_display_off(&s5ptv_status);
|
| + s5ptvfb_disable_window(0);
|
| + break;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +static int s5ptvfb_open(struct fb_info *fb, int user)
|
| +{
|
| + struct s5ptvfb_window *win = fb->par;
|
| + int ret = 0;
|
| +
|
| + ret = s5p_tv_clk_gate(true);
|
| + if (ret < 0) {
|
| + printk(KERN_ERR "[Error]Cannot open it\n");
|
| + return -1;
|
| + }
|
| +
|
| + tv_phy_power(true);
|
| +
|
| + tv_if_init_param();
|
| +
|
| + s5p_tv_v4l2_init_param();
|
| +
|
| + s5ptv_status.tvout_param.out_mode = TVOUT_OUTPUT_HDMI_RGB;
|
| +
|
| + tv_if_set_disp();
|
| +
|
| + mutex_lock(&s5ptv_status.fb_lock);
|
| +
|
| + if (atomic_read(&win->in_use)) {
|
| +
|
| + dev_dbg(s5ptv_status.dev_fb,
|
| + "do not allow multiple open "
|
| + "for window\n");
|
| + ret = -EBUSY;
|
| +
|
| + } else
|
| + atomic_inc(&win->in_use);
|
| +
|
| + mutex_unlock(&s5ptv_status.fb_lock);
|
| +
|
| + return ret;
|
| +
|
| +}
|
| +
|
| +
|
| +int s5p_tv_v_read(struct file *filp, char *buf, size_t count,
|
| + loff_t *f_pos)
|
| +{
|
| + return 0;
|
| +}
|
| +
|
| +int s5p_tv_v_write(struct file *filp, const char *buf, size_t
|
| + count, loff_t *f_pos)
|
| +{
|
| + return 0;
|
| +}
|
| +
|
| +struct fb_ops s5ptvfb_ops = {
|
| + .owner = THIS_MODULE,
|
| + .fb_fillrect = cfb_fillrect,
|
| + .fb_copyarea = cfb_copyarea,
|
| + .fb_imageblit = cfb_imageblit,
|
| + .fb_check_var = s5ptvfb_check_var,
|
| + .fb_set_par = s5ptvfb_set_par,
|
| + .fb_blank = s5ptvfb_blank,
|
| + .fb_pan_display = s5ptvfb_pan_display,
|
| + .fb_setcolreg = s5ptvfb_setcolreg,
|
| + .fb_cursor = s5ptvfb_cursor,
|
| + .fb_ioctl = s5ptvfb_ioctl,
|
| + .fb_open = s5ptvfb_open,
|
| + .fb_release = s5ptvfb_release,
|
| +};
|
| +
|
| +int s5ptvfb_init_fbinfo(int id)
|
| +{
|
| + struct fb_info *fb = s5ptv_status.fb;
|
| + struct fb_fix_screeninfo *fix = &fb->fix;
|
| + struct fb_var_screeninfo *var = &fb->var;
|
| + struct s5ptvfb_window *win = fb->par;
|
| + struct s5ptvfb_alpha *alpha = &win->alpha;
|
| + struct s5ptvfb_lcd *lcd = s5ptv_status.lcd;
|
| + struct s5ptvfb_lcd_timing *timing = &lcd->timing;
|
| +
|
| + memset(win, 0, sizeof(struct s5ptvfb_window));
|
| +
|
| + platform_set_drvdata(to_platform_device(s5ptv_status.dev_fb), fb);
|
| +
|
| + strcpy(fix->id, S5PTVFB_NAME);
|
| +
|
| + /* fimd specific */
|
| + win->id = id;
|
| + win->path = DATA_PATH_DMA;
|
| + win->dma_burst = 16;
|
| + alpha->mode = PLANE_BLENDING;
|
| +
|
| + /* fbinfo */
|
| + fb->fbops = &s5ptvfb_ops;
|
| + fb->flags = FBINFO_FLAG_DEFAULT;
|
| + fb->pseudo_palette = &win->pseudo_pal;
|
| + fix->xpanstep = 0;
|
| + fix->ypanstep = 0;
|
| + fix->type = FB_TYPE_PACKED_PIXELS;
|
| + fix->accel = FB_ACCEL_NONE;
|
| + fix->visual = FB_VISUAL_TRUECOLOR;
|
| + var->xres = lcd->width;
|
| + var->yres = lcd->height;
|
| + var->xres_virtual = var->xres;
|
| + var->yres_virtual = var->yres
|
| + + (var->yres * fix->ypanstep);
|
| + var->bits_per_pixel = 32;
|
| + var->xoffset = 0;
|
| + var->yoffset = 0;
|
| + var->width = 0;
|
| + var->height = 0;
|
| + var->transp.length = 0;
|
| + var->nonstd = 0;
|
| + var->activate = FB_ACTIVATE_NOW;
|
| + var->vmode = FB_VMODE_NONINTERLACED;
|
| + var->hsync_len = timing->h_sw;
|
| + var->vsync_len = timing->v_sw;
|
| + var->left_margin = timing->h_fp;
|
| + var->right_margin = timing->h_bp;
|
| + var->upper_margin = timing->v_fp;
|
| + var->lower_margin = timing->v_bp;
|
| +
|
| + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
|
| + fix->smem_len = fix->line_length * var->yres_virtual;
|
| +
|
| + var->pixclock = lcd->freq * (var->left_margin + var->right_margin +
|
| + var->hsync_len + var->xres) *
|
| + (var->upper_margin + var->lower_margin +
|
| + var->vsync_len + var->yres);
|
| +
|
| + dev_dbg(s5ptv_status.dev_fb, "pixclock: %d\n", var->pixclock);
|
| +
|
| + s5ptvfb_set_bitfield(var);
|
| + s5ptvfb_set_alpha_info(var, win);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +static struct s5ptvfb_lcd max_tvfb = {
|
| + .width = 1920,
|
| + .height = 1080,
|
| + .bpp = 32,
|
| + .freq = 60,
|
| +
|
| + .timing = {
|
| + .h_fp = 49,
|
| + .h_bp = 17,
|
| + .h_sw = 33,
|
| + .v_fp = 4,
|
| + .v_fpe = 1,
|
| + .v_bp = 15,
|
| + .v_bpe = 1,
|
| + .v_sw = 6,
|
| + },
|
| +
|
| + .polarity = {
|
| + .rise_vclk = 0,
|
| + .inv_hsync = 1,
|
| + .inv_vsync = 1,
|
| + .inv_vden = 0,
|
| + },
|
| +};
|
| +
|
| +void s5ptvfb_set_lcd_info(struct s5p_tv_status *ctrl)
|
| +{
|
| + ctrl->lcd = &max_tvfb;
|
| +}
|
| +
|
| +
|
| +int s5ptvfb_direct_ioctl(int id, unsigned int cmd, unsigned long arg)
|
| +{
|
| + struct fb_info *fb = s5ptv_status.fb;
|
| + struct fb_fix_screeninfo *fix = &fb->fix;
|
| + struct s5ptvfb_window *win = fb->par;
|
| + void *argp = (void *) arg;
|
| + int ret = 0;
|
| +
|
| + switch (cmd) {
|
| +
|
| + case FBIO_ALLOC:
|
| + win->path = (enum s5ptvfb_data_path_t) argp;
|
| + break;
|
| +
|
| + case FBIOGET_FSCREENINFO:
|
| + ret = memcpy(argp, &fb->fix, sizeof(fb->fix)) ? 0 : -EFAULT;
|
| + break;
|
| +
|
| + case FBIOGET_VSCREENINFO:
|
| + ret = memcpy(argp, &fb->var, sizeof(fb->var)) ? 0 : -EFAULT;
|
| + break;
|
| +
|
| + case FBIOPUT_VSCREENINFO:
|
| + ret = s5ptvfb_check_var((struct fb_var_screeninfo *) argp, fb);
|
| + if (ret) {
|
| + dev_err(s5ptv_status.dev_fb, "invalid vscreeninfo\n");
|
| + break;
|
| + }
|
| +
|
| + ret = memcpy(&fb->var, (struct fb_var_screeninfo *) argp,
|
| + sizeof(fb->var)) ? 0 : -EFAULT;
|
| + if (ret) {
|
| + dev_err(s5ptv_status.dev_fb,
|
| + "failed to put new vscreeninfo\n");
|
| + break;
|
| + }
|
| +
|
| + ret = s5ptvfb_set_par(fb);
|
| + break;
|
| +
|
| + case S5PTVFB_SET_WIN_ON:
|
| + s5ptvfb_display_on(&s5ptv_status);
|
| + s5ptvfb_enable_window(0);
|
| + break;
|
| +
|
| + case S5PTVFB_SET_WIN_OFF:
|
| + s5ptvfb_display_off(&s5ptv_status);
|
| + s5ptvfb_disable_window(0);
|
| + break;
|
| +
|
| + case S5PTVFB_POWER_ON:
|
| + ret = s5p_tv_clk_gate(true);
|
| + if (ret < 0) {
|
| + printk(KERN_ERR "[Error]The power is not on\n");
|
| + break;
|
| + }
|
| + tv_phy_power(true);
|
| +
|
| + tv_if_init_param();
|
| +
|
| + s5p_tv_v4l2_init_param();
|
| +
|
| + s5ptv_status.tvout_param.out_mode = TVOUT_OUTPUT_HDMI_RGB;
|
| +
|
| + tv_if_set_disp();
|
| +
|
| + break;
|
| +
|
| + case S5PTVFB_POWER_OFF:
|
| + tv_vlayer_stop();
|
| + tv_if_stop();
|
| +
|
| + ret = s5p_tv_clk_gate(false);
|
| + if (ret < 0) {
|
| + printk(KERN_ERR "[Error]The power is not off\n");
|
| + break;
|
| + }
|
| + tv_phy_power(false);
|
| + break;
|
| +
|
| + case S5PTVFB_SET_VSYNC_INT:
|
| + tv_vm_set_vsync_interrupt((int)argp);
|
| + break;
|
| +
|
| + case S5PTVFB_WAITFORVSYNC:
|
| + s5ptvfb_wait_for_vsync();
|
| + break;
|
| +
|
| + case S5PTVFB_WIN_SET_ADDR:
|
| + fix->smem_start = (unsigned long)argp;
|
| + s5ptvfb_set_buffer_address(&s5ptv_status, win->id);
|
| + break;
|
| +
|
| + default:
|
| + ret = s5ptvfb_ioctl(fb, cmd, arg);
|
| + break;
|
| + }
|
| +
|
| + return ret;
|
| +}
|
| +EXPORT_SYMBOL(s5ptvfb_direct_ioctl);
|
|
|