new file mode 100644
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Mixer raw 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/io.h>
+
+#include <mach/regs-mixer.h>
+
+#include "../s5p_tvout_common_lib.h"
+#include "../s5p_tvout_ctrl.h"
+#include "hw_if.h"
+
+#undef tvout_dbg
+
+#ifdef CONFIG_MIXER_DEBUG
+#define tvout_dbg(fmt, ...) \
+ printk(KERN_INFO "\t[MIXER] %s(): " fmt, \
+ __func__, ##__VA_ARGS__)
+#else
+#define tvout_dbg(fmt, ...)
+#endif
+
+void __iomem *mixer_base;
+spinlock_t lock_mixer;
+
+int s5p_mixer_set_show(enum s5p_mixer_layer layer, bool show)
+{
+ u32 mxr_config;
+
+ tvout_dbg("%d, %d\n", layer, show);
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ mxr_config = (show) ?
+ (readl(mixer_base + S5P_MXR_CFG) |
+ S5P_MXR_CFG_VIDEO_ENABLE) :
+ (readl(mixer_base + S5P_MXR_CFG) &
+ ~S5P_MXR_CFG_VIDEO_ENABLE);
+ break;
+
+ case MIXER_GPR0_LAYER:
+ mxr_config = (show) ?
+ (readl(mixer_base + S5P_MXR_CFG) |
+ S5P_MXR_CFG_GRAPHIC0_ENABLE) :
+ (readl(mixer_base + S5P_MXR_CFG) &
+ ~S5P_MXR_CFG_GRAPHIC0_ENABLE);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ mxr_config = (show) ?
+ (readl(mixer_base + S5P_MXR_CFG) |
+ S5P_MXR_CFG_GRAPHIC1_ENABLE) :
+ (readl(mixer_base + S5P_MXR_CFG) &
+ ~S5P_MXR_CFG_GRAPHIC1_ENABLE);
+ break;
+
+ default:
+ tvout_err("invalid layer parameter = %d\n", layer);
+ return -1;
+ }
+
+ writel(mxr_config, mixer_base + S5P_MXR_CFG);
+
+ return 0;
+}
+
+int s5p_mixer_set_priority(enum s5p_mixer_layer layer, u32 priority)
+{
+ u32 layer_cfg;
+
+ tvout_dbg("%d, %d\n", layer, priority);
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ layer_cfg = S5P_MXR_LAYER_CFG_VID_PRIORITY_CLR(
+ readl(mixer_base + S5P_MXR_LAYER_CFG)) |
+ S5P_MXR_LAYER_CFG_VID_PRIORITY(priority);
+ break;
+
+ case MIXER_GPR0_LAYER:
+ layer_cfg = S5P_MXR_LAYER_CFG_GRP0_PRIORITY_CLR(
+ readl(mixer_base + S5P_MXR_LAYER_CFG)) |
+ S5P_MXR_LAYER_CFG_GRP0_PRIORITY(priority);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ layer_cfg = S5P_MXR_LAYER_CFG_GRP1_PRIORITY_CLR(
+ readl(mixer_base + S5P_MXR_LAYER_CFG)) |
+ S5P_MXR_LAYER_CFG_GRP1_PRIORITY(priority);
+ break;
+
+ default:
+ tvout_err("invalid layer parameter = %d\n", layer);
+ return -1;
+ }
+
+ writel(layer_cfg, mixer_base + S5P_MXR_LAYER_CFG);
+
+ return 0;
+}
+
+void s5p_mixer_set_pre_mul_mode(enum s5p_mixer_layer layer, bool enable)
+{
+ u32 reg;
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC0_CFG);
+
+ if (enable)
+ reg |= S5P_MXR_PRE_MUL_MODE;
+ else
+ reg &= ~S5P_MXR_PRE_MUL_MODE;
+
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC0_CFG);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC1_CFG);
+
+ if (enable)
+ reg |= S5P_MXR_PRE_MUL_MODE;
+ else
+ reg &= ~S5P_MXR_PRE_MUL_MODE;
+
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC1_CFG);
+ break;
+
+ case MIXER_VIDEO_LAYER:
+ break;
+ }
+}
+
+int s5p_mixer_set_pixel_blend(enum s5p_mixer_layer layer, bool enable)
+{
+ u32 temp_reg;
+
+ tvout_dbg("%d, %d\n", layer, enable);
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_GRAPHIC0_CFG)
+ & (~S5P_MXR_PIXEL_BLEND_ENABLE) ;
+
+ if (enable)
+ temp_reg |= S5P_MXR_PIXEL_BLEND_ENABLE;
+ else
+ temp_reg |= S5P_MXR_PIXEL_BLEND_DISABLE;
+
+ writel(temp_reg, mixer_base + S5P_MXR_GRAPHIC0_CFG);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_GRAPHIC1_CFG)
+ & (~S5P_MXR_PIXEL_BLEND_ENABLE) ;
+
+ if (enable)
+ temp_reg |= S5P_MXR_PIXEL_BLEND_ENABLE;
+ else
+ temp_reg |= S5P_MXR_PIXEL_BLEND_DISABLE;
+
+ writel(temp_reg, mixer_base + S5P_MXR_GRAPHIC1_CFG);
+ break;
+
+ default:
+ tvout_err("invalid layer parameter = %d\n", layer);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_mixer_set_layer_blend(enum s5p_mixer_layer layer, bool enable)
+{
+ u32 temp_reg;
+
+ tvout_dbg("%d, %d\n", layer, enable);
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_VIDEO_CFG)
+ & (~S5P_MXR_VIDEO_CFG_BLEND_EN) ;
+
+ if (enable)
+ temp_reg |= S5P_MXR_VIDEO_CFG_BLEND_EN;
+ else
+ temp_reg |= S5P_MXR_VIDEO_CFG_BLEND_DIS;
+
+ writel(temp_reg, mixer_base + S5P_MXR_VIDEO_CFG);
+ break;
+
+ case MIXER_GPR0_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_GRAPHIC0_CFG)
+ & (~S5P_MXR_WIN_BLEND_ENABLE) ;
+
+ if (enable)
+ temp_reg |= S5P_MXR_WIN_BLEND_ENABLE;
+ else
+ temp_reg |= S5P_MXR_WIN_BLEND_DISABLE;
+
+ writel(temp_reg, mixer_base + S5P_MXR_GRAPHIC0_CFG);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_GRAPHIC1_CFG)
+ & (~S5P_MXR_WIN_BLEND_ENABLE) ;
+
+ if (enable)
+ temp_reg |= S5P_MXR_WIN_BLEND_ENABLE;
+ else
+ temp_reg |= S5P_MXR_WIN_BLEND_DISABLE;
+
+ writel(temp_reg, mixer_base + S5P_MXR_GRAPHIC1_CFG);
+ break;
+
+ default:
+ tvout_err("invalid layer parameter = %d\n", layer);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_mixer_set_alpha(enum s5p_mixer_layer layer, u32 alpha)
+{
+ u32 temp_reg;
+
+ tvout_dbg("%d, %d\n", layer, alpha);
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_VIDEO_CFG)
+ & (~S5P_MXR_VIDEO_CFG_ALPHA_MASK) ;
+ temp_reg |= S5P_MXR_VIDEO_CFG_ALPHA_VALUE(alpha);
+ writel(temp_reg, mixer_base + S5P_MXR_VIDEO_CFG);
+ break;
+
+ case MIXER_GPR0_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_GRAPHIC0_CFG)
+ & (~S5P_MXR_VIDEO_CFG_ALPHA_MASK) ;
+ temp_reg |= S5P_MXR_GRP_ALPHA_VALUE(alpha);
+ writel(temp_reg, mixer_base + S5P_MXR_GRAPHIC0_CFG);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ temp_reg = readl(mixer_base + S5P_MXR_GRAPHIC1_CFG)
+ & (~S5P_MXR_VIDEO_CFG_ALPHA_MASK) ;
+ temp_reg |= S5P_MXR_GRP_ALPHA_VALUE(alpha);
+ writel(temp_reg, mixer_base + S5P_MXR_GRAPHIC1_CFG);
+ break;
+
+ default:
+ tvout_err("invalid layer parameter = %d\n", layer);
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_mixer_set_grp_base_address(enum s5p_mixer_layer layer, u32 base_addr)
+{
+ tvout_dbg("%d, 0x%x\n", layer, base_addr);
+
+ if (S5P_MXR_GRP_ADDR_ILLEGAL(base_addr)) {
+ tvout_err("address is not word align = %d\n", base_addr);
+ return -1;
+ }
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ writel(S5P_MXR_GPR_BASE(base_addr),
+ mixer_base + S5P_MXR_GRAPHIC0_BASE);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ writel(S5P_MXR_GPR_BASE(base_addr),
+ mixer_base + S5P_MXR_GRAPHIC1_BASE);
+ break;
+
+ default:
+ tvout_err("invalid layer parameter = %d\n", layer);
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_mixer_set_grp_layer_dst_pos(enum s5p_mixer_layer layer,
+ u32 dst_offs_x, u32 dst_offs_y)
+{
+ tvout_dbg("%d, %d, %d\n", layer, dst_offs_x, dst_offs_y);
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ writel(S5P_MXR_GRP_DESTX(dst_offs_x) |
+ S5P_MXR_GRP_DESTY(dst_offs_y),
+ mixer_base + S5P_MXR_GRAPHIC0_DXY);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ writel(S5P_MXR_GRP_DESTX(dst_offs_x) |
+ S5P_MXR_GRP_DESTY(dst_offs_y),
+ mixer_base + S5P_MXR_GRAPHIC1_DXY);
+ break;
+
+ default:
+ tvout_err("invalid layer parameter = %d\n", layer);
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_mixer_set_grp_layer_src_pos(enum s5p_mixer_layer layer,
+ u32 src_offs_x, u32 src_offs_y, u32 span,
+ u32 width, u32 height)
+{
+ tvout_dbg("%d, %d, %d, %d, %d, %d\n", layer, span, width, height, src_offs_x, src_offs_y);
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ writel(S5P_MXR_GRP_SPAN(span),
+ mixer_base + S5P_MXR_GRAPHIC0_SPAN);
+ writel(S5P_MXR_GRP_WIDTH(width) | S5P_MXR_GRP_HEIGHT(height),
+ mixer_base + S5P_MXR_GRAPHIC0_WH);
+ writel(S5P_MXR_GRP_STARTX(src_offs_x) |
+ S5P_MXR_GRP_STARTY(src_offs_y),
+ mixer_base + S5P_MXR_GRAPHIC0_SXY);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ writel(S5P_MXR_GRP_SPAN(span),
+ mixer_base + S5P_MXR_GRAPHIC1_SPAN);
+ writel(S5P_MXR_GRP_WIDTH(width) | S5P_MXR_GRP_HEIGHT(height),
+ mixer_base + S5P_MXR_GRAPHIC1_WH);
+ writel(S5P_MXR_GRP_STARTX(src_offs_x) |
+ S5P_MXR_GRP_STARTY(src_offs_y),
+ mixer_base + S5P_MXR_GRAPHIC1_SXY);
+ break;
+
+ default:
+ tvout_err(" invalid layer parameter = %d\n", layer);
+ return -1;
+ }
+
+ return 0;
+}
+
+void s5p_mixer_set_bg_color(enum s5p_mixer_bg_color_num colornum,
+ u32 color_y, u32 color_cb, u32 color_cr)
+{
+ u32 reg_value;
+
+ reg_value = S5P_MXR_BG_COLOR_Y(color_y) |
+ S5P_MXR_BG_COLOR_CB(color_cb) |
+ S5P_MXR_BG_COLOR_CR(color_cr);
+
+ switch (colornum) {
+ case MIXER_BG_COLOR_0:
+ writel(reg_value, mixer_base + S5P_MXR_BG_COLOR0);
+ break;
+
+ case MIXER_BG_COLOR_1:
+ writel(reg_value, mixer_base + S5P_MXR_BG_COLOR1);
+ break;
+
+ case MIXER_BG_COLOR_2:
+ writel(reg_value, mixer_base + S5P_MXR_BG_COLOR2);
+ break;
+ }
+}
+
+void s5p_mixer_init_status_reg(enum s5p_mixer_burst_mode burst,
+ enum s5p_tvout_endian endian)
+{
+ u32 temp_reg = 0;
+
+ temp_reg = S5P_MXR_STATUS_SYNC_ENABLE | S5P_MXR_STATUS_OPERATING;
+
+ switch (burst) {
+ case MIXER_BURST_8:
+ temp_reg |= S5P_MXR_STATUS_8_BURST;
+ break;
+ case MIXER_BURST_16:
+ temp_reg |= S5P_MXR_STATUS_16_BURST;
+ break;
+ }
+
+ switch (endian) {
+ case TVOUT_BIG_ENDIAN:
+ temp_reg |= S5P_MXR_STATUS_BIG_ENDIAN;
+ break;
+ case TVOUT_LITTLE_ENDIAN:
+ temp_reg |= S5P_MXR_STATUS_LITTLE_ENDIAN;
+ break;
+ }
+
+ writel(temp_reg, mixer_base + S5P_MXR_STATUS);
+}
+
+int s5p_mixer_init_display_mode(enum s5p_tvout_disp_mode mode,
+ enum s5p_tvout_o_mode output_mode)
+{
+ u32 temp_reg = readl(mixer_base + S5P_MXR_CFG);
+
+ tvout_dbg("%d, %d\n", mode, output_mode);
+
+ switch (mode) {
+ case TVOUT_NTSC_M:
+ case TVOUT_NTSC_443:
+ temp_reg &= ~S5P_MXR_CFG_HD;
+ temp_reg &= ~S5P_MXR_CFG_PAL;
+ temp_reg &= S5P_MXR_CFG_INTERLACE;
+ break;
+
+ case TVOUT_PAL_BDGHI:
+ case TVOUT_PAL_M:
+ case TVOUT_PAL_N:
+ case TVOUT_PAL_NC:
+ case TVOUT_PAL_60:
+ temp_reg &= ~S5P_MXR_CFG_HD;
+ temp_reg |= S5P_MXR_CFG_PAL;
+ temp_reg &= S5P_MXR_CFG_INTERLACE;
+ break;
+
+ case TVOUT_480P_60_16_9:
+ case TVOUT_480P_60_4_3:
+ case TVOUT_480P_59:
+ temp_reg &= ~S5P_MXR_CFG_HD;
+ temp_reg &= ~S5P_MXR_CFG_PAL;
+ temp_reg |= S5P_MXR_CFG_PROGRASSIVE;
+ temp_reg |= MIXER_RGB601_16_235<<9;
+ break;
+
+ case TVOUT_576P_50_16_9:
+ case TVOUT_576P_50_4_3:
+ temp_reg &= ~S5P_MXR_CFG_HD;
+ temp_reg |= S5P_MXR_CFG_PAL;
+ temp_reg |= S5P_MXR_CFG_PROGRASSIVE;
+ temp_reg |= MIXER_RGB601_16_235<<9;
+ break;
+
+ case TVOUT_720P_50:
+ case TVOUT_720P_59:
+ case TVOUT_720P_60:
+ temp_reg |= S5P_MXR_CFG_HD;
+ temp_reg &= ~S5P_MXR_CFG_HD_1080I;
+ temp_reg |= S5P_MXR_CFG_PROGRASSIVE;
+ temp_reg |= MIXER_RGB709_16_235<<9;
+ break;
+
+ case TVOUT_1080I_50:
+ case TVOUT_1080I_59:
+ case TVOUT_1080I_60:
+ temp_reg |= S5P_MXR_CFG_HD;
+ temp_reg |= S5P_MXR_CFG_HD_1080I;
+ temp_reg &= S5P_MXR_CFG_INTERLACE;
+ temp_reg |= MIXER_RGB709_16_235<<9;
+ break;
+
+ case TVOUT_1080P_50:
+ case TVOUT_1080P_59:
+ case TVOUT_1080P_60:
+ case TVOUT_1080P_30:
+ temp_reg |= S5P_MXR_CFG_HD;
+ temp_reg |= S5P_MXR_CFG_HD_1080P;
+ temp_reg |= S5P_MXR_CFG_PROGRASSIVE;
+ temp_reg |= MIXER_RGB709_16_235<<9;
+ break;
+
+ default:
+ tvout_err("invalid mode parameter = %d\n", mode);
+ return -1;
+ }
+
+ switch (output_mode) {
+ case TVOUT_COMPOSITE:
+ temp_reg &= S5P_MXR_CFG_TV_OUT;
+ temp_reg &= ~(0x1<<8);
+ temp_reg |= MIXER_YUV444<<8;
+ break;
+
+ case TVOUT_HDMI_RGB:
+ case TVOUT_DVI:
+ temp_reg |= S5P_MXR_CFG_HDMI_OUT;
+ temp_reg &= ~(0x1<<8);
+ temp_reg |= MIXER_RGB888<<8;
+ break;
+
+ case TVOUT_HDMI:
+ temp_reg |= S5P_MXR_CFG_HDMI_OUT;
+ temp_reg &= ~(0x1<<8);
+ temp_reg |= MIXER_YUV444<<8;
+ break;
+
+ default:
+ tvout_err("invalid mode parameter = %d\n", mode);
+ return -1;
+ }
+
+ writel(temp_reg, mixer_base + S5P_MXR_CFG);
+
+ return 0;
+}
+
+void s5p_mixer_scaling(enum s5p_mixer_layer layer,
+ struct s5ptvfb_user_scaling scaling)
+{
+ u32 reg, ver_val = 0, hor_val = 0;
+
+ switch (scaling.ver) {
+ case VERTICAL_X1:
+ ver_val = 0;
+ break;
+
+ case VERTICAL_X2:
+ ver_val = 1;
+ break;
+ }
+
+ switch (scaling.hor) {
+ case HORIZONTAL_X1:
+ hor_val = 0;
+ break;
+
+ case HORIZONTAL_X2:
+ hor_val = 1;
+ break;
+ }
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC0_WH);
+ reg |= S5P_MXR_GRP_V_SCALE(ver_val);
+ reg |= S5P_MXR_GRP_H_SCALE(hor_val);
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC0_WH);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC1_WH);
+ reg |= S5P_MXR_GRP_V_SCALE(ver_val);
+ reg |= S5P_MXR_GRP_H_SCALE(hor_val);
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC1_WH);
+ break;
+
+ case MIXER_VIDEO_LAYER:
+ break;
+ }
+}
+
+void s5p_mixer_set_color_format(enum s5p_mixer_layer layer,
+ enum s5p_mixer_color_fmt format)
+{
+ u32 reg;
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC0_CFG);
+ reg &= ~(S5P_MXR_EG_COLOR_FORMAT(0xf));
+ reg |= S5P_MXR_EG_COLOR_FORMAT(format);
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC0_CFG);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC1_CFG);
+ reg &= ~(S5P_MXR_EG_COLOR_FORMAT(0xf));
+ reg |= S5P_MXR_EG_COLOR_FORMAT(format);
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC1_CFG);
+ break;
+
+ case MIXER_VIDEO_LAYER:
+ break;
+ }
+}
+
+void s5p_mixer_set_chroma_key(enum s5p_mixer_layer layer, bool enabled, u32 key)
+{
+ u32 reg;
+
+ switch (layer) {
+ case MIXER_GPR0_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC0_CFG);
+
+ if (enabled)
+ reg &= ~S5P_MXR_BLANK_CHANGE_NEW_PIXEL;
+ else
+ reg |= S5P_MXR_BLANK_CHANGE_NEW_PIXEL;
+
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC0_CFG);
+ writel(S5P_MXR_GPR_BLANK_COLOR(key),
+ mixer_base + S5P_MXR_GRAPHIC0_BLANK);
+ break;
+
+ case MIXER_GPR1_LAYER:
+ reg = readl(mixer_base + S5P_MXR_GRAPHIC1_CFG);
+
+ if (enabled)
+ reg &= ~S5P_MXR_BLANK_CHANGE_NEW_PIXEL;
+ else
+ reg |= S5P_MXR_BLANK_CHANGE_NEW_PIXEL;
+
+ writel(reg, mixer_base + S5P_MXR_GRAPHIC1_CFG);
+ writel(S5P_MXR_GPR_BLANK_COLOR(key),
+ mixer_base + S5P_MXR_GRAPHIC1_BLANK);
+ break;
+
+ case MIXER_VIDEO_LAYER:
+ break;
+ }
+}
+
+void s5p_mixer_init_bg_dither_enable(bool cr_dither_enable,
+ bool cb_dither_enable,
+ bool y_dither_enable)
+{
+ u32 temp_reg = 0;
+
+ tvout_dbg("%d, %d, %d\n", cr_dither_enable, cb_dither_enable,
+ y_dither_enable);
+
+ temp_reg = (cr_dither_enable) ?
+ (temp_reg | S5P_MXR_BG_CR_DIHER_EN) :
+ (temp_reg & ~S5P_MXR_BG_CR_DIHER_EN);
+ temp_reg = (cb_dither_enable) ?
+ (temp_reg | S5P_MXR_BG_CB_DIHER_EN) :
+ (temp_reg & ~S5P_MXR_BG_CB_DIHER_EN);
+ temp_reg = (y_dither_enable) ?
+ (temp_reg | S5P_MXR_BG_Y_DIHER_EN) :
+ (temp_reg & ~S5P_MXR_BG_Y_DIHER_EN);
+
+ writel(temp_reg, mixer_base + S5P_MXR_BG_CFG);
+
+}
+
+void s5p_mixer_init_csc_coef_default(enum s5p_mixer_csc_type csc_type)
+{
+ tvout_dbg("%d\n", csc_type);
+
+ switch (csc_type) {
+ case MIXER_CSC_601_LR:
+ writel((0 << 30) | (153 << 20) | (300 << 10) | (58 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_Y);
+ writel((936 << 20) | (851 << 10) | (262 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CB);
+ writel((262 << 20) | (805 << 10) | (982 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CR);
+ break;
+
+ case MIXER_CSC_601_FR:
+ writel((1 << 30) | (132 << 20) | (258 << 10) | (50 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_Y);
+ writel((948 << 20) | (875 << 10) | (225 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CB);
+ writel((225 << 20) | (836 << 10) | (988 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CR);
+ break;
+
+ case MIXER_CSC_709_LR:
+ writel((0 << 30) | (109 << 20) | (366 << 10) | (36 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_Y);
+ writel((964 << 20) | (822 << 10) | (216 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CB);
+ writel((262 << 20) | (787 << 10) | (1000 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CR);
+ break;
+
+ case MIXER_CSC_709_FR:
+ writel((1 << 30) | (94 << 20) | (314 << 10) | (32 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_Y);
+ writel((972 << 20) | (851 << 10) | (225 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CB);
+ writel((225 << 20) | (820 << 10) | (1004 << 0),
+ mixer_base + S5P_MXR_CM_COEFF_CR);
+ break;
+
+ default:
+ tvout_err("invalid csc_type parameter = %d\n", csc_type);
+ break;
+ }
+}
+
+void s5p_mixer_start(void)
+{
+ writel((readl(mixer_base + S5P_MXR_STATUS) | S5P_MXR_STATUS_RUN),
+ mixer_base + S5P_MXR_STATUS);
+}
+
+void s5p_mixer_stop(void)
+{
+ u32 reg = readl(mixer_base + S5P_MXR_STATUS);
+
+ reg &= ~S5P_MXR_STATUS_RUN;
+
+ writel(reg, mixer_base + S5P_MXR_STATUS);
+
+ do {
+ reg = readl(mixer_base + S5P_MXR_STATUS);
+ } while (!(reg & S5P_MXR_STATUS_IDLE_MODE));
+}
+
+void s5p_mixer_set_underflow_int_enable(enum s5p_mixer_layer layer, bool en)
+{
+ u32 enable_mask = 0;
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ enable_mask = S5P_MXR_INT_EN_VP_ENABLE;
+ break;
+
+ case MIXER_GPR0_LAYER:
+ enable_mask = S5P_MXR_INT_EN_GRP0_ENABLE;
+ break;
+
+ case MIXER_GPR1_LAYER:
+ enable_mask = S5P_MXR_INT_EN_GRP1_ENABLE;
+ break;
+ }
+
+ if (en)
+ writel((readl(mixer_base + S5P_MXR_INT_EN) | enable_mask),
+ mixer_base + S5P_MXR_INT_EN);
+ else
+ writel((readl(mixer_base + S5P_MXR_INT_EN) & ~enable_mask),
+ mixer_base + S5P_MXR_INT_EN);
+}
+
+void s5p_mixer_set_vsync_interrupt(bool en)
+{
+ if (en) {
+ writel(S5P_MXR_INT_STATUS_VSYNC_CLEARED, mixer_base +
+ S5P_MXR_INT_STATUS);
+ writel((readl(mixer_base + S5P_MXR_INT_EN) |
+ S5P_MXR_INT_EN_VSYNC_ENABLE),
+ mixer_base + S5P_MXR_INT_EN);
+ } else {
+ writel((readl(mixer_base + S5P_MXR_INT_EN) &
+ ~S5P_MXR_INT_EN_VSYNC_ENABLE),
+ mixer_base + S5P_MXR_INT_EN);
+ }
+
+ tvout_dbg("%s mixer VSYNC interrupt.\n", en ? "Enable" : "Disable");
+}
+
+void s5p_mixer_clear_pend_all(void)
+{
+ writel(S5P_MXR_INT_STATUS_INT_FIRED | S5P_MXR_INT_STATUS_VP_FIRED |
+ S5P_MXR_INT_STATUS_GRP0_FIRED | S5P_MXR_INT_STATUS_GRP1_FIRED,
+ mixer_base + S5P_MXR_INT_STATUS);
+}
+
+irqreturn_t s5p_mixer_irq(int irq, void *dev_id)
+{
+ bool v_i_f;
+ bool g0_i_f;
+ bool g1_i_f;
+ bool mxr_i_f;
+ u32 temp_reg = 0;
+
+ spin_lock_irq(&lock_mixer);
+
+ v_i_f = (readl(mixer_base + S5P_MXR_INT_STATUS)
+ & S5P_MXR_INT_STATUS_VP_FIRED) ? true : false;
+ g0_i_f = (readl(mixer_base + S5P_MXR_INT_STATUS)
+ & S5P_MXR_INT_STATUS_GRP0_FIRED) ? true : false;
+ g1_i_f = (readl(mixer_base + S5P_MXR_INT_STATUS)
+ & S5P_MXR_INT_STATUS_GRP1_FIRED) ? true : false;
+ mxr_i_f = (readl(mixer_base + S5P_MXR_INT_STATUS)
+ & S5P_MXR_INT_STATUS_INT_FIRED) ? true : false;
+
+ if (mxr_i_f) {
+ temp_reg |= S5P_MXR_INT_STATUS_INT_FIRED;
+
+ if (v_i_f) {
+ temp_reg |= S5P_MXR_INT_STATUS_VP_FIRED;
+ tvout_dbg("VP fifo under run!!\n");
+ }
+
+ if (g0_i_f) {
+ temp_reg |= S5P_MXR_INT_STATUS_GRP0_FIRED;
+ tvout_dbg("GRP0 fifo under run!!\n");
+ }
+
+ if (g1_i_f) {
+ temp_reg |= S5P_MXR_INT_STATUS_GRP1_FIRED;
+ tvout_dbg("GRP1 fifo under run!!\n");
+ }
+
+ if (!v_i_f && !g0_i_f && !g1_i_f) {
+ writel(S5P_MXR_INT_STATUS_VSYNC_CLEARED,
+ mixer_base + S5P_MXR_INT_STATUS);
+ wake_up(&s5ptv_wq);
+ } else {
+ writel(temp_reg, mixer_base + S5P_MXR_INT_STATUS);
+ }
+ }
+
+ spin_unlock_irq(&lock_mixer);
+
+ return IRQ_HANDLED;
+}
+
+void s5p_mixer_init(void __iomem *addr)
+{
+ mixer_base = addr;
+
+ spin_lock_init(&lock_mixer);
+}
new file mode 100644
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Functions of mixer ctrl class 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/clk.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include "hw_if/hw_if.h"
+#include "s5p_tvout_ctrl.h"
+
+enum {
+ ACLK,
+ MUX,
+ NO_OF_CLK
+};
+
+struct s5p_bg_color {
+ u32 color_y;
+ u32 color_cb;
+ u32 color_cr;
+};
+
+struct s5p_mixer_video_layer_info {
+ bool layer_blend;
+ u32 alpha;
+ u32 priority;
+
+ bool use_video_layer;
+};
+
+struct s5p_mixer_grp_layer_info {
+ bool pixel_blend;
+ bool layer_blend;
+ u32 alpha;
+
+ bool chroma_enable;
+ u32 chroma_key;
+
+ bool pre_mul_mode;
+
+ u32 src_x;
+ u32 src_y;
+ u32 dst_x;
+ u32 dst_y;
+ u32 width;
+ u32 height;
+ dma_addr_t fb_addr;
+
+ bool use_grp_layer;
+
+ u32 priority;
+ enum s5p_mixer_color_fmt format;
+
+ enum s5ptvfb_ver_scaling_t ver;
+ enum s5ptvfb_hor_scaling_t hor;
+};
+
+struct s5p_mixer_ctrl_private_data {
+ char *pow_name;
+ struct s5p_tvout_clk_info clk[NO_OF_CLK];
+ struct irq_info irq;
+ struct reg_mem_info reg_mem;
+
+ enum s5p_mixer_burst_mode burst;
+ enum s5p_tvout_endian endian;
+ struct s5p_bg_color bg_color[3];
+
+ struct s5p_mixer_video_layer_info v_layer;
+ struct s5p_mixer_grp_layer_info layer[S5PTV_FB_CNT];
+
+ bool running;
+};
+
+static struct s5p_mixer_ctrl_private_data s5p_mixer_ctrl_private = {
+ .pow_name = "mixer_pd",
+
+ .clk[ACLK] = {
+ .name = "mixer",
+ .ptr = NULL
+ },
+ .clk[MUX] = {
+ .name = "sclk_mixer",
+ .ptr = NULL
+ },
+ .irq = {
+ .name = "s5p-mixer",
+ .handler = s5p_mixer_irq,
+ .no = -1
+ },
+ .reg_mem = {
+ .name = "s5p-mixer",
+ .res = NULL,
+ .base = NULL
+ },
+
+ .burst = MIXER_BURST_16,
+ .endian = TVOUT_LITTLE_ENDIAN,
+
+ .bg_color[0].color_y = 0,
+ .bg_color[0].color_cb = 128,
+ .bg_color[0].color_cr = 128,
+ .bg_color[1].color_y = 0,
+ .bg_color[1].color_cb = 128,
+ .bg_color[1].color_cr = 128,
+ .bg_color[2].color_y = 0,
+ .bg_color[2].color_cb = 128,
+ .bg_color[2].color_cr = 128,
+
+ .v_layer = {
+ .layer_blend = false,
+ .alpha = 0xff,
+ .priority = 10
+ },
+ .layer[MIXER_GPR0_LAYER] = {
+ .pixel_blend = false,
+ .layer_blend = false,
+ .alpha = 0xff,
+ .chroma_enable = false,
+ .chroma_key = 0x0,
+ .pre_mul_mode = false,
+ .src_x = 0,
+ .src_y = 0,
+ .dst_x = 0,
+ .dst_y = 0,
+ .width = 0,
+ .height = 0,
+ .priority = 10,
+ .format = MIXER_RGB8888,
+ .ver = VERTICAL_X1,
+ .hor = HORIZONTAL_X1
+ },
+ .layer[MIXER_GPR1_LAYER] = {
+ .pixel_blend = false,
+ .layer_blend = false,
+ .alpha = 0xff,
+ .chroma_enable = false,
+ .chroma_key = 0x0,
+ .pre_mul_mode = false,
+ .src_x = 0,
+ .src_y = 0,
+ .dst_x = 0,
+ .dst_y = 0,
+ .width = 0,
+ .height = 0,
+ .priority = 10,
+ .format = MIXER_RGB8888,
+ .ver = VERTICAL_X1,
+ .hor = HORIZONTAL_X1
+ },
+ .running = false,
+};
+
+static int s5p_mixer_ctrl_set_reg(enum s5p_mixer_layer layer)
+{
+ bool layer_blend;
+ u32 alpha;
+ u32 priority;
+ struct s5ptvfb_user_scaling scaling;
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ layer_blend = s5p_mixer_ctrl_private.v_layer.layer_blend;
+ alpha = s5p_mixer_ctrl_private.v_layer.alpha;
+ priority = s5p_mixer_ctrl_private.v_layer.priority;
+ break;
+
+ case MIXER_GPR0_LAYER:
+ case MIXER_GPR1_LAYER:
+ layer_blend = s5p_mixer_ctrl_private.layer[layer].layer_blend;
+ alpha = s5p_mixer_ctrl_private.layer[layer].alpha;
+ priority = s5p_mixer_ctrl_private.layer[layer].priority;
+
+ s5p_mixer_set_pre_mul_mode(layer,
+ s5p_mixer_ctrl_private.layer[layer].pre_mul_mode);
+
+ s5p_mixer_set_chroma_key(layer,
+ s5p_mixer_ctrl_private.layer[layer].chroma_enable,
+ s5p_mixer_ctrl_private.layer[layer].chroma_key);
+
+ s5p_mixer_set_grp_layer_dst_pos(layer,
+ s5p_mixer_ctrl_private.layer[layer].dst_x,
+ s5p_mixer_ctrl_private.layer[layer].dst_y);
+
+ scaling.ver = s5p_mixer_ctrl_private.layer[layer].ver;
+ scaling.hor = s5p_mixer_ctrl_private.layer[layer].hor;
+
+ s5p_mixer_scaling(layer, scaling);
+ s5p_mixer_set_grp_base_address(layer,
+ s5p_mixer_ctrl_private.layer[layer].fb_addr);
+
+ s5p_mixer_set_color_format(layer,
+ s5p_mixer_ctrl_private.layer[layer].format);
+
+ s5p_mixer_set_grp_layer_src_pos(layer,
+ s5p_mixer_ctrl_private.layer[layer].src_x,
+ s5p_mixer_ctrl_private.layer[layer].src_y,
+ s5p_mixer_ctrl_private.layer[layer].width,
+ s5p_mixer_ctrl_private.layer[layer].width,
+ s5p_mixer_ctrl_private.layer[layer].height);
+
+ s5p_mixer_set_pixel_blend(layer, s5p_mixer_ctrl_private.layer[layer].pixel_blend);
+ break;
+
+ default:
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ s5p_mixer_set_layer_blend(layer, layer_blend);
+ s5p_mixer_set_alpha(layer, alpha);
+ s5p_mixer_set_priority(layer, priority);
+
+ return 0;
+}
+
+static void s5p_mixer_ctrl_clock(bool on)
+{
+ /* power control function is not implemented yet */
+ if (on) {
+ clk_enable(s5p_mixer_ctrl_private.clk[MUX].ptr);
+ clk_enable(s5p_mixer_ctrl_private.clk[ACLK].ptr);
+ } else {
+ clk_disable(s5p_mixer_ctrl_private.clk[ACLK].ptr);
+ clk_disable(s5p_mixer_ctrl_private.clk[MUX].ptr);
+ }
+}
+
+void s5p_mixer_ctrl_init_fb_addr_phy(enum s5p_mixer_layer layer,
+ dma_addr_t fb_addr)
+{
+ s5p_mixer_ctrl_private.layer[layer].fb_addr = fb_addr;
+}
+
+void s5p_mixer_ctrl_init_grp_layer(enum s5p_mixer_layer layer)
+{
+ struct s5ptvfb_user_scaling scaling;
+
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_set_priority(layer,
+ s5p_mixer_ctrl_private.layer[layer].priority);
+
+ s5p_mixer_set_pre_mul_mode(layer,
+ s5p_mixer_ctrl_private.layer[layer].pre_mul_mode);
+
+ s5p_mixer_set_chroma_key(layer,
+ s5p_mixer_ctrl_private.layer[layer].chroma_enable,
+ s5p_mixer_ctrl_private.layer[layer].chroma_key);
+
+ s5p_mixer_set_layer_blend(layer,
+ s5p_mixer_ctrl_private.layer[layer].layer_blend);
+
+ s5p_mixer_set_alpha(layer,
+ s5p_mixer_ctrl_private.layer[layer].alpha);
+
+ s5p_mixer_set_grp_layer_dst_pos(layer,
+ s5p_mixer_ctrl_private.layer[layer].dst_x,
+ s5p_mixer_ctrl_private.layer[layer].dst_y);
+
+ scaling.ver = s5p_mixer_ctrl_private.layer[layer].ver;
+ scaling.hor = s5p_mixer_ctrl_private.layer[layer].hor;
+
+ s5p_mixer_scaling(layer, scaling);
+
+ s5p_mixer_set_grp_base_address(layer,
+ s5p_mixer_ctrl_private.layer[layer].fb_addr);
+ }
+}
+
+int s5p_mixer_ctrl_set_pixel_format(enum s5p_mixer_layer layer,
+ u32 bpp, u32 trans_len)
+{
+ enum s5p_mixer_color_fmt format;
+
+ switch (bpp) {
+ case 16:
+ if (trans_len == 1)
+ format = MIXER_RGB1555;
+ else if (trans_len == 4)
+ format = MIXER_RGB4444;
+ else
+ format = MIXER_RGB565;
+ break;
+
+ case 32:
+ format = MIXER_RGB8888;
+ break;
+
+ default:
+ tvout_err("invalid bits per pixel\n");
+ return -1;
+ }
+ s5p_mixer_ctrl_private.layer[layer].format = format;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_color_format(layer, format);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_enable_layer(enum s5p_mixer_layer layer)
+{
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ s5p_mixer_ctrl_private.v_layer.use_video_layer = true;
+ break;
+
+ case MIXER_GPR0_LAYER:
+ case MIXER_GPR1_LAYER:
+ s5p_mixer_ctrl_private.layer[layer].use_grp_layer = true;
+ break;
+
+ default:
+ tvout_err("invalid layer\n");
+
+ return -1;
+ }
+
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_ctrl_set_reg(layer);
+ s5p_mixer_set_show(layer, true);
+ }
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_disable_layer(enum s5p_mixer_layer layer)
+{
+ bool use_vid, use_grp0, use_grp1;
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ s5p_mixer_ctrl_private.v_layer.use_video_layer = false;
+ break;
+
+ case MIXER_GPR0_LAYER:
+ case MIXER_GPR1_LAYER:
+ s5p_mixer_ctrl_private.layer[layer].use_grp_layer = false;
+ break;
+
+ default:
+ tvout_err("invalid layer\n");
+
+ return -1;
+ }
+
+ use_vid = s5p_mixer_ctrl_private.v_layer.use_video_layer;
+ use_grp0 = s5p_mixer_ctrl_private.layer[MIXER_GPR0_LAYER].use_grp_layer;
+ use_grp1 = s5p_mixer_ctrl_private.layer[MIXER_GPR1_LAYER].use_grp_layer;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_show(layer, false);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_priority(enum s5p_mixer_layer layer, int prio)
+{
+ if ((prio < 0) || (prio > 15)) {
+ tvout_err("layer priority range : 0 - 15\n");
+ return -1;
+ }
+
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ s5p_mixer_ctrl_private.v_layer.priority = prio;
+ break;
+
+ case MIXER_GPR0_LAYER:
+ case MIXER_GPR1_LAYER:
+ s5p_mixer_ctrl_private.layer[layer].priority = prio;
+ break;
+
+ default:
+ tvout_err("invalid layer\n");
+
+ return -1;
+ }
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_priority(layer, (u32)prio);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_dst_win_pos(enum s5p_mixer_layer layer,
+ int dst_x, int dst_y, u32 w, u32 h)
+{
+ u32 w_t, h_t;
+ enum s5p_tvout_disp_mode std;
+ enum s5p_tvout_o_mode inf;
+
+ if ((layer != MIXER_GPR0_LAYER) && (layer != MIXER_GPR1_LAYER)) {
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ s5p_tvif_ctrl_get_std_if(&std, &inf);
+ tvout_dbg("standard no = %d, output mode no = %d\n", std, inf);
+
+ /*
+ * 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 (std) {
+ 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 (dst_x < 0)
+ dst_x = 0;
+
+ if (dst_y < 0)
+ dst_y = 0;
+
+ if (dst_x + w > w_t)
+ dst_x = w_t - w;
+
+ if (dst_y + h > h_t)
+ dst_y = h_t - h;
+
+ tvout_dbg("destination coordinates : x = %d, y = %d\n", dst_x, dst_y);
+ tvout_dbg("output device screen size : width = %d, height = %d", w_t, h_t);
+
+ s5p_mixer_ctrl_private.layer[layer].dst_x = dst_x;
+ s5p_mixer_ctrl_private.layer[layer].dst_y = dst_y;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_grp_layer_dst_pos(layer, dst_x, dst_y);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_src_win_pos(enum s5p_mixer_layer layer,
+ u32 src_x, u32 src_y, u32 w, u32 h)
+{
+ if ((layer != MIXER_GPR0_LAYER) && (layer != MIXER_GPR1_LAYER)) {
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ tvout_dbg("source coordinates : x = %d, y = %d\n", src_x, src_y);
+ tvout_dbg("source size : width = %d, height = %d\n", w, h);
+
+ s5p_mixer_ctrl_private.layer[layer].src_x = src_x;
+ s5p_mixer_ctrl_private.layer[layer].src_y = src_y;
+ s5p_mixer_ctrl_private.layer[layer].width = w;
+ s5p_mixer_ctrl_private.layer[layer].height = h;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_grp_layer_src_pos(layer, src_x, src_y, w, w, h);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_buffer_address(enum s5p_mixer_layer layer,
+ dma_addr_t start_addr)
+{
+ if ((layer != MIXER_GPR0_LAYER) && (layer != MIXER_GPR1_LAYER)) {
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ tvout_dbg("TV frame buffer base address = 0x%x\n", start_addr);
+
+ s5p_mixer_ctrl_private.layer[layer].fb_addr = start_addr;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_grp_base_address(layer, start_addr);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_chroma_key(enum s5p_mixer_layer layer,
+ struct s5ptvfb_chroma chroma)
+{
+ bool enabled = (chroma.enabled) ? true : false;
+
+ if ((layer != MIXER_GPR0_LAYER) && (layer != MIXER_GPR1_LAYER)) {
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ s5p_mixer_ctrl_private.layer[layer].chroma_enable = enabled;
+ s5p_mixer_ctrl_private.layer[layer].chroma_key = chroma.key;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_chroma_key(layer, enabled, chroma.key);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_alpha(enum s5p_mixer_layer layer, u32 alpha)
+{
+ switch (layer) {
+ case MIXER_VIDEO_LAYER:
+ s5p_mixer_ctrl_private.v_layer.alpha = alpha;
+ break;
+
+ case MIXER_GPR0_LAYER:
+ case MIXER_GPR1_LAYER:
+ s5p_mixer_ctrl_private.layer[layer].alpha = alpha;
+ break;
+
+ default:
+ tvout_err("invalid layer\n");
+
+ return -1;
+ }
+
+ tvout_dbg("alpha value = 0x%x\n", alpha);
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_alpha(layer, alpha);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_blend_mode(enum s5p_mixer_layer layer,
+ enum s5ptvfb_alpha_t mode)
+{
+ if ((layer != MIXER_VIDEO_LAYER) && (layer != MIXER_GPR0_LAYER) &&
+ (layer != MIXER_GPR1_LAYER)) {
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ if ((layer == MIXER_VIDEO_LAYER) && (mode == PIXEL_BLENDING)) {
+ tvout_err("video layer doesn't support pixel blending\n");
+ return -1;
+ }
+
+ switch (mode) {
+ case PIXEL_BLENDING:
+ tvout_dbg("pixel blending\n");
+
+ s5p_mixer_ctrl_private.layer[layer].pixel_blend = true;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_pixel_blend(layer, true);
+ break;
+
+ case LAYER_BLENDING:
+ tvout_dbg("layer blending\n");
+
+ if (layer == MIXER_VIDEO_LAYER)
+ s5p_mixer_ctrl_private.v_layer.layer_blend = true;
+ else /* graphic layer */
+ s5p_mixer_ctrl_private.layer[layer].layer_blend = true;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_layer_blend(layer, true);
+ break;
+
+ case NONE_BLENDING:
+ tvout_dbg("alpha blending off\n");
+
+ if (layer == MIXER_VIDEO_LAYER) {
+ s5p_mixer_ctrl_private.v_layer.layer_blend = false;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_layer_blend(layer, false);
+ } else { /* graphic layer */
+ s5p_mixer_ctrl_private.layer[layer].pixel_blend = false;
+ s5p_mixer_ctrl_private.layer[layer].layer_blend = false;
+
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_set_layer_blend(layer, false);
+ s5p_mixer_set_pixel_blend(layer, false);
+ }
+ }
+ break;
+
+ default:
+ tvout_err("invalid blending mode\n");
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_set_alpha_blending(enum s5p_mixer_layer layer,
+ enum s5ptvfb_alpha_t blend_mode,
+ unsigned int alpha)
+{
+ if ((layer != MIXER_GPR0_LAYER) && (layer != MIXER_GPR1_LAYER)) {
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ switch (blend_mode) {
+ case PIXEL_BLENDING:
+ tvout_dbg("pixel blending\n");
+
+ s5p_mixer_ctrl_private.layer[layer].pixel_blend = true;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_pixel_blend(layer, true);
+ break;
+
+ case LAYER_BLENDING:
+ tvout_dbg("layer blending : alpha value = 0x%x\n", alpha);
+
+ s5p_mixer_ctrl_private.layer[layer].layer_blend = true;
+ s5p_mixer_ctrl_private.layer[layer].alpha = alpha;
+
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_set_layer_blend(layer, true);
+ s5p_mixer_set_alpha(layer, alpha);
+ }
+ break;
+
+ case NONE_BLENDING:
+ tvout_dbg("alpha blending off\n");
+ s5p_mixer_ctrl_private.layer[layer].pixel_blend = false;
+ s5p_mixer_ctrl_private.layer[layer].layer_blend = false;
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_set_pixel_blend(layer, false);
+ s5p_mixer_set_layer_blend(layer, false);
+ }
+ break;
+
+ default:
+ tvout_err("invalid blending mode\n");
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_scaling(enum s5p_mixer_layer layer,
+ struct s5ptvfb_user_scaling scaling)
+{
+ if ((layer != MIXER_GPR0_LAYER) && (layer != MIXER_GPR1_LAYER)) {
+ tvout_err("invalid layer\n");
+
+ return -1;
+ }
+
+ if ((scaling.ver != VERTICAL_X1) && (scaling.ver != VERTICAL_X2)) {
+ tvout_err("invalid vertical size\n");
+
+ return -1;
+ }
+
+ if ((scaling.hor != HORIZONTAL_X1) && (scaling.hor != HORIZONTAL_X2)) {
+ tvout_err("invalid horizontal size\n");
+
+ return -1;
+ }
+
+ s5p_mixer_ctrl_private.layer[layer].ver = scaling.ver;
+ s5p_mixer_ctrl_private.layer[layer].hor = scaling.hor;
+
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_scaling(layer, scaling);
+
+ return 0;
+}
+
+int s5p_mixer_ctrl_mux_clk(struct clk *ptr)
+{
+ if (clk_set_parent(s5p_mixer_ctrl_private.clk[MUX].ptr, ptr)) {
+ tvout_err("mixer clock mux failed\n");
+
+ return -1;
+ }
+ return 0;
+}
+
+void s5p_mixer_ctrl_set_int_enable(bool en)
+{
+ tvout_dbg("mixer layers' underflow interrupts are %s\n",
+ en ? "enabled" : "disabled");
+
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_set_underflow_int_enable(MIXER_VIDEO_LAYER, en);
+ s5p_mixer_set_underflow_int_enable(MIXER_GPR0_LAYER, en);
+ s5p_mixer_set_underflow_int_enable(MIXER_GPR1_LAYER, en);
+ }
+}
+
+void s5p_mixer_ctrl_set_vsync_interrupt(bool en)
+{
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_set_vsync_interrupt(en);
+}
+
+void s5p_mixer_ctrl_clear_pend_all(void)
+{
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_clear_pend_all();
+}
+
+void s5p_mixer_ctrl_stop(void)
+{
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_stop();
+
+ s5p_mixer_ctrl_clock(0);
+ s5p_mixer_ctrl_private.running = false;
+ }
+}
+
+void s5p_mixer_ctrl_internal_start(void)
+{
+ if (s5p_mixer_ctrl_private.running)
+ s5p_mixer_start();
+}
+
+int s5p_mixer_ctrl_start(enum s5p_tvout_disp_mode disp,
+ enum s5p_tvout_o_mode out)
+{
+ int i;
+ enum s5p_mixer_burst_mode burst = s5p_mixer_ctrl_private.burst;
+ enum s5p_tvout_endian endian = s5p_mixer_ctrl_private.endian;
+ struct clk *sclk_mixer = s5p_mixer_ctrl_private.clk[MUX].ptr;
+
+ /*
+ * Getting mega struct member variable will
+ * be replaced another tvout interface
+ */
+ struct s5p_tvout_status *st = &s5ptv_status;
+
+ switch (out) {
+ case TVOUT_COMPOSITE:
+ clk_set_parent(sclk_mixer, st->sclk_dac);
+
+ if (!s5p_mixer_ctrl_private.running) {
+ s5p_mixer_ctrl_clock(true);
+ s5p_mixer_ctrl_private.running = true;
+ }
+
+ s5p_mixer_init_csc_coef_default(MIXER_CSC_601_FR);
+ break;
+
+ case TVOUT_HDMI_RGB:
+ case TVOUT_HDMI:
+ case TVOUT_DVI:
+ clk_set_parent(sclk_mixer, st->sclk_hdmi);
+ clk_set_parent(st->sclk_hdmi, st->sclk_hdmiphy);
+
+ if (!s5p_mixer_ctrl_private.running) {
+ s5p_mixer_ctrl_clock(true);
+ s5p_mixer_ctrl_private.running = true;
+ }
+
+ switch (disp) {
+
+ case TVOUT_480P_60_16_9:
+ case TVOUT_480P_60_4_3:
+ case TVOUT_480P_59:
+ case TVOUT_576P_50_16_9:
+ case TVOUT_576P_50_4_3:
+ s5p_mixer_init_csc_coef_default(MIXER_CSC_601_FR);
+ break;
+
+ case TVOUT_720P_60:
+ case TVOUT_720P_50:
+ case TVOUT_720P_59:
+ case TVOUT_1080I_60:
+ case TVOUT_1080I_59:
+ case TVOUT_1080I_50:
+ case TVOUT_1080P_60:
+ case TVOUT_1080P_30:
+ case TVOUT_1080P_59:
+ case TVOUT_1080P_50:
+ s5p_mixer_init_csc_coef_default(MIXER_CSC_709_FR);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ tvout_err("invalid tvout output mode = %d\n", out);
+ return -1;
+ }
+
+ tvout_dbg("%s burst mode\n", burst ? "16" : "8");
+ tvout_dbg("%s endian\n", endian ? "big" : "little");
+
+ if ((burst != MIXER_BURST_8) && (burst != MIXER_BURST_16)) {
+ tvout_err("invalid burst mode\n");
+ return -1;
+ }
+
+ if ((endian != TVOUT_BIG_ENDIAN) && (endian != TVOUT_LITTLE_ENDIAN)) {
+ tvout_err("invalid endian\n");
+ return -1;
+ }
+
+ s5p_mixer_init_status_reg(burst, endian);
+
+ tvout_dbg("tvout standard = 0x%X, output mode = %d\n", disp, out);
+
+ /* error handling will be implemented */
+ s5p_mixer_init_display_mode(disp, out);
+
+ for (i = MIXER_BG_COLOR_0; i <= MIXER_BG_COLOR_2; i++) {
+ s5p_mixer_set_bg_color(i,
+ s5p_mixer_ctrl_private.bg_color[i].color_y,
+ s5p_mixer_ctrl_private.bg_color[i].color_cb,
+ s5p_mixer_ctrl_private.bg_color[i].color_cr);
+ }
+
+ if (s5p_mixer_ctrl_private.v_layer.use_video_layer) {
+ s5p_mixer_ctrl_set_reg(MIXER_VIDEO_LAYER);
+ s5p_mixer_set_show(MIXER_VIDEO_LAYER, true);
+ }
+ if (s5p_mixer_ctrl_private.layer[MIXER_GPR0_LAYER].use_grp_layer) {
+ s5p_mixer_ctrl_set_reg(MIXER_GPR0_LAYER);
+ s5p_mixer_set_show(MIXER_GPR0_LAYER, true);
+ }
+ if (s5p_mixer_ctrl_private.layer[MIXER_GPR0_LAYER].use_grp_layer) {
+ s5p_mixer_ctrl_set_reg(MIXER_GPR0_LAYER);
+ s5p_mixer_set_show(MIXER_GPR0_LAYER, true);
+ }
+
+ s5p_mixer_start();
+
+ return 0;
+}
+
+wait_queue_head_t s5ptv_wq;
+
+int s5p_mixer_ctrl_constructor(struct platform_device *pdev)
+{
+ int ret = 0, i;
+
+ ret = s5p_tvout_map_resource_mem(
+ pdev,
+ s5p_mixer_ctrl_private.reg_mem.name,
+ &(s5p_mixer_ctrl_private.reg_mem.base),
+ &(s5p_mixer_ctrl_private.reg_mem.res));
+
+ if (ret)
+ goto err_on_res;
+
+ for (i = ACLK; i < NO_OF_CLK; i++) {
+ s5p_mixer_ctrl_private.clk[i].ptr =
+ clk_get(&pdev->dev, s5p_mixer_ctrl_private.clk[i].name);
+
+ if (IS_ERR(s5p_mixer_ctrl_private.clk[i].ptr)) {
+ printk(KERN_ERR "Failed to find clock %s\n",
+ s5p_mixer_ctrl_private.clk[i].name);
+ ret = -ENOENT;
+ goto err_on_clk;
+ }
+ }
+
+ s5p_mixer_ctrl_private.irq.no =
+ platform_get_irq_byname(pdev, s5p_mixer_ctrl_private.irq.name);
+
+ if (s5p_mixer_ctrl_private.irq.no < 0) {
+ tvout_err("Failed to call platform_get_irq_byname() for %s\n",
+ s5p_mixer_ctrl_private.irq.name);
+ ret = s5p_mixer_ctrl_private.irq.no;
+ goto err_on_irq;
+ }
+
+ ret = request_irq(s5p_mixer_ctrl_private.irq.no,
+ s5p_mixer_ctrl_private.irq.handler, IRQF_DISABLED,
+ s5p_mixer_ctrl_private.irq.name, NULL);
+ if (ret) {
+ tvout_err("Failed to call request_irq() for %s\n",
+ s5p_mixer_ctrl_private.irq.name);
+ goto err_on_irq;
+ }
+
+ /* Initializing wait queue for mixer vsync interrupt */
+ init_waitqueue_head(&s5ptv_wq);
+
+ s5p_mixer_init(s5p_mixer_ctrl_private.reg_mem.base);
+
+ return 0;
+
+err_on_irq:
+err_on_clk:
+ iounmap(s5p_mixer_ctrl_private.reg_mem.base);
+ release_resource(s5p_mixer_ctrl_private.reg_mem.res);
+ kfree(s5p_mixer_ctrl_private.reg_mem.res);
+
+err_on_res:
+ return ret;
+}
+
+void s5p_mixer_ctrl_destructor(void)
+{
+ int i;
+ int irq_no = s5p_mixer_ctrl_private.irq.no;
+
+ if (irq_no >= 0)
+ free_irq(irq_no, NULL);
+
+ s5p_tvout_unmap_resource_mem(
+ s5p_mixer_ctrl_private.reg_mem.base,
+ s5p_mixer_ctrl_private.reg_mem.res);
+
+ for (i = ACLK; i < NO_OF_CLK; i++) {
+ if (s5p_mixer_ctrl_private.clk[i].ptr) {
+ clk_disable(s5p_mixer_ctrl_private.clk[i].ptr);
+ clk_put(s5p_mixer_ctrl_private.clk[i].ptr);
+ }
+ }
+}
+
+bool pm_running;
+
+void s5p_mixer_ctrl_suspend(void)
+{
+ pm_running = s5p_mixer_ctrl_private.running;
+
+ if (s5p_mixer_ctrl_private.running) {
+ s5p_mixer_stop();
+ s5p_mixer_ctrl_clock(0);
+ s5p_mixer_ctrl_private.running = false;
+ }
+}
+
+void s5p_mixer_ctrl_resume(void)
+{
+ if (pm_running) {
+ if (!s5p_mixer_ctrl_private.running) {
+ s5p_mixer_ctrl_clock(1);
+ s5p_mixer_ctrl_private.running = true;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * V4L2 API for Samsung S5P TVOOUT 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/version.h>
+#include <linux/slab.h>
+#include <linux/videodev2_samsung.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "s5p_tvout_common_lib.h"
+#include "s5p_tvout_ctrl.h"
+#include "s5p_tvout_v4l2.h"
+
+#define MAJOR_VERSION 0
+#define MINOR_VERSION 9
+#define RELEASE_VERSION 0
+
+#define V4L2_STD_ALL_HD ((v4l2_std_id)0xffffffff)
+
+#define S5P_TVOUT_TVIF_MINOR 14
+#define S5P_TVOUT_VO_MINOR 21
+
+#define V4L2_OUTPUT_TYPE_COMPOSITE 5
+#define V4L2_OUTPUT_TYPE_HDMI 10
+#define V4L2_OUTPUT_TYPE_HDMI_RGB 11
+#define V4L2_OUTPUT_TYPE_DVI 12
+
+#define V4L2_STD_PAL_BDGHI (V4L2_STD_PAL_B | V4L2_STD_PAL_D | \
+ V4L2_STD_PAL_G | V4L2_STD_PAL_H | \
+ V4L2_STD_PAL_I)
+
+#define V4L2_STD_480P_60_16_9 ((v4l2_std_id)0x04000000)
+#define V4L2_STD_480P_60_4_3 ((v4l2_std_id)0x05000000)
+#define V4L2_STD_576P_50_16_9 ((v4l2_std_id)0x06000000)
+#define V4L2_STD_576P_50_4_3 ((v4l2_std_id)0x07000000)
+#define V4L2_STD_720P_60 ((v4l2_std_id)0x08000000)
+#define V4L2_STD_720P_50 ((v4l2_std_id)0x09000000)
+#define V4L2_STD_1080P_60 ((v4l2_std_id)0x0a000000)
+#define V4L2_STD_1080P_50 ((v4l2_std_id)0x0b000000)
+#define V4L2_STD_1080I_60 ((v4l2_std_id)0x0c000000)
+#define V4L2_STD_1080I_50 ((v4l2_std_id)0x0d000000)
+#define V4L2_STD_480P_59 ((v4l2_std_id)0x0e000000)
+#define V4L2_STD_720P_59 ((v4l2_std_id)0x0f000000)
+#define V4L2_STD_1080I_59 ((v4l2_std_id)0x10000000)
+#define V4L2_STD_1080P_59 ((v4l2_std_id)0x11000000)
+#define V4L2_STD_1080P_30 ((v4l2_std_id)0x12000000)
+
+#define CVBS_S_VIDEO (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP| \
+ V4L2_STD_PAL | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
+ V4L2_STD_PAL_60 | V4L2_STD_NTSC_443)
+
+struct v4l2_vid_overlay_src {
+ void *base_y;
+ void *base_c;
+ struct v4l2_pix_format pix_fmt;
+};
+
+static const struct v4l2_output s5p_tvout_tvif_output[] = {
+ {
+ .index = 0,
+ .name = "Analog COMPOSITE",
+ .type = V4L2_OUTPUT_TYPE_COMPOSITE,
+ .audioset = 0,
+ .modulator = 0,
+ .std = CVBS_S_VIDEO,
+ }, {
+ .index = 1,
+ .name = "Digital HDMI(YCbCr)",
+ .type = V4L2_OUTPUT_TYPE_HDMI,
+ .audioset = 2,
+ .modulator = 0,
+ .std = V4L2_STD_480P_60_16_9 |
+ V4L2_STD_480P_60_16_9 |
+ V4L2_STD_720P_60 |
+ V4L2_STD_720P_50 |
+ V4L2_STD_1080P_60 |
+ V4L2_STD_1080P_50 |
+ V4L2_STD_1080I_60 |
+ V4L2_STD_1080I_50 |
+ V4L2_STD_480P_59 |
+ V4L2_STD_720P_59 |
+ V4L2_STD_1080I_59 |
+ V4L2_STD_1080P_59 |
+ V4L2_STD_1080P_30,
+ }, {
+ .index = 2,
+ .name = "Digital HDMI(RGB)",
+ .type = V4L2_OUTPUT_TYPE_HDMI_RGB,
+ .audioset = 2,
+ .modulator = 0,
+ .std = V4L2_STD_480P_60_16_9 |
+ V4L2_STD_480P_60_16_9 |
+ V4L2_STD_720P_60 |
+ V4L2_STD_720P_50 |
+ V4L2_STD_1080P_60 |
+ V4L2_STD_1080P_50 |
+ V4L2_STD_1080I_60 |
+ V4L2_STD_1080I_50 |
+ V4L2_STD_480P_59 |
+ V4L2_STD_720P_59 |
+ V4L2_STD_1080I_59 |
+ V4L2_STD_1080P_59 |
+ V4L2_STD_1080P_30,
+ }, {
+ .index = 3,
+ .name = "Digital DVI",
+ .type = V4L2_OUTPUT_TYPE_DVI,
+ .audioset = 2,
+ .modulator = 0,
+ .std = V4L2_STD_480P_60_16_9 |
+ V4L2_STD_480P_60_16_9 |
+ V4L2_STD_720P_60 |
+ V4L2_STD_720P_50 |
+ V4L2_STD_1080P_60 |
+ V4L2_STD_1080P_50 |
+ V4L2_STD_1080I_60 |
+ V4L2_STD_1080I_50 |
+ V4L2_STD_480P_59 |
+ V4L2_STD_720P_59 |
+ V4L2_STD_1080I_59 |
+ V4L2_STD_1080P_59 |
+ V4L2_STD_1080P_30,
+ }
+
+};
+
+#define S5P_TVOUT_TVIF_NO_OF_OUTPUT ARRAY_SIZE(s5p_tvout_tvif_output)
+
+static const struct v4l2_standard s5p_tvout_tvif_standard[] = {
+ {
+ .index = 0,
+ .id = V4L2_STD_NTSC_M,
+ .name = "NTSC_M",
+ }, {
+ .index = 1,
+ .id = V4L2_STD_PAL_BDGHI,
+ .name = "PAL_BDGHI",
+ }, {
+ .index = 2,
+ .id = V4L2_STD_PAL_M,
+ .name = "PAL_M",
+ }, {
+ .index = 3,
+ .id = V4L2_STD_PAL_N,
+ .name = "PAL_N",
+ }, {
+ .index = 4,
+ .id = V4L2_STD_PAL_Nc,
+ .name = "PAL_Nc",
+ }, {
+ .index = 5,
+ .id = V4L2_STD_PAL_60,
+ .name = "PAL_60",
+ }, {
+ .index = 6,
+ .id = V4L2_STD_NTSC_443,
+ .name = "NTSC_443",
+ }, {
+ .index = 7,
+ .id = V4L2_STD_480P_60_16_9,
+ .name = "480P_60_16_9",
+ }, {
+ .index = 8,
+ .id = V4L2_STD_480P_60_4_3,
+ .name = "480P_60_4_3",
+ }, {
+ .index = 9,
+ .id = V4L2_STD_576P_50_16_9,
+ .name = "576P_50_16_9",
+ }, {
+ .index = 10,
+ .id = V4L2_STD_576P_50_4_3,
+ .name = "576P_50_4_3",
+ }, {
+ .index = 11,
+ .id = V4L2_STD_720P_60,
+ .name = "720P_60",
+ }, {
+ .index = 12,
+ .id = V4L2_STD_720P_50,
+ .name = "720P_50",
+ }, {
+ .index = 13,
+ .id = V4L2_STD_1080P_60,
+ .name = "1080P_60",
+ }, {
+ .index = 14,
+ .id = V4L2_STD_1080P_50,
+ .name = "1080P_50",
+ }, {
+ .index = 15,
+ .id = V4L2_STD_1080I_60,
+ .name = "1080I_60",
+ }, {
+ .index = 16,
+ .id = V4L2_STD_1080I_50,
+ .name = "1080I_50",
+ }, {
+ .index = 17,
+ .id = V4L2_STD_480P_59,
+ .name = "480P_59",
+ }, {
+ .index = 18,
+ .id = V4L2_STD_720P_59,
+ .name = "720P_59",
+ }, {
+ .index = 19,
+ .id = V4L2_STD_1080I_59,
+ .name = "1080I_59",
+ }, {
+ .index = 20,
+ .id = V4L2_STD_1080P_59,
+ .name = "1080I_50",
+ }, {
+ .index = 21,
+ .id = V4L2_STD_1080P_30,
+ .name = "1080I_30",
+ }
+};
+
+#define S5P_TVOUT_TVIF_NO_OF_STANDARD ARRAY_SIZE(s5p_tvout_tvif_standard)
+
+static const struct v4l2_fmtdesc s5p_tvout_vo_fmt_desc[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_PRIVATE,
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ .description = "NV12 (Linear YUV420 2 Planes)",
+ }, {
+ .index = 1,
+ .type = V4L2_BUF_TYPE_PRIVATE,
+ .pixelformat = V4L2_PIX_FMT_NV12T,
+ .description = "NV12T (Tiled YUV420 2 Planes)",
+ }, {
+ .index = 2,
+ .type = V4L2_BUF_TYPE_PRIVATE,
+ .pixelformat = V4L2_PIX_FMT_NV21,
+ .description = "NV12 (Linear YUV420 2 Planes)",
+ }, {
+ .index = 3,
+ .type = V4L2_BUF_TYPE_PRIVATE,
+ .pixelformat = V4L2_PIX_FMT_NV21T,
+ .description = "NV12T (Tiled YUV420 2 Planes)",
+ },
+};
+
+static DEFINE_MUTEX(s5p_tvout_tvif_mutex);
+static DEFINE_MUTEX(s5p_tvout_vo_mutex);
+
+struct s5p_tvout_v4l2_private_data {
+ struct v4l2_vid_overlay_src vo_src_fmt;
+ struct v4l2_rect vo_src_rect;
+ struct v4l2_window vo_dst_fmt;
+ struct v4l2_framebuffer vo_dst_plane;
+
+ int tvif_output_index;
+ v4l2_std_id tvif_standard_id;
+
+ atomic_t tvif_use;
+ atomic_t vo_use;
+};
+
+static struct s5p_tvout_v4l2_private_data s5p_tvout_v4l2_private = {
+ .tvif_output_index = -1,
+ .tvif_standard_id = 0,
+
+ .tvif_use = ATOMIC_INIT(0),
+ .vo_use = ATOMIC_INIT(0),
+};
+
+static void s5p_tvout_v4l2_init_private(void)
+{
+}
+
+static int s5p_tvout_tvif_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "s5p-tvout-tvif");
+ strcpy(cap->card, "Samsung TVOUT TV Interface");
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT;
+ cap->version = KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION);
+
+ return 0;
+}
+
+static int s5p_tvout_tvif_g_std(struct file *file, void *fh,
+ v4l2_std_id *norm)
+{
+ if (s5p_tvout_v4l2_private.tvif_standard_id == 0) {
+ tvout_err("Standard has not set\n");
+ return -1;
+ }
+
+ *norm = s5p_tvout_v4l2_private.tvif_standard_id;
+
+ return 0;
+}
+
+static int s5p_tvout_tvif_s_std(struct file *file, void *fh,
+ v4l2_std_id *norm)
+{
+ int i;
+ v4l2_std_id std_id = *norm;
+
+ for (i = 0; i < S5P_TVOUT_TVIF_NO_OF_STANDARD; i++) {
+ if (s5p_tvout_tvif_standard[i].id == std_id)
+ break;
+ }
+
+ if (i == S5P_TVOUT_TVIF_NO_OF_STANDARD) {
+ tvout_err("There is no TV standard(0x%08Lx)\n", std_id);
+
+ return -EINVAL;
+ }
+
+ s5p_tvout_v4l2_private.tvif_standard_id = std_id;
+
+ tvout_dbg("standard id=0x%X, name=\"%s\"\n", (u32) std_id, s5p_tvout_tvif_standard[i].name);
+
+ return 0;
+}
+
+static int s5p_tvout_tvif_enum_output(struct file *file, void *fh,
+ struct v4l2_output *a)
+{
+ unsigned int index = a->index;
+
+ if (index >= S5P_TVOUT_TVIF_NO_OF_OUTPUT) {
+ tvout_err("Invalid index(%d)\n", index);
+
+ return -EINVAL;
+ }
+
+ memcpy(a, &s5p_tvout_tvif_output[index], sizeof(struct v4l2_output));
+
+ return 0;
+}
+
+static int s5p_tvout_tvif_g_output(struct file *file, void *fh, unsigned int *i)
+{
+ if (s5p_tvout_v4l2_private.tvif_output_index == -1) {
+ tvout_err("Output has not set\n");
+ return -EINVAL;
+ }
+
+ *i = s5p_tvout_v4l2_private.tvif_output_index;
+
+ return 0;
+}
+
+static int s5p_tvout_tvif_s_output(struct file *file, void *fh, unsigned int i)
+{
+ enum s5p_tvout_disp_mode tv_std;
+ enum s5p_tvout_o_mode tv_if;
+
+ if (i >= S5P_TVOUT_TVIF_NO_OF_OUTPUT) {
+ tvout_err("Invalid index(%d)\n", i);
+ return -EINVAL;
+ }
+
+ s5p_tvout_v4l2_private.tvif_output_index = i;
+
+ tvout_dbg("output id=%d, name=\"%s\"\n", (int) i, s5p_tvout_tvif_output[i].name);
+
+ switch (s5p_tvout_tvif_output[i].type) {
+ case V4L2_OUTPUT_TYPE_COMPOSITE:
+ tv_if = TVOUT_COMPOSITE;
+ break;
+
+ case V4L2_OUTPUT_TYPE_HDMI:
+ tv_if = TVOUT_HDMI;
+ break;
+
+ case V4L2_OUTPUT_TYPE_HDMI_RGB:
+ tv_if = TVOUT_HDMI_RGB;
+ break;
+
+ case V4L2_OUTPUT_TYPE_DVI:
+ tv_if = TVOUT_DVI;
+ break;
+
+ default:
+ tvout_err("Invalid output type(%d)\n", s5p_tvout_tvif_output[i].type);
+
+ return -1;
+ }
+
+ switch (s5p_tvout_v4l2_private.tvif_standard_id) {
+ case V4L2_STD_NTSC_M:
+ tv_std = TVOUT_NTSC_M;
+ break;
+
+ case V4L2_STD_PAL_BDGHI:
+ tv_std = TVOUT_PAL_BDGHI;
+ break;
+
+ case V4L2_STD_PAL_M:
+ tv_std = TVOUT_PAL_M;
+ break;
+
+ case V4L2_STD_PAL_N:
+ tv_std = TVOUT_PAL_N;
+ break;
+
+ case V4L2_STD_PAL_Nc:
+ tv_std = TVOUT_PAL_NC;
+ break;
+
+ case V4L2_STD_PAL_60:
+ tv_std = TVOUT_PAL_60;
+ break;
+
+ case V4L2_STD_NTSC_443:
+ tv_std = TVOUT_NTSC_443;
+ break;
+
+ case V4L2_STD_480P_60_16_9:
+ tv_std = TVOUT_480P_60_16_9;
+ break;
+
+ case V4L2_STD_480P_60_4_3:
+ tv_std = TVOUT_480P_60_4_3;
+ break;
+
+ case V4L2_STD_480P_59:
+ tv_std = TVOUT_480P_59;
+ break;
+ case V4L2_STD_576P_50_16_9:
+ tv_std = TVOUT_576P_50_16_9;
+ break;
+
+ case V4L2_STD_576P_50_4_3:
+ tv_std = TVOUT_576P_50_4_3;
+ break;
+
+ case V4L2_STD_720P_60:
+ tv_std = TVOUT_720P_60;
+ break;
+
+ case V4L2_STD_720P_59:
+ tv_std = TVOUT_720P_59;
+ break;
+
+ case V4L2_STD_720P_50:
+ tv_std = TVOUT_720P_50;
+ break;
+
+ case V4L2_STD_1080I_60:
+ tv_std = TVOUT_1080I_60;
+ break;
+
+ case V4L2_STD_1080I_59:
+ tv_std = TVOUT_1080I_59;
+ break;
+
+ case V4L2_STD_1080I_50:
+ tv_std = TVOUT_1080I_50;
+ break;
+
+ case V4L2_STD_1080P_30:
+ tv_std = TVOUT_1080P_30;
+ break;
+
+ case V4L2_STD_1080P_60:
+ tv_std = TVOUT_1080P_60;
+ break;
+
+ case V4L2_STD_1080P_59:
+ tv_std = TVOUT_1080P_59;
+ break;
+
+ case V4L2_STD_1080P_50:
+ tv_std = TVOUT_1080P_50;
+ break;
+
+ default:
+ tvout_err("Invalid standard id(0x%08Lx)\n",
+ s5p_tvout_v4l2_private.tvif_standard_id);
+
+ return -1;
+ }
+
+ s5p_tvif_ctrl_start(tv_std, tv_if);
+
+ return 0;
+};
+
+static int s5p_tvout_tvif_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *a)
+{
+ enum s5p_tvout_disp_mode std;
+ enum s5p_tvout_o_mode inf;
+
+ struct v4l2_cropcap *cropcap = a;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ tvout_err("Invalid buf type(%d)\n", cropcap->type);
+ return -EINVAL;
+ }
+
+ /* below part will be modified and moved to tvif ctrl class */
+ s5p_tvif_ctrl_get_std_if(&std, &inf);
+
+ switch (std) {
+ case TVOUT_NTSC_M:
+ case TVOUT_NTSC_443:
+ case TVOUT_480P_60_16_9:
+ case TVOUT_480P_60_4_3:
+ case TVOUT_480P_59:
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = 480;
+
+ cropcap->defrect.top = 0;
+ cropcap->defrect.left = 0;
+ cropcap->defrect.width = 720;
+ cropcap->defrect.height = 480;
+ break;
+
+ case TVOUT_PAL_M:
+ case TVOUT_PAL_BDGHI:
+ case TVOUT_PAL_N:
+ case TVOUT_PAL_NC:
+ case TVOUT_PAL_60:
+ case TVOUT_576P_50_16_9:
+ case TVOUT_576P_50_4_3:
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = 576;
+
+ cropcap->defrect.top = 0;
+ cropcap->defrect.left = 0;
+ cropcap->defrect.width = 720;
+ cropcap->defrect.height = 576;
+ break;
+
+ case TVOUT_720P_60:
+ case TVOUT_720P_59:
+ case TVOUT_720P_50:
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 1280;
+ cropcap->bounds.height = 720;
+
+ cropcap->defrect.top = 0;
+ cropcap->defrect.left = 0;
+ cropcap->defrect.width = 1280;
+ cropcap->defrect.height = 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:
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 1920;
+ cropcap->bounds.height = 1080;
+
+ cropcap->defrect.top = 0;
+ cropcap->defrect.left = 0;
+ cropcap->defrect.width = 1920;
+ cropcap->defrect.height = 1080;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int s5p_tvout_tvif_wait_for_vsync(void)
+{
+ sleep_on_timeout(&s5ptv_wq, HZ / 10);
+
+ return 0;
+}
+
+const struct v4l2_ioctl_ops s5p_tvout_tvif_ioctl_ops = {
+ .vidioc_querycap = s5p_tvout_tvif_querycap,
+ .vidioc_g_std = s5p_tvout_tvif_g_std,
+ .vidioc_s_std = s5p_tvout_tvif_s_std,
+ .vidioc_enum_output = s5p_tvout_tvif_enum_output,
+ .vidioc_g_output = s5p_tvout_tvif_g_output,
+ .vidioc_s_output = s5p_tvout_tvif_s_output,
+ .vidioc_cropcap = s5p_tvout_tvif_cropcap,
+};
+
+#define VIDIOC_HDCP_ENABLE _IOWR('V', 100, unsigned int)
+#define VIDIOC_HDCP_STATUS _IOR('V', 101, unsigned int)
+#define VIDIOC_HDCP_PROT_STATUS _IOR('V', 102, unsigned int)
+#define VIDIOC_INIT_AUDIO _IOR('V', 103, unsigned int)
+#define VIDIOC_AV_MUTE _IOR('V', 104, unsigned int)
+#define VIDIOC_G_AVMUTE _IOR('V', 105, unsigned int)
+#define VIDIOC_SET_VSYNC_INT _IOR('V', 106, unsigned int)
+#define VIDIOC_WAITFORVSYNC _IOR('V', 107, unsigned int)
+
+long s5p_tvout_tvif_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void *argp = (void *) arg;
+
+ switch (cmd) {
+ case VIDIOC_INIT_AUDIO:
+ tvout_dbg("VIDIOC_INIT_AUDIO(%d)\n", (int) arg);
+
+ if (arg)
+ s5p_tvif_ctrl_set_audio(true);
+ else
+ s5p_tvif_ctrl_set_audio(false);
+
+ return 0;
+
+ case VIDIOC_AV_MUTE:
+ tvout_dbg("VIDIOC_AV_MUTE(%d)\n", (int) arg);
+
+ if (arg)
+ s5p_tvif_ctrl_set_av_mute(true);
+ else
+ s5p_tvif_ctrl_set_av_mute(false);
+
+ return 0;
+
+ case VIDIOC_G_AVMUTE:
+ return s5p_hdmi_ctrl_get_mute();
+
+ case VIDIOC_HDCP_ENABLE:
+ tvout_dbg("VIDIOC_HDCP_ENABLE(%d)\n", (int) arg);
+
+ s5p_hdmi_ctrl_set_hdcp((bool) arg);
+ return 0;
+
+ case VIDIOC_HDCP_STATUS: {
+ unsigned int *status = (unsigned int *)&arg;
+
+ *status = 1;
+
+ return 0;
+ }
+
+ case VIDIOC_HDCP_PROT_STATUS: {
+ unsigned int *prot = (unsigned int *)&arg;
+
+ *prot = 1;
+
+ return 0;
+ }
+
+ case VIDIOC_ENUMSTD: {
+ struct v4l2_standard *p = (struct v4l2_standard *)arg;
+
+ if (p->index >= S5P_TVOUT_TVIF_NO_OF_STANDARD) {
+ tvout_dbg("VIDIOC_ENUMSTD: Invalid index(%d)\n",
+ p->index);
+
+ return -EINVAL;
+ }
+
+ memcpy(p, &s5p_tvout_tvif_standard[p->index],
+ sizeof(struct v4l2_standard));
+
+ return 0;
+ }
+
+ case VIDIOC_SET_VSYNC_INT:
+ s5p_mixer_ctrl_set_vsync_interrupt((int)argp);
+ break;
+
+ case VIDIOC_WAITFORVSYNC:
+ s5p_tvout_tvif_wait_for_vsync();
+ break;
+
+ default:
+ break;
+ }
+
+ return video_ioctl2(file, cmd, arg);
+}
+
+
+static int s5p_tvout_tvif_open(struct file *file)
+{
+ mutex_lock(&s5p_tvout_tvif_mutex);
+
+ atomic_inc(&s5p_tvout_v4l2_private.tvif_use);
+
+ mutex_unlock(&s5p_tvout_tvif_mutex);
+
+ tvout_dbg("count=%d\n", atomic_read(&s5p_tvout_v4l2_private.tvif_use));
+
+ return 0;
+}
+
+static int s5p_tvout_tvif_release(struct file *file)
+{
+ tvout_dbg("count=%d\n", atomic_read(&s5p_tvout_v4l2_private.tvif_use));
+
+ mutex_lock(&s5p_tvout_tvif_mutex);
+
+ atomic_dec(&s5p_tvout_v4l2_private.tvif_use);
+
+ if (atomic_read(&s5p_tvout_v4l2_private.tvif_use) == 0)
+ s5p_tvif_ctrl_stop();
+
+ mutex_unlock(&s5p_tvout_tvif_mutex);
+
+ return 0;
+}
+
+static struct v4l2_file_operations s5p_tvout_tvif_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_tvout_tvif_open,
+ .release = s5p_tvout_tvif_release,
+ .ioctl = s5p_tvout_tvif_ioctl
+};
+
+static int s5p_tvout_vo_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "s5p-tvout-vo");
+ strcpy(cap->card, "Samsung TVOUT Video Overlay");
+ cap->capabilities = V4L2_CAP_VIDEO_OVERLAY;
+ cap->version = KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION);
+
+ return 0;
+}
+
+static int s5p_tvout_vo_enum_fmt_type_private(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ int index = f->index;
+
+ if (index >= ARRAY_SIZE(s5p_tvout_vo_fmt_desc)) {
+ tvout_err("Invalid index(%d)\n", index);
+
+ return -EINVAL;
+ }
+
+ memcpy(f, &s5p_tvout_vo_fmt_desc[index], sizeof(struct v4l2_fmtdesc));
+
+ return 0;
+}
+
+static int s5p_tvout_vo_g_fmt_type_private(struct file *file, void *fh,
+ struct v4l2_format *a)
+{
+ memcpy(a->fmt.raw_data, &s5p_tvout_v4l2_private.vo_src_fmt,
+ sizeof(struct v4l2_vid_overlay_src));
+
+ return 0;
+}
+
+static int s5p_tvout_vo_s_fmt_type_private(struct file *file, void *fh,
+ struct v4l2_format *a)
+{
+ struct v4l2_vid_overlay_src vparam;
+ struct v4l2_pix_format *pix_fmt;
+ enum s5p_vp_src_color color;
+ enum s5p_vp_field field;
+
+ memcpy(&vparam, a->fmt.raw_data, sizeof(struct v4l2_vid_overlay_src));
+
+ pix_fmt = &vparam.pix_fmt;
+
+ tvout_dbg("base_y=0x%X, base_c=0x%X, field=%d\n",
+ (u32) vparam.base_y, (u32) vparam.base_c,
+ pix_fmt->field);
+
+ /* check progressive or not */
+ if (pix_fmt->field == V4L2_FIELD_NONE) {
+ /* progressive */
+ switch (pix_fmt->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ /* linear */
+ tvout_dbg("pixelformat=V4L2_PIX_FMT_NV12\n");
+
+ color = VP_SRC_COLOR_NV12;
+ break;
+
+ case V4L2_PIX_FMT_NV12T:
+ /* tiled */
+ tvout_dbg("pixelformat=V4L2_PIX_FMT_NV12T\n");
+ color = VP_SRC_COLOR_TILE_NV12;
+ break;
+
+ case V4L2_PIX_FMT_NV21:
+ /* linear */
+ color = VP_SRC_COLOR_NV21;
+ break;
+
+ case V4L2_PIX_FMT_NV21T:
+ /* tiled */
+ color = VP_SRC_COLOR_TILE_NV21;
+ break;
+
+ default:
+ tvout_err("src img format not supported\n");
+ goto error_on_s_fmt_type_private;
+ }
+ field = VP_TOP_FIELD;
+
+ } else if ((pix_fmt->field == V4L2_FIELD_TOP) ||
+ (pix_fmt->field == V4L2_FIELD_BOTTOM)) {
+ /* interlaced */
+ switch (pix_fmt->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ /* linear */
+ tvout_dbg("pixelformat=V4L2_PIX_FMT_NV12\n");
+ color = VP_SRC_COLOR_NV12IW;
+ break;
+
+ case V4L2_PIX_FMT_NV12T:
+ /* tiled */
+ tvout_dbg("pixelformat=V4L2_PIX_FMT_NV12T\n");
+ color = VP_SRC_COLOR_TILE_NV12IW;
+ break;
+
+ case V4L2_PIX_FMT_NV21:
+ /* linear */
+ color = VP_SRC_COLOR_NV21IW;
+ break;
+
+ case V4L2_PIX_FMT_NV21T:
+ /* tiled */
+ color = VP_SRC_COLOR_TILE_NV21IW;
+ break;
+
+ default:
+ tvout_err("src img format not supported\n");
+ goto error_on_s_fmt_type_private;
+ }
+ field = (pix_fmt->field == V4L2_FIELD_BOTTOM) ?
+ VP_BOTTOM_FIELD : VP_TOP_FIELD;
+
+ } else {
+ tvout_err("this field id not supported\n");
+
+ goto error_on_s_fmt_type_private;
+ }
+
+ s5p_tvout_v4l2_private.vo_src_fmt = vparam;
+
+ s5p_vp_ctrl_set_src_plane((u32) vparam.base_y, (u32) vparam.base_c,
+ pix_fmt->width, pix_fmt->height, color, field);
+ return 0;
+
+error_on_s_fmt_type_private:
+ return -1;
+}
+
+static int s5p_tvout_vo_g_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *a)
+{
+ a->fmt.win = s5p_tvout_v4l2_private.vo_dst_fmt;
+
+ return 0;
+}
+
+static int s5p_tvout_vo_s_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *a)
+{
+ struct v4l2_rect *rect = &a->fmt.win.w;
+
+ tvout_dbg("l=%d, t=%d, w=%d, h=%d, g_alpha_value=%d\n",
+ rect->left, rect->top, rect->width, rect->height,
+ a->fmt.win.global_alpha);
+
+ s5p_tvout_v4l2_private.vo_dst_fmt = a->fmt.win;
+
+ s5p_vp_ctrl_set_dest_win_alpha_val(a->fmt.win.global_alpha);
+ s5p_vp_ctrl_set_dest_win(rect->left, rect->top, rect->width, rect->height);
+
+ return 0;
+}
+
+static int s5p_tvout_vo_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ switch (a->type) {
+ case V4L2_BUF_TYPE_PRIVATE:
+ a->c = s5p_tvout_v4l2_private.vo_src_rect;
+ break;
+
+ default:
+ tvout_err("Invalid buf type(0x%08x)\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+static int s5p_tvout_vo_s_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ switch (a->type) {
+ case V4L2_BUF_TYPE_PRIVATE: {
+ struct v4l2_rect *rect = &s5p_tvout_v4l2_private.vo_src_rect;
+
+ *rect = a->c;
+
+ tvout_dbg("l=%d, t=%d, w=%d, h=%d\n",
+ rect->left, rect->top, rect->width, rect->height);
+
+ s5p_vp_ctrl_set_src_win(rect->left, rect->top,
+ rect->width, rect->height);
+ break;
+
+ }
+ default:
+ tvout_err("Invalid buf type(0x%08x)\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+static int s5p_tvout_vo_g_fbuf(struct file *file, void *fh,
+ struct v4l2_framebuffer *a)
+{
+ *a = s5p_tvout_v4l2_private.vo_dst_plane;
+
+ a->capability = V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+ return 0;
+}
+
+static int s5p_tvout_vo_s_fbuf(struct file *file, void *fh,
+ struct v4l2_framebuffer *a)
+{
+ s5p_tvout_v4l2_private.vo_dst_plane = *a;
+
+ tvout_dbg("g_alpha_enable=%d, priority=%d\n",
+ (a->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) ? 1 : 0, a->fmt.priv);
+
+ s5p_vp_ctrl_set_dest_win_blend(
+ (a->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) ? 1 : 0);
+
+ s5p_vp_ctrl_set_dest_win_priority(a->fmt.priv);
+
+ return 0;
+}
+
+static int s5p_tvout_vo_overlay(struct file *file, void *fh,
+ unsigned int i)
+{
+ tvout_dbg("%s\n", (i) ? "start" : "stop");
+
+ if (i)
+ s5p_vp_ctrl_start();
+ else
+ s5p_vp_ctrl_stop();
+
+ return 0;
+}
+
+const struct v4l2_ioctl_ops s5p_tvout_vo_ioctl_ops = {
+ .vidioc_querycap = s5p_tvout_vo_querycap,
+
+ .vidioc_enum_fmt_type_private = s5p_tvout_vo_enum_fmt_type_private,
+ .vidioc_g_fmt_type_private = s5p_tvout_vo_g_fmt_type_private,
+ .vidioc_s_fmt_type_private = s5p_tvout_vo_s_fmt_type_private,
+
+ .vidioc_g_fmt_vid_overlay = s5p_tvout_vo_g_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = s5p_tvout_vo_s_fmt_vid_overlay,
+
+ .vidioc_g_crop = s5p_tvout_vo_g_crop,
+ .vidioc_s_crop = s5p_tvout_vo_s_crop,
+
+ .vidioc_g_fbuf = s5p_tvout_vo_g_fbuf,
+ .vidioc_s_fbuf = s5p_tvout_vo_s_fbuf,
+
+ .vidioc_overlay = s5p_tvout_vo_overlay,
+};
+
+static int s5p_tvout_vo_open(struct file *file)
+{
+ int ret = 0;
+
+ tvout_dbg("\n");
+
+ mutex_lock(&s5p_tvout_vo_mutex);
+
+ if (atomic_read(&s5p_tvout_v4l2_private.vo_use)) {
+ tvout_err("Can't open TVOUT TVIF control\n");
+ ret = -EBUSY;
+ } else
+ atomic_inc(&s5p_tvout_v4l2_private.vo_use);
+
+ mutex_unlock(&s5p_tvout_vo_mutex);
+
+ return ret;
+}
+
+static int s5p_tvout_vo_release(struct file *file)
+{
+ tvout_dbg("\n");
+
+ s5p_vp_ctrl_stop();
+
+ s5p_mixer_ctrl_disable_layer(MIXER_VIDEO_LAYER);
+
+ atomic_dec(&s5p_tvout_v4l2_private.vo_use);
+
+ return 0;
+}
+
+static struct v4l2_file_operations s5p_tvout_vo_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_tvout_vo_open,
+ .release = s5p_tvout_vo_release,
+ .ioctl = video_ioctl2
+};
+
+static void s5p_tvout_video_dev_release(struct video_device *vdev)
+{
+ /* dummy function for release callback of v4l2 video device */
+}
+
+static struct video_device s5p_tvout_video_dev[] = {
+ [0] = {
+ .name = "S5P TVOUT TVIF control",
+ .fops = &s5p_tvout_tvif_fops,
+ .ioctl_ops = &s5p_tvout_tvif_ioctl_ops,
+ .minor = S5P_TVOUT_TVIF_MINOR,
+ .release = s5p_tvout_video_dev_release,
+ .tvnorms = V4L2_STD_ALL_HD,
+ },
+ [1] = {
+ .name = "S5P TVOUT Video Overlay",
+ .fops = &s5p_tvout_vo_fops,
+ .ioctl_ops = &s5p_tvout_vo_ioctl_ops,
+ .release = s5p_tvout_video_dev_release,
+ .minor = S5P_TVOUT_VO_MINOR
+ }
+};
+
+int s5p_tvout_v4l2_constructor(struct platform_device *pdev)
+{
+ int i;
+
+ /* v4l2 video device registration */
+ for (i = 0; i < ARRAY_SIZE(s5p_tvout_video_dev); i++) {
+
+ if (video_register_device(
+ &s5p_tvout_video_dev[i],
+ VFL_TYPE_GRABBER,
+ s5p_tvout_video_dev[i].minor) != 0) {
+ tvout_err("Fail to register v4l2 video device\n");
+
+ return -1;
+ }
+ }
+
+#ifdef CONFIG_S5P_HDMI_HPD
+ s5p_hpd_set_kobj(&(s5p_tvout_video_dev[0].dev.kobj),
+ &(s5p_tvout_video_dev[1].dev.kobj));
+#endif
+ s5p_tvout_v4l2_init_private();
+
+ return 0;
+}
+
+void s5p_tvout_v4l2_destructor(void)
+{
+ mutex_destroy(&s5p_tvout_tvif_mutex);
+ mutex_destroy(&s5p_tvout_vo_mutex);
+}
new file mode 100644
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Video4Linux API header for Samsung S5P 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.
+*/
+
+#ifndef __S5P_TVOUT_V4L2_H_
+#define __S5P_TVOUT_V4L2_H_ __FILE__
+
+extern int s5p_tvout_v4l2_constructor(struct platform_device *pdev);
+extern void s5p_tvout_v4l2_destructor(void);
+
+#endif /* __S5P_TVOUT_V4L2_H_ */