Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3024)

Unified Diff: drivers/media/video/samsung/tv20/s5p_stda_grp.c

Issue 2036011: V4L/DVB : Add S5PV210 TV out driver support (Closed) Base URL: swsolcc@12.23.106.100:kernel-samsung.git
Patch Set: Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « drivers/media/video/samsung/tv20/hpd.c ('k') | drivers/media/video/samsung/tv20/s5p_stda_tvout_if.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
« no previous file with comments | « drivers/media/video/samsung/tv20/hpd.c ('k') | drivers/media/video/samsung/tv20/s5p_stda_tvout_if.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698