===============================================================================
-----------------------------------------------------------------------
VFS
-----------------------------------------------------------------------
KERNEL | |
V V
---------- -------- ------------------
V4L2 STACK FB STACK Linux Driver Model
---------- -------- ------------------
| | |
=============+===============================+========================+========
| | |
| | +-------------|
| | | |
V V V |
+---------------------------------------------------------------+-------+
| | |
| | |
| ------------ ------------- ----------- | |
| Video Ctrl -- Graphics Ctrl -- TVOUT I/F | |
| ------------ ------------- ----------- V |
| | | | |__________ ----------- |
| | | | | HPD Driver |
| V V V V (GPIO) |
| ----------- ----------- ----------- ----------- ----------- |
| VP I/F Mixer I/F Analog I/F HDMI I/F |
| ----------- ----------- ----------- ----------- |
| | | | | | |
DEVICE| | | | | |_______________ |
DRIVER| | | | | | | |
| | | | | V V |
| | | | | ----- ------|
| | | | | CEC HDCP |
| | | | | ----- ------|
| | | | |___________|_______| |
| | | | |
+------+---------------+---------------+--------------------------------+
| | | |
| | | |
=============+===============+===============+=================================
| | | |
V V V |
----------- ----------- ----------- | --------
VP ----> Mixer ----> TV Encoder ---+--------> DAC
----------- ----------- | ----------- | --------
| |
HARDWARE | V
| ---------- --------
+----------> HDMI Link --> HDMI PHY
---------- --------
===============================================================================
[based on work originally written by Sunil Choi <sunil111.choi@samsung.com>]
Cc: Kukjin Kim <kgene.kim@samsung.com>
Acked-by: KyungHwan Kim <kh.k.kim@samsung.com>
Signed-off-by: Jiun Yu <jiun.yu@samsung.com>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
---
drivers/media/video/s5p-tvout/hw_if/hw_if.h | 651 +++++++++++++++++
drivers/media/video/s5p-tvout/hw_if/vp.c | 731 ++++++++++++++++++++
drivers/media/video/s5p-tvout/s5p_tvout.c | 210 ++++++
.../media/video/s5p-tvout/s5p_tvout_common_lib.c | 99 +++
.../media/video/s5p-tvout/s5p_tvout_common_lib.h | 187 +++++
drivers/media/video/s5p-tvout/s5p_tvout_ctrl.h | 126 ++++
drivers/media/video/s5p-tvout/s5p_tvout_fb.c | 639 +++++++++++++++++
drivers/media/video/s5p-tvout/s5p_tvout_fb.h | 20 +
drivers/media/video/s5p-tvout/s5p_vp_ctrl.c | 586 ++++++++++++++++
9 files changed, 3249 insertions(+), 0 deletions(-)
create mode 100644 drivers/media/video/s5p-tvout/hw_if/hw_if.h
create mode 100644 drivers/media/video/s5p-tvout/hw_if/vp.c
create mode 100644 drivers/media/video/s5p-tvout/s5p_tvout.c
create mode 100644 drivers/media/video/s5p-tvout/s5p_tvout_common_lib.c
create mode 100644 drivers/media/video/s5p-tvout/s5p_tvout_common_lib.h
create mode 100644 drivers/media/video/s5p-tvout/s5p_tvout_ctrl.h
create mode 100644 drivers/media/video/s5p-tvout/s5p_tvout_fb.c
create mode 100644 drivers/media/video/s5p-tvout/s5p_tvout_fb.h
create mode 100644 drivers/media/video/s5p-tvout/s5p_vp_ctrl.c
new file mode 100644
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file for interface of Samsung TVOUT-related hardware
+ *
+ * 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_HW_IF_H_
+#define __S5P_TVOUT_HW_IF_H_ __FILE__
+
+/*
+ * This file includes declarations for external functions of
+ * Samsung TVOUT-related hardware. So only external functions
+ * to be used by higher layer must exist in this file.
+ *
+ * Higher layer must use only the declarations included in this file.
+ */
+
+#include <linux/irqreturn.h>
+#include <linux/stddef.h>
+
+#include "../s5p_tvout_common_lib.h"
+
+/* Common */
+
+enum s5p_tvout_endian {
+ TVOUT_LITTLE_ENDIAN = 0,
+ TVOUT_BIG_ENDIAN = 1
+};
+
+/* for MIXER */
+
+enum s5p_mixer_layer {
+ MIXER_VIDEO_LAYER = 2,
+ MIXER_GPR0_LAYER = 0,
+ MIXER_GPR1_LAYER = 1
+};
+
+enum s5p_mixer_bg_color_num {
+ MIXER_BG_COLOR_0 = 0,
+ MIXER_BG_COLOR_1 = 1,
+ MIXER_BG_COLOR_2 = 2
+};
+
+enum s5p_mixer_color_fmt {
+ MIXER_RGB565 = 4,
+ MIXER_RGB1555 = 5,
+ MIXER_RGB4444 = 6,
+ MIXER_RGB8888 = 7
+};
+
+enum s5p_mixer_csc_type {
+ MIXER_CSC_601_LR,
+ MIXER_CSC_601_FR,
+ MIXER_CSC_709_LR,
+ MIXER_CSC_709_FR
+};
+
+enum s5p_mixer_rgb {
+ MIXER_RGB601_0_255,
+ MIXER_RGB601_16_235,
+ MIXER_RGB709_0_255,
+ MIXER_RGB709_16_235
+};
+
+enum s5p_mixer_out_type {
+ MIXER_YUV444,
+ MIXER_RGB888
+};
+
+extern int s5p_mixer_set_show(enum s5p_mixer_layer layer, bool show);
+extern int s5p_mixer_set_priority(enum s5p_mixer_layer layer, u32 priority);
+extern void s5p_mixer_set_pre_mul_mode(enum s5p_mixer_layer layer, bool enable);
+extern int s5p_mixer_set_pixel_blend(enum s5p_mixer_layer layer, bool enable);
+extern int s5p_mixer_set_layer_blend(enum s5p_mixer_layer layer, bool enable);
+extern int s5p_mixer_set_alpha(enum s5p_mixer_layer layer, u32 alpha);
+extern int s5p_mixer_set_grp_base_address(enum s5p_mixer_layer layer,
+ u32 baseaddr);
+extern int s5p_mixer_set_grp_layer_dst_pos(enum s5p_mixer_layer layer,
+ u32 dst_offs_x, u32 dst_offs_y);
+extern int s5p_mixer_set_grp_layer_src_pos(enum s5p_mixer_layer layer,
+ u32 span, u32 width, u32 height,
+ u32 src_offs_x, u32 src_offs_y);
+extern void s5p_mixer_set_bg_color(enum s5p_mixer_bg_color_num colornum,
+ u32 color_y, u32 color_cb, u32 color_cr);
+extern void s5p_mixer_init_status_reg(enum s5p_mixer_burst_mode burst,
+ enum s5p_tvout_endian endian);
+extern int s5p_mixer_init_display_mode(enum s5p_tvout_disp_mode mode,
+ enum s5p_tvout_o_mode output_mode);
+extern void s5p_mixer_scaling(enum s5p_mixer_layer layer,
+ struct s5ptvfb_user_scaling scaling);
+extern void s5p_mixer_set_color_format(enum s5p_mixer_layer layer,
+ enum s5p_mixer_color_fmt format);
+extern void s5p_mixer_set_chroma_key(enum s5p_mixer_layer layer,
+ bool enabled, u32 key);
+extern void s5p_mixer_init_bg_dither_enable(bool cr_dither_enable,
+ bool cdither_enable,
+ bool y_dither_enable);
+extern void s5p_mixer_init_csc_coef_default(enum s5p_mixer_csc_type csc_type);
+extern void s5p_mixer_start(void);
+extern void s5p_mixer_stop(void);
+extern void s5p_mixer_set_underflow_int_enable(enum s5p_mixer_layer layer,
+ bool en);
+extern void s5p_mixer_set_vsync_interrupt(bool);
+extern void s5p_mixer_clear_pend_all(void);
+extern irqreturn_t s5p_mixer_irq(int irq, void *dev_id);
+extern void s5p_mixer_init(void __iomem *addr);
+
+/* for HDMI */
+
+#define HDMI_MASK_8(x) ((x) & 0xFF)
+#define HDMI_MASK_16(x) (((x) >> 8) & 0xFF)
+#define HDMI_MASK_24(x) (((x) >> 16) & 0xFF)
+#define HDMI_MASK_32(x) (((x) >> 24) & 0xFF)
+
+#define hdmi_write_16(x, y) \
+ do { \
+ writeb(HDMI_MASK_8(x), y); \
+ writeb(HDMI_MASK_16(x), y + 4); \
+ } while (0);
+
+#define hdmi_write_24(x, y) \
+ do { \
+ writeb(HDMI_MASK_8(x), y); \
+ writeb(HDMI_MASK_16(x), y + 4); \
+ writeb(HDMI_MASK_24(x), y + 8); \
+ } while (0);
+
+#define hdmi_write_32(x, y) \
+ do { \
+ writeb(HDMI_MASK_8(x), y); \
+ writeb(HDMI_MASK_16(x), y + 4); \
+ writeb(HDMI_MASK_24(x), y + 8); \
+ writeb(HDMI_MASK_32(x), y + 12); \
+ } while (0);
+
+#define hdmi_write_l(buff, base, start, count) \
+ do { \
+ u8 *ptr = buff; \
+ int i = 0; \
+ int a = start; \
+ do { \
+ writeb(ptr[i], base + a); \
+ a += 4; \
+ i++; \
+ } while (i <= (count - 1)); \
+ } while (0);
+
+#define hdmi_read_l(buff, base, start, count) \
+ do { \
+ u8 *ptr = buff; \
+ int i = 0; \
+ int a = start; \
+ do { \
+ ptr[i] = readb(base + a); \
+ a += 4; \
+ i++; \
+ } while (i <= (count - 1)); \
+ } while (0);
+
+#define hdmi_bit_set(en, reg, val) \
+ do { \
+ if (en) \
+ reg |= val; \
+ else \
+ reg &= ~val; \
+ } while (0);
+
+enum s5p_hdmi_transmit {
+ HDMI_DO_NOT_TANS,
+ HDMI_TRANS_ONCE,
+ HDMI_TRANS_EVERY_SYNC,
+};
+
+enum s5p_tvout_audio_codec_type {
+ PCM = 1,
+ AC3,
+ MP3,
+ WMA
+};
+
+enum s5p_hdmi_infoframe_type {
+ HDMI_VSI_INFO = 0x81,
+ HDMI_AVI_INFO,
+ HDMI_SPD_INFO,
+ HDMI_AUI_INFO,
+ HDMI_MPG_INFO,
+};
+
+enum s5p_hdmi_color_depth {
+ HDMI_CD_48,
+ HDMI_CD_36,
+ HDMI_CD_30,
+ HDMI_CD_24
+};
+
+enum s5p_hdmi_interrrupt {
+ HDMI_IRQ_PIN_POLAR_CTL = 7,
+ HDMI_IRQ_GLOBAL = 6,
+ HDMI_IRQ_I2S = 5,
+ HDMI_IRQ_CEC = 4,
+ HDMI_IRQ_HPD_PLUG = 3,
+ HDMI_IRQ_HPD_UNPLUG = 2,
+ HDMI_IRQ_SPDIF = 1,
+ HDMI_IRQ_HDCP = 0
+};
+
+enum phy_freq {
+ ePHY_FREQ_25_200,
+ ePHY_FREQ_25_175,
+ ePHY_FREQ_27,
+ ePHY_FREQ_27_027,
+ ePHY_FREQ_54,
+ ePHY_FREQ_54_054,
+ ePHY_FREQ_74_250,
+ ePHY_FREQ_74_176,
+ ePHY_FREQ_148_500,
+ ePHY_FREQ_148_352,
+ ePHY_FREQ_108_108,
+ ePHY_FREQ_72,
+ ePHY_FREQ_25,
+ ePHY_FREQ_65,
+ ePHY_FREQ_108,
+ ePHY_FREQ_162
+};
+
+struct s5p_hdmi_infoframe {
+ enum s5p_hdmi_infoframe_type type;
+ u8 version;
+ u8 length;
+};
+
+struct s5p_hdmi_o_trans {
+ enum s5p_hdmi_transmit avi;
+ enum s5p_hdmi_transmit mpg;
+ enum s5p_hdmi_transmit spd;
+ enum s5p_hdmi_transmit gcp;
+ enum s5p_hdmi_transmit gmp;
+ enum s5p_hdmi_transmit isrc;
+ enum s5p_hdmi_transmit acp;
+ enum s5p_hdmi_transmit aui;
+ enum s5p_hdmi_transmit acr;
+};
+
+struct s5p_hdmi_o_reg {
+ u8 pxl_fmt;
+ u8 preemble;
+ u8 mode;
+ u8 pxl_limit;
+ u8 dvi;
+};
+
+struct s5p_hdmi_v_frame {
+ u8 vic;
+ u8 vic_16_9;
+ u8 repetition;
+ u8 polarity;
+ u8 i_p;
+
+ u16 h_active;
+ u16 v_active;
+
+ u16 h_total;
+ u16 h_blank;
+
+ u16 v_total;
+ u16 v_blank;
+
+ enum phy_freq pixel_clock;
+};
+
+struct s5p_hdmi_tg_sync {
+ u16 begin;
+ u16 end;
+};
+
+struct s5p_hdmi_v_format {
+ struct s5p_hdmi_v_frame frame;
+
+ struct s5p_hdmi_tg_sync h_sync;
+ struct s5p_hdmi_tg_sync v_sync_top;
+ struct s5p_hdmi_tg_sync v_sync_bottom;
+ struct s5p_hdmi_tg_sync v_sync_h_pos;
+
+ struct s5p_hdmi_tg_sync v_blank_f;
+
+ u8 mhl_hsync;
+ u8 mhl_vsync;
+};
+
+extern int s5p_hdmi_phy_power(bool on);
+extern s32 s5p_hdmi_phy_config(enum phy_freq freq,
+ enum s5p_hdmi_color_depth cd);
+
+extern void s5p_hdmi_set_gcp(enum s5p_hdmi_color_depth depth, u8 *gcp);
+extern void s5p_hdmi_reg_acr(u8 *acr);
+extern void s5p_hdmi_reg_asp(u8 *asp);
+extern void s5p_hdmi_reg_gcp(u8 i_p, u8 *gcp);
+extern void s5p_hdmi_reg_acp(u8 *header, u8 *acp);
+extern void s5p_hdmi_reg_isrc(u8 *isrc1, u8 *isrc2);
+extern void s5p_hdmi_reg_gmp(u8 *gmp);
+extern void s5p_hdmi_reg_infoframe(struct s5p_hdmi_infoframe *info, u8 *data);
+extern void s5p_hdmi_reg_tg(struct s5p_hdmi_v_frame *frame);
+extern void s5p_hdmi_reg_v_timing(struct s5p_hdmi_v_format *v);
+extern void s5p_hdmi_reg_bluescreen_clr(u8 cb_b, u8 y_g, u8 cr_r);
+extern void s5p_hdmi_reg_bluescreen(bool en);
+extern void s5p_hdmi_reg_clr_range(u8 y_min, u8 y_max, u8 c_min, u8 c_max);
+extern void s5p_hdmi_reg_tg_cmd(bool time, bool bt656, bool tg);
+extern void s5p_hdmi_reg_enable(bool en);
+extern u8 s5p_hdmi_reg_intc_status(void);
+extern u8 s5p_hdmi_reg_intc_get_enabled(void);
+extern void s5p_hdmi_reg_intc_clear_pending(enum s5p_hdmi_interrrupt intr);
+extern void s5p_hdmi_reg_sw_hpd_enable(bool enable);
+extern void s5p_hdmi_reg_set_hpd_onoff(bool on_off);
+extern u8 s5p_hdmi_reg_get_hpd_status(void);
+extern void s5p_hdmi_reg_hpd_gen(void);
+extern int s5p_hdmi_reg_intc_set_isr(irqreturn_t (*isr)(int, void *), u8 num);
+extern void s5p_hdmi_reg_intc_enable(enum s5p_hdmi_interrrupt intr, u8 en);
+extern void s5p_hdmi_reg_audio_enable(u8 en);
+extern int s5p_hdmi_audio_init(enum s5p_tvout_audio_codec_type audio_codec,
+ u32 sample_rate, u32 bits, u32 frame_size_code);
+extern irqreturn_t s5p_hdmi_irq(int irq, void *dev_id);
+extern void s5p_hdmi_init(void __iomem *hdmi_addr,
+ void __iomem *hdmi_phy_addr);
+extern void s5p_hdmi_reg_output(struct s5p_hdmi_o_reg *reg);
+extern void s5p_hdmi_reg_packet_trans(struct s5p_hdmi_o_trans *trans);
+extern void s5p_hdmi_reg_mute(bool en);
+
+extern void __iomem *hdmi_base;
+
+/* for SDO */
+
+enum s5p_sdo_level {
+ SDO_LEVEL_0IRE,
+ SDO_LEVEL_75IRE
+};
+
+enum s5p_sdo_vsync_ratio {
+ SDO_VTOS_RATIO_10_4,
+ SDO_VTOS_RATIO_7_3
+};
+
+enum s5p_sdo_order {
+ SDO_O_ORDER_COMPONENT_RGB_PRYPB,
+ SDO_O_ORDER_COMPONENT_RBG_PRPBY,
+ SDO_O_ORDER_COMPONENT_BGR_PBYPR,
+ SDO_O_ORDER_COMPONENT_BRG_PBPRY,
+ SDO_O_ORDER_COMPONENT_GRB_YPRPB,
+ SDO_O_ORDER_COMPONENT_GBR_YPBPR,
+ SDO_O_ORDER_COMPOSITE_CVBS_Y_C,
+ SDO_O_ORDER_COMPOSITE_CVBS_C_Y,
+ SDO_O_ORDER_COMPOSITE_Y_C_CVBS,
+ SDO_O_ORDER_COMPOSITE_Y_CVBS_C,
+ SDO_O_ORDER_COMPOSITE_C_CVBS_Y,
+ SDO_O_ORDER_COMPOSITE_C_Y_CVBS
+};
+
+enum s5p_sdo_sync_sig_pin {
+ SDO_SYNC_SIG_NO,
+ SDO_SYNC_SIG_YG,
+ SDO_SYNC_SIG_ALL
+};
+
+enum s5p_sdo_closed_caption_type {
+ SDO_NO_INS,
+ SDO_INS_1,
+ SDO_INS_2,
+ SDO_INS_OTHERS
+};
+
+enum s5p_sdo_525_copy_permit {
+ SDO_525_COPY_PERMIT,
+ SDO_525_ONECOPY_PERMIT,
+ SDO_525_NOCOPY_PERMIT
+};
+
+enum s5p_sdo_525_mv_psp {
+ SDO_525_MV_PSP_OFF,
+ SDO_525_MV_PSP_ON_2LINE_BURST,
+ SDO_525_MV_PSP_ON_BURST_OFF,
+ SDO_525_MV_PSP_ON_4LINE_BURST,
+};
+
+enum s5p_sdo_525_copy_info {
+ SDO_525_COPY_INFO,
+ SDO_525_DEFAULT,
+};
+
+enum s5p_sdo_525_aspect_ratio {
+ SDO_525_4_3_NORMAL,
+ SDO_525_16_9_ANAMORPIC,
+ SDO_525_4_3_LETTERBOX
+};
+
+enum s5p_sdo_625_subtitles {
+ SDO_625_NO_OPEN_SUBTITLES,
+ SDO_625_INACT_OPEN_SUBTITLES,
+ SDO_625_OUTACT_OPEN_SUBTITLES
+};
+
+enum s5p_sdo_625_camera_film {
+ SDO_625_CAMERA,
+ SDO_625_FILM
+};
+
+enum s5p_sdo_625_color_encoding {
+ SDO_625_NORMAL_PAL,
+ SDO_625_MOTION_ADAPTIVE_COLORPLUS
+};
+
+enum s5p_sdo_625_aspect_ratio {
+ SDO_625_4_3_FULL_576,
+ SDO_625_14_9_LETTERBOX_CENTER_504,
+ SDO_625_14_9_LETTERBOX_TOP_504,
+ SDO_625_16_9_LETTERBOX_CENTER_430,
+ SDO_625_16_9_LETTERBOX_TOP_430,
+ SDO_625_16_9_LETTERBOX_CENTER,
+ SDO_625_14_9_FULL_CENTER_576,
+ SDO_625_16_9_ANAMORPIC_576
+};
+
+struct s5p_sdo_cvbs_compensation {
+ bool cvbs_color_compen;
+ u32 y_lower_mid;
+ u32 y_bottom;
+ u32 y_top;
+ u32 y_upper_mid;
+ u32 radius;
+};
+
+struct s5p_sdo_bright_hue_saturation {
+ bool bright_hue_sat_adj;
+ u32 gain_brightness;
+ u32 offset_brightness;
+ u32 gain0_cb_hue_sat;
+ u32 gain1_cb_hue_sat;
+ u32 gain0_cr_hue_sat;
+ u32 gain1_cr_hue_sat;
+ u32 offset_cb_hue_sat;
+ u32 offset_cr_hue_sat;
+};
+
+struct s5p_sdo_525_data {
+ bool analog_on;
+ enum s5p_sdo_525_copy_permit copy_permit;
+ enum s5p_sdo_525_mv_psp mv_psp;
+ enum s5p_sdo_525_copy_info copy_info;
+ enum s5p_sdo_525_aspect_ratio display_ratio;
+};
+
+struct s5p_sdo_625_data {
+ bool surround_sound;
+ bool copyright;
+ bool copy_protection;
+ bool text_subtitles;
+ enum s5p_sdo_625_subtitles open_subtitles;
+ enum s5p_sdo_625_camera_film camera_film;
+ enum s5p_sdo_625_color_encoding color_encoding;
+ bool helper_signal;
+ enum s5p_sdo_625_aspect_ratio display_ratio;
+};
+
+extern int s5p_sdo_set_video_scale_cfg(enum s5p_sdo_level composite_level,
+ enum s5p_sdo_vsync_ratio composite_ratio);
+extern int s5p_sdo_set_vbi(bool wss_cvbs,
+ enum s5p_sdo_closed_caption_type caption_cvbs);
+extern void s5p_sdo_set_offset_gain(u32 offset, u32 gain);
+extern void s5p_sdo_set_delay(u32 delay_y, u32 offset_video_start,
+ u32 offset_video_end);
+extern void s5p_sdo_set_schlock(bool color_sucarrier_pha_adj);
+extern void s5p_sdo_set_brightness_hue_saturation(struct s5p_sdo_bright_hue_saturation bri_hue_sat);
+extern void s5p_sdo_set_cvbs_color_compensation(struct s5p_sdo_cvbs_compensation cvbs_comp);
+extern void s5p_sdo_set_component_porch(u32 back_525, u32 front_525,
+ u32 back_625, u32 front_625);
+extern void s5p_sdo_set_ch_xtalk_cancel_coef(u32 coeff2, u32 coeff1);
+extern void s5p_sdo_set_closed_caption(u32 display_cc, u32 non_display_cc);
+
+extern int s5p_sdo_set_wss525_data(struct s5p_sdo_525_data wss525);
+extern int s5p_sdo_set_wss625_data(struct s5p_sdo_625_data wss625);
+extern int s5p_sdo_set_cgmsa525_data(struct s5p_sdo_525_data cgmsa525);
+extern int s5p_sdo_set_cgmsa625_data(struct s5p_sdo_625_data cgmsa625);
+extern int s5p_sdo_set_display_mode(enum s5p_tvout_disp_mode disp_mode,
+ enum s5p_sdo_order order);
+extern void s5p_sdo_clock_on(bool on);
+extern void s5p_sdo_dac_on(bool on);
+extern void s5p_sdo_sw_reset(bool active);
+extern void s5p_sdo_set_interrupt_enable(bool vsync_intc_en);
+extern void s5p_sdo_clear_interrupt_pending(void);
+extern void s5p_sdo_init(void __iomem *addr);
+
+/* for VP */
+
+enum s5p_vp_field {
+ VP_TOP_FIELD,
+ VP_BOTTOM_FIELD
+};
+
+enum s5p_vp_line_eq {
+ VP_LINE_EQ_0,
+ VP_LINE_EQ_1,
+ VP_LINE_EQ_2,
+ VP_LINE_EQ_3,
+ VP_LINE_EQ_4,
+ VP_LINE_EQ_5,
+ VP_LINE_EQ_6,
+ VP_LINE_EQ_7,
+ VP_LINE_EQ_DEFAULT
+};
+
+enum s5p_vp_mem_type {
+ VP_YUV420_NV12,
+ VP_YUV420_NV21
+};
+
+enum s5p_vp_mem_mode {
+ VP_LINEAR_MODE,
+ VP_2D_TILE_MODE
+};
+
+enum s5p_vp_chroma_expansion {
+ VP_C_TOP,
+ VP_C_TOP_BOTTOM
+};
+
+enum s5p_vp_pxl_rate {
+ VP_PXL_PER_RATE_1_1,
+ VP_PXL_PER_RATE_1_2,
+ VP_PXL_PER_RATE_1_3,
+ VP_PXL_PER_RATE_1_4
+};
+
+enum s5p_vp_sharpness_control {
+ VP_SHARPNESS_NO,
+ VP_SHARPNESS_MIN,
+ VP_SHARPNESS_MOD,
+ VP_SHARPNESS_MAX
+};
+
+enum s5p_vp_csc_type {
+ VP_CSC_SD_HD,
+ VP_CSC_HD_SD
+};
+
+enum s5p_vp_csc_coeff {
+ VP_CSC_Y2Y_COEF,
+ VP_CSC_CB2Y_COEF,
+ VP_CSC_CR2Y_COEF,
+ VP_CSC_Y2CB_COEF,
+ VP_CSC_CB2CB_COEF,
+ VP_CSC_CR2CB_COEF,
+ VP_CSC_Y2CR_COEF,
+ VP_CSC_CB2CR_COEF,
+ VP_CSC_CR2CR_COEF
+};
+
+
+extern void s5p_vp_set_poly_filter_coef_default(u32 src_width, u32 src_height,
+ u32 dst_width, u32 dst_height,
+ bool ipc_2d);
+extern void s5p_vp_set_field_id(enum s5p_vp_field mode);
+extern int s5p_vp_set_top_field_address(u32 top_y_addr, u32 top_c_addr);
+extern int s5p_vp_set_bottom_field_address(u32 bottom_y_addr,
+ u32 bottom_c_addr);
+extern int s5p_vp_set_img_size(u32 img_width, u32 img_height);
+extern void s5p_vp_set_src_position(u32 src_off_x, u32 src_x_fract_step,
+ u32 src_off_y);
+extern void s5p_vp_set_dest_position(u32 dst_off_x, u32 dst_off_y);
+extern void s5p_vp_set_src_dest_size(u32 src_width, u32 src_height,
+ u32 dst_width, u32 dst_height,
+ bool ipc_2d);
+extern void s5p_vp_set_op_mode(bool line_skip, enum s5p_vp_mem_type mem_type,
+ enum s5p_vp_mem_mode mem_mode,
+ enum s5p_vp_chroma_expansion chroma_exp,
+ bool auto_toggling);
+extern void s5p_vp_set_pixel_rate_control(enum s5p_vp_pxl_rate rate);
+extern void s5p_vp_set_endian(enum s5p_tvout_endian endian);
+extern void s5p_vp_set_bypass_post_process(bool bypass);
+extern void s5p_vp_set_saturation(u32 sat);
+extern void s5p_vp_set_sharpness(u32 th_h_noise,
+ enum s5p_vp_sharpness_control sharpness);
+extern void s5p_vp_set_brightness_contrast(u16 b, u8 c);
+extern void s5p_vp_set_brightness_offset(u32 offset);
+extern int s5p_vp_set_brightness_contrast_control(enum s5p_vp_line_eq eq_num,
+ u32 intc, u32 slope);
+extern void s5p_vp_set_csc_control(bool sub_y_offset_en, bool csc_en);
+extern int s5p_vp_set_csc_coef(enum s5p_vp_csc_coeff csc_coeff, u32 coeff);
+extern int s5p_vp_set_csc_coef_default(enum s5p_vp_csc_type csc_type);
+extern int s5p_vp_update(void);
+extern int s5p_vp_get_update_status(void);
+extern void s5p_vp_sw_reset(void);
+extern int s5p_vp_start(void);
+extern int s5p_vp_stop(void);
+extern void s5p_vp_init(void __iomem *addr);
+
+/* for CEC */
+
+enum cec_state {
+ STATE_RX,
+ STATE_TX,
+ STATE_DONE,
+ STATE_ERROR
+};
+
+struct cec_rx_struct {
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+ atomic_t state;
+ u8 *buffer;
+ unsigned int size;
+};
+
+struct cec_tx_struct {
+ wait_queue_head_t waitq;
+ atomic_t state;
+};
+
+extern struct cec_rx_struct cec_rx_struct;
+extern struct cec_tx_struct cec_tx_struct;
+
+void s5p_cec_set_divider(void);
+void s5p_cec_enable_rx(void);
+void s5p_cec_mask_rx_interrupts(void);
+void s5p_cec_unmask_rx_interrupts(void);
+void s5p_cec_mask_tx_interrupts(void);
+void s5p_cec_unmask_tx_interrupts(void);
+void s5p_cec_reset(void);
+void s5p_cec_tx_reset(void);
+void s5p_cec_rx_reset(void);
+void s5p_cec_threshold(void);
+void s5p_cec_set_tx_state(enum cec_state state);
+void s5p_cec_set_rx_state(enum cec_state state);
+void s5p_cec_copy_packet(char *data, size_t count);
+void s5p_cec_set_addr(u32 addr);
+u32 s5p_cec_get_status(void);
+void s5p_clr_pending_tx(void);
+void s5p_clr_pending_rx(void);
+void s5p_cec_get_rx_buf(u32 size, u8 *buffer);
+void __init s5p_cec_mem_probe(struct platform_device *pdev);
+
+/* for HDCP */
+
+extern int s5p_hdcp_encrypt_stop(bool on);
+extern int __init s5p_hdcp_init(void);
+extern int s5p_hdcp_start(void);
+extern int s5p_hdcp_stop(void);
+
+#endif /* __S5P_TVOUT_HW_IF_H_ */
new file mode 100644
@@ -0,0 +1,731 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Hardware interface functions for video processor
+ *
+ * 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-vp.h>
+
+#include "../s5p_tvout_common_lib.h"
+#include "hw_if.h"
+
+#undef tvout_dbg
+
+#ifdef CONFIG_VP_DEBUG
+#define tvout_dbg(fmt, ...) \
+ printk(KERN_INFO "\t[VP] %s(): " fmt, \
+ __func__, ##__VA_ARGS__)
+#else
+#define tvout_dbg(fmt, ...)
+#endif
+
+/*
+ * Area for definitions to be used in only this file.
+ * This area can include #define, enum and struct defintition.
+ */
+#define H_RATIO(s_w, d_w) (((s_w) << 16) / (d_w))
+#define V_RATIO(s_h, d_h, ipc_2d) (((s_h) << ((ipc_2d) ? 17 : 16)) / (d_h))
+
+enum s5p_vp_poly_coeff {
+ VP_POLY8_Y0_LL = 0,
+ VP_POLY8_Y0_LH,
+ VP_POLY8_Y0_HL,
+ VP_POLY8_Y0_HH,
+ VP_POLY8_Y1_LL,
+ VP_POLY8_Y1_LH,
+ VP_POLY8_Y1_HL,
+ VP_POLY8_Y1_HH,
+ VP_POLY8_Y2_LL,
+ VP_POLY8_Y2_LH,
+ VP_POLY8_Y2_HL,
+ VP_POLY8_Y2_HH,
+ VP_POLY8_Y3_LL,
+ VP_POLY8_Y3_LH,
+ VP_POLY8_Y3_HL,
+ VP_POLY8_Y3_HH,
+ VP_POLY4_Y0_LL = 32,
+ VP_POLY4_Y0_LH,
+ VP_POLY4_Y0_HL,
+ VP_POLY4_Y0_HH,
+ VP_POLY4_Y1_LL,
+ VP_POLY4_Y1_LH,
+ VP_POLY4_Y1_HL,
+ VP_POLY4_Y1_HH,
+ VP_POLY4_Y2_LL,
+ VP_POLY4_Y2_LH,
+ VP_POLY4_Y2_HL,
+ VP_POLY4_Y2_HH,
+ VP_POLY4_Y3_LL,
+ VP_POLY4_Y3_LH,
+ VP_POLY4_Y3_HL,
+ VP_POLY4_Y3_HH,
+ VP_POLY4_C0_LL,
+ VP_POLY4_C0_LH,
+ VP_POLY4_C0_HL,
+ VP_POLY4_C0_HH,
+ VP_POLY4_C1_LL,
+ VP_POLY4_C1_LH,
+ VP_POLY4_C1_HL,
+ VP_POLY4_C1_HH
+};
+
+enum s5p_vp_filter_h_pp {
+ VP_PP_H_NORMAL,
+ VP_PP_H_8_9,
+ VP_PP_H_1_2,
+ VP_PP_H_1_3,
+ VP_PP_H_1_4
+};
+
+enum s5p_vp_filter_v_pp {
+ VP_PP_V_NORMAL,
+ VP_PP_V_5_6,
+ VP_PP_V_3_4,
+ VP_PP_V_1_2,
+ VP_PP_V_1_3,
+ VP_PP_V_1_4
+};
+
+/* Area for global variables to be used in only this file */
+
+static void __iomem *vp_base;
+
+/* Horizontal Y 8tap */
+
+const signed char g_s_vp8tap_coef_y_h[] = {
+ /* VP_PP_H_NORMAL */
+ 0, 0, 0, 0, 127, 0, 0, 0,
+ 0, 1, -2, 8, 126, -6, 2, -1,
+ 0, 1, -5, 16, 125, -12, 4, -1,
+ 0, 2, -8, 25, 121, -16, 5, -1,
+ -1, 3, -10, 35, 114, -18, 6, -1,
+ -1, 4, -13, 46, 107, -20, 6, -1,
+ -1, 5, -16, 57, 99, -21, 6, -1,
+ -1, 5, -18, 68, 89, -20, 6, -1,
+ -1, 6, -20, 79, 79, -20, 6, -1,
+ -1, 6, -20, 89, 68, -18, 5, -1,
+ -1, 6, -21, 99, 57, -16, 5, -1,
+ -1, 6, -20, 107, 46, -13, 4, -1,
+ -1, 6, -18, 114, 35, -10, 3, -1,
+ -1, 5, -16, 121, 25, -8, 2, 0,
+ -1, 4, -12, 125, 16, -5, 1, 0,
+ -1, 2, -6, 126, 8, -2, 1, 0,
+
+ /* VP_PP_H_8_9 */
+ 0, 3, -7, 12, 112, 12, -7, 3,
+ -1, 3, -9, 19, 113, 6, -5, 2,
+ -1, 3, -11, 27, 111, 0, -3, 2,
+ -1, 4, -13, 35, 108, -5, -1, 1,
+ -1, 4, -14, 43, 104, -9, 0, 1,
+ -1, 5, -16, 52, 99, -12, 1, 0,
+ -1, 5, -17, 61, 92, -14, 2, 0,
+ 0, 4, -17, 69, 85, -16, 3, 0,
+ 0, 4, -17, 77, 77, -17, 4, 0,
+ 0, 3, -16, 85, 69, -17, 4, 0,
+ 0, 2, -14, 92, 61, -17, 5, -1,
+ 0, 1, -12, 99, 52, -16, 5, -1,
+ 1, 0, -9, 104, 43, -14, 4, -1,
+ 1, -1, -5, 108, 35, -13, 4, -1,
+ 2, -3, 0, 111, 27, -11, 3, -1,
+ 2, -5, 6, 113, 19, -9, 3, -1,
+
+ /* VP_PP_H_1_2 */
+ 0, -3, 0, 35, 64, 35, 0, -3,
+ 0, -3, 1, 38, 64, 32, -1, -3,
+ 0, -3, 2, 41, 63, 29, -2, -2,
+ 0, -4, 4, 43, 63, 27, -3, -2,
+ 0, -4, 5, 46, 62, 24, -3, -2,
+ 0, -4, 7, 49, 60, 21, -3, -2,
+ -1, -4, 9, 51, 59, 19, -4, -1,
+ -1, -4, 12, 53, 57, 16, -4, -1,
+ -1, -4, 14, 55, 55, 14, -4, -1,
+ -1, -4, 16, 57, 53, 12, -4, -1,
+ -1, -4, 19, 59, 51, 9, -4, -1,
+ -2, -3, 21, 60, 49, 7, -4, 0,
+ -2, -3, 24, 62, 46, 5, -4, 0,
+ -2, -3, 27, 63, 43, 4, -4, 0,
+ -2, -2, 29, 63, 41, 2, -3, 0,
+ -3, -1, 32, 64, 38, 1, -3, 0,
+
+ /* VP_PP_H_1_3 */
+ 0, 0, 10, 32, 44, 32, 10, 0,
+ -1, 0, 11, 33, 45, 31, 9, 0,
+ -1, 0, 12, 35, 45, 29, 8, 0,
+ -1, 1, 13, 36, 44, 28, 7, 0,
+ -1, 1, 15, 37, 44, 26, 6, 0,
+ -1, 2, 16, 38, 43, 25, 5, 0,
+ -1, 2, 18, 39, 43, 23, 5, -1,
+ -1, 3, 19, 40, 42, 22, 4, -1,
+ -1, 3, 21, 41, 41, 21, 3, -1,
+ -1, 4, 22, 42, 40, 19, 3, -1,
+ -1, 5, 23, 43, 39, 18, 2, -1,
+ 0, 5, 25, 43, 38, 16, 2, -1,
+ 0, 6, 26, 44, 37, 15, 1, -1,
+ 0, 7, 28, 44, 36, 13, 1, -1,
+ 0, 8, 29, 45, 35, 12, 0, -1,
+ 0, 9, 31, 45, 33, 11, 0, -1,
+
+ /* VP_PP_H_1_4 */
+ 0, 2, 13, 30, 38, 30, 13, 2,
+ 0, 3, 14, 30, 38, 29, 12, 2,
+ 0, 3, 15, 31, 38, 28, 11, 2,
+ 0, 4, 16, 32, 38, 27, 10, 1,
+ 0, 4, 17, 33, 37, 26, 10, 1,
+ 0, 5, 18, 34, 37, 24, 9, 1,
+ 0, 5, 19, 34, 37, 24, 8, 1,
+ 1, 6, 20, 35, 36, 22, 7, 1,
+ 1, 6, 21, 36, 36, 21, 6, 1,
+ 1, 7, 22, 36, 35, 20, 6, 1,
+ 1, 8, 24, 37, 34, 19, 5, 0,
+ 1, 9, 24, 37, 34, 18, 5, 0,
+ 1, 10, 26, 37, 33, 17, 4, 0,
+ 1, 10, 27, 38, 32, 16, 4, 0,
+ 2, 11, 28, 38, 31, 15, 3, 0,
+ 2, 12, 29, 38, 30, 14, 3, 0
+};
+
+/* Horizontal C 4tap */
+
+const signed char g_s_vp4tap_coef_c_h[] = {
+ /* VP_PP_H_NORMAL */
+ 0, 0, 128, 0, 0, 5, 126, -3,
+ -1, 11, 124, -6, -1, 19, 118, -8,
+ -2, 27, 111, -8, -3, 37, 102, -8,
+ -4, 48, 92, -8, -5, 59, 81, -7,
+ -6, 70, 70, -6, -7, 81, 59, -5,
+ -8, 92, 48, -4, -8, 102, 37, -3,
+ -8, 111, 27, -2, -8, 118, 19, -1,
+ -6, 124, 11, -1, -3, 126, 5, 0,
+
+ /* VP_PP_H_8_9 */
+ 0, 8, 112, 8, -1, 13, 113, 3,
+ -2, 19, 111, 0, -2, 26, 107, -3,
+ -3, 34, 101, -4, -3, 42, 94, -5,
+ -4, 51, 86, -5, -5, 60, 78, -5,
+ -5, 69, 69, -5, -5, 78, 60, -5,
+ -5, 86, 51, -4, -5, 94, 42, -3,
+ -4, 101, 34, -3, -3, 107, 26, -2,
+ 0, 111, 19, -2, 3, 113, 13, -1,
+
+ /* VP_PP_H_1_2 */
+ 0, 26, 76, 26, 0, 30, 76, 22,
+ 0, 34, 75, 19, 1, 38, 73, 16,
+ 1, 43, 71, 13, 2, 47, 69, 10,
+ 3, 51, 66, 8, 4, 55, 63, 6,
+ 5, 59, 59, 5, 6, 63, 55, 4,
+ 8, 66, 51, 3, 10, 69, 47, 2,
+ 13, 71, 43, 1, 16, 73, 38, 1,
+ 19, 75, 34, 0, 22, 76, 30, 0,
+
+ /* VP_PP_H_1_3 */
+ 0, 30, 68, 30, 2, 33, 66, 27,
+ 3, 36, 66, 23, 3, 39, 65, 21,
+ 4, 43, 63, 18, 5, 46, 62, 15,
+ 6, 49, 60, 13, 8, 52, 57, 11,
+ 9, 55, 55, 9, 11, 57, 52, 8,
+ 13, 60, 49, 6, 15, 62, 46, 5,
+ 18, 63, 43, 4, 21, 65, 39, 3,
+ 23, 66, 36, 3, 27, 66, 33, 2,
+
+ /* VP_PP_H_1_4 */
+ 0, 31, 66, 31, 3, 34, 63, 28,
+ 4, 37, 62, 25, 4, 40, 62, 22,
+ 5, 43, 61, 19, 6, 46, 59, 17,
+ 7, 48, 58, 15, 9, 51, 55, 13,
+ 11, 53, 53, 11, 13, 55, 51, 9,
+ 15, 58, 48, 7, 17, 59, 46, 6,
+ 19, 61, 43, 5, 22, 62, 40, 4,
+ 25, 62, 37, 4, 28, 63, 34, 3,
+};
+
+/* Vertical Y 8tap */
+
+const signed char g_s_vp4tap_coef_y_v[] = {
+ /* VP_PP_V_NORMAL */
+ 0, 0, 127, 0, 0, 5, 126, -3,
+ -1, 11, 124, -6, -1, 19, 118, -8,
+ -2, 27, 111, -8, -3, 37, 102, -8,
+ -4, 48, 92, -8, -5, 59, 81, -7,
+ -6, 70, 70, -6, -7, 81, 59, -5,
+ -8, 92, 48, -4, -8, 102, 37, -3,
+ -8, 111, 27, -2, -8, 118, 19, -1,
+ -6, 124, 11, -1, -3, 126, 5, 0,
+
+ /* VP_PP_V_5_6 */
+ 0, 11, 106, 11, -2, 16, 107, 7,
+ -2, 22, 105, 3, -2, 29, 101, 0,
+ -3, 36, 96, -1, -3, 44, 90, -3,
+ -4, 52, 84, -4, -4, 60, 76, -4,
+ -4, 68, 68, -4, -4, 76, 60, -4,
+ -4, 84, 52, -4, -3, 90, 44, -3,
+ -1, 96, 36, -3, 0, 101, 29, -2,
+ 3, 105, 22, -2, 7, 107, 16, -2,
+
+ /* VP_PP_V_3_4 */
+ 0, 15, 98, 15, -2, 21, 97, 12,
+ -2, 26, 96, 8, -2, 32, 93, 5,
+ -2, 39, 89, 2, -2, 46, 84, 0,
+ -3, 53, 79, -1, -2, 59, 73, -2,
+ -2, 66, 66, -2, -2, 73, 59, -2,
+ -1, 79, 53, -3, 0, 84, 46, -2,
+ 2, 89, 39, -2, 5, 93, 32, -2,
+ 8, 96, 26, -2, 12, 97, 21, -2,
+
+ /* VP_PP_V_1_2 */
+ 0, 26, 76, 26, 0, 30, 76, 22,
+ 0, 34, 75, 19, 1, 38, 73, 16,
+ 1, 43, 71, 13, 2, 47, 69, 10,
+ 3, 51, 66, 8, 4, 55, 63, 6,
+ 5, 59, 59, 5, 6, 63, 55, 4,
+ 8, 66, 51, 3, 10, 69, 47, 2,
+ 13, 71, 43, 1, 16, 73, 38, 1,
+ 19, 75, 34, 0, 22, 76, 30, 0,
+
+ /* VP_PP_V_1_3 */
+ 0, 30, 68, 30, 2, 33, 66, 27,
+ 3, 36, 66, 23, 3, 39, 65, 21,
+ 4, 43, 63, 18, 5, 46, 62, 15,
+ 6, 49, 60, 13, 8, 52, 57, 11,
+ 9, 55, 55, 9, 11, 57, 52, 8,
+ 13, 60, 49, 6, 15, 62, 46, 5,
+ 18, 63, 43, 4, 21, 65, 39, 3,
+ 23, 66, 36, 3, 27, 66, 33, 2,
+
+ /* VP_PP_V_1_4 */
+ 0, 31, 66, 31, 3, 34, 63, 28,
+ 4, 37, 62, 25, 4, 40, 62, 22,
+ 5, 43, 61, 19, 6, 46, 59, 17,
+ 7, 48, 58, 15, 9, 51, 55, 13,
+ 11, 53, 53, 11, 13, 55, 51, 9,
+ 15, 58, 48, 7, 17, 59, 46, 6,
+ 19, 61, 43, 5, 22, 62, 40, 4,
+ 25, 62, 37, 4, 28, 63, 34, 3
+};
+
+/*
+ * Area for functions to be used in only this file.
+ * Functions of this area are defined by static
+ */
+
+static int s5p_vp_set_poly_filter_coef(
+ enum s5p_vp_poly_coeff poly_coeff,
+ signed char ch0, signed char ch1,
+ signed char ch2, signed char ch3)
+{
+ if (poly_coeff > VP_POLY4_C1_HH || poly_coeff < VP_POLY8_Y0_LL ||
+ (poly_coeff > VP_POLY8_Y3_HH && poly_coeff < VP_POLY4_Y0_LL)) {
+ tvout_err("invaild poly_coeff parameter\n");
+
+ return -1;
+ }
+
+ writel((((0xff & ch0) << 24) | ((0xff & ch1) << 16) |
+ ((0xff & ch2) << 8) | (0xff & ch3)),
+ vp_base + S5P_VP_POLY8_Y0_LL + poly_coeff * 4);
+
+ return 0;
+}
+
+/*
+ * Area for functions to be used by other files.
+ * Functions of this area must be defined in header file.
+ */
+
+void s5p_vp_set_poly_filter_coef_default(
+ u32 src_width, u32 src_height,
+ u32 dst_width, u32 dst_height, bool ipc_2d)
+{
+ enum s5p_vp_filter_h_pp e_h_filter;
+ enum s5p_vp_filter_v_pp e_v_filter;
+ u8 *poly_flt_coeff;
+ int i, j;
+
+ u32 h_ratio = H_RATIO(src_width, dst_width);
+ u32 v_ratio = V_RATIO(src_height, dst_height, ipc_2d);
+
+ /*
+ * For the real interlace mode, the vertical ratio should be
+ * used after divided by 2. Because in the interlace mode, all
+ * the VP output is used for SDOUT display and it should be the
+ * same as one field of the progressive mode. Therefore the same
+ * filter coefficients should be used for the same the final
+ * output video. When half of the interlace V_RATIO is same as
+ * the progressive V_RATIO, the final output video scale is same.
+ */
+
+ if (h_ratio <= (0x1 << 16)) /* 720->720 or zoom in */
+ e_h_filter = VP_PP_H_NORMAL;
+ else if (h_ratio <= (0x9 << 13)) /* 720->640 */
+ e_h_filter = VP_PP_H_8_9;
+ else if (h_ratio <= (0x1 << 17)) /* 2->1 */
+ e_h_filter = VP_PP_H_1_2;
+ else if (h_ratio <= (0x3 << 16)) /* 2->1 */
+ e_h_filter = VP_PP_H_1_3;
+ else
+ e_h_filter = VP_PP_H_1_4; /* 4->1 */
+
+ /* Vertical Y 4tap */
+
+ if (v_ratio <= (0x1 << 16)) /* 720->720 or zoom in*/
+ e_v_filter = VP_PP_V_NORMAL;
+ else if (v_ratio <= (0x5 << 14)) /* 4->3*/
+ e_v_filter = VP_PP_V_3_4;
+ else if (v_ratio <= (0x3 << 15)) /*6->5*/
+ e_v_filter = VP_PP_V_5_6;
+ else if (v_ratio <= (0x1 << 17)) /* 2->1*/
+ e_v_filter = VP_PP_V_1_2;
+ else if (v_ratio <= (0x3 << 16)) /* 3->1*/
+ e_v_filter = VP_PP_V_1_3;
+ else
+ e_v_filter = VP_PP_V_1_4;
+
+ poly_flt_coeff = (u8 *)(g_s_vp8tap_coef_y_h + e_h_filter * 16 * 8);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ s5p_vp_set_poly_filter_coef(
+ VP_POLY8_Y0_LL + (i*4) + j,
+ *(poly_flt_coeff + 4*j*8 + (7 - i)),
+ *(poly_flt_coeff + (4*j + 1)*8 + (7 - i)),
+ *(poly_flt_coeff + (4*j + 2)*8 + (7 - i)),
+ *(poly_flt_coeff + (4*j + 3)*8 + (7 - i)));
+ }
+ }
+
+ poly_flt_coeff = (u8 *)(g_s_vp4tap_coef_c_h + e_h_filter * 16 * 4);
+
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 4; j++) {
+ s5p_vp_set_poly_filter_coef(
+ VP_POLY4_C0_LL + (i*4) + j,
+ *(poly_flt_coeff + 4*j*4 + (3 - i)),
+ *(poly_flt_coeff + (4*j + 1)*4 + (3 - i)),
+ *(poly_flt_coeff + (4*j + 2)*4 + (3 - i)),
+ *(poly_flt_coeff + (4*j + 3)*4 + (3 - i)));
+ }
+ }
+
+ poly_flt_coeff = (u8 *)(g_s_vp4tap_coef_y_v + e_v_filter * 16 * 4);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ s5p_vp_set_poly_filter_coef(
+ VP_POLY4_Y0_LL + (i*4) + j,
+ *(poly_flt_coeff + 4*j*4 + (3 - i)),
+ *(poly_flt_coeff + (4*j + 1)*4 + (3 - i)),
+ *(poly_flt_coeff + (4*j + 2)*4 + (3 - i)),
+ *(poly_flt_coeff + (4*j + 3)*4 + (3 - i)));
+ }
+ }
+}
+
+void s5p_vp_set_field_id(enum s5p_vp_field mode)
+{
+ writel((mode == VP_TOP_FIELD) ? VP_TOP_FIELD : VP_BOTTOM_FIELD,
+ vp_base + S5P_VP_FIELD_ID);
+}
+
+int s5p_vp_set_top_field_address(u32 top_y_addr, u32 top_c_addr)
+{
+ if (S5P_VP_PTR_ILLEGAL(top_y_addr) || S5P_VP_PTR_ILLEGAL(top_c_addr)) {
+ tvout_err("address is not double word align = 0x%x, 0x%x\n",
+ top_y_addr, top_c_addr);
+
+ return -1;
+ }
+
+ writel(top_y_addr, vp_base + S5P_VP_TOP_Y_PTR);
+ writel(top_c_addr, vp_base + S5P_VP_TOP_C_PTR);
+
+ return 0;
+}
+
+int s5p_vp_set_bottom_field_address(u32 bottom_y_addr, u32 bottom_c_addr)
+{
+ if (S5P_VP_PTR_ILLEGAL(bottom_y_addr) ||
+ S5P_VP_PTR_ILLEGAL(bottom_c_addr)) {
+ tvout_err("address is not double word align = 0x%x, 0x%x\n",
+ bottom_y_addr, bottom_c_addr);
+
+ return -1;
+ }
+
+ writel(bottom_y_addr, vp_base + S5P_VP_BOT_Y_PTR);
+ writel(bottom_c_addr, vp_base + S5P_VP_BOT_C_PTR);
+
+ return 0;
+}
+
+int s5p_vp_set_img_size(u32 img_width, u32 img_height)
+{
+ if (S5P_VP_IMG_SIZE_ILLEGAL(img_width) ||
+ S5P_VP_IMG_SIZE_ILLEGAL(img_height)) {
+ tvout_err("full image size is not double word align ="
+ "%d, %d\n", img_width, img_height);
+
+ return -1;
+ }
+
+ writel(S5P_VP_IMG_HSIZE(img_width) | S5P_VP_IMG_VSIZE(img_height),
+ vp_base + S5P_VP_IMG_SIZE_Y);
+ writel(S5P_VP_IMG_HSIZE(img_width) | S5P_VP_IMG_VSIZE(img_height / 2),
+ vp_base + S5P_VP_IMG_SIZE_C);
+
+ return 0;
+}
+
+void s5p_vp_set_src_position(u32 src_off_x, u32 src_x_fract_step, u32 src_off_y)
+{
+ writel(S5P_VP_SRC_H_POSITION_VAL(src_off_x) |
+ S5P_VP_SRC_X_FRACT_STEP(src_x_fract_step),
+ vp_base + S5P_VP_SRC_H_POSITION);
+ writel(S5P_VP_SRC_V_POSITION_VAL(src_off_y),
+ vp_base + S5P_VP_SRC_V_POSITION);
+}
+
+void s5p_vp_set_dest_position(u32 dst_off_x, u32 dst_off_y)
+{
+ writel(S5P_VP_DST_H_POSITION_VAL(dst_off_x),
+ vp_base + S5P_VP_DST_H_POSITION);
+ writel(S5P_VP_DST_V_POSITION_VAL(dst_off_y),
+ vp_base + S5P_VP_DST_V_POSITION);
+}
+
+void s5p_vp_set_src_dest_size(u32 src_width, u32 src_height,
+ u32 dst_width, u32 dst_height, bool ipc_2d)
+{
+ u32 h_ratio = H_RATIO(src_width, dst_width);
+ u32 v_ratio = V_RATIO(src_height, dst_height, ipc_2d);
+
+ writel(S5P_VP_SRC_WIDTH_VAL(src_width), vp_base + S5P_VP_SRC_WIDTH);
+ writel(S5P_VP_SRC_HEIGHT_VAL(src_height), vp_base + S5P_VP_SRC_HEIGHT);
+ writel(S5P_VP_DST_WIDTH_VAL(dst_width), vp_base + S5P_VP_DST_WIDTH);
+ writel(S5P_VP_DST_HEIGHT_VAL(dst_height), vp_base + S5P_VP_DST_HEIGHT);
+ writel(S5P_VP_H_RATIO_VAL(h_ratio), vp_base + S5P_VP_H_RATIO);
+ writel(S5P_VP_V_RATIO_VAL(v_ratio), vp_base + S5P_VP_V_RATIO);
+
+ writel((ipc_2d) ?
+ (readl(vp_base + S5P_VP_MODE) | S5P_VP_MODE_2D_IPC_ENABLE) :
+ (readl(vp_base + S5P_VP_MODE) & ~S5P_VP_MODE_2D_IPC_ENABLE),
+ vp_base + S5P_VP_MODE);
+}
+
+void s5p_vp_set_op_mode(bool line_skip, enum s5p_vp_mem_type mem_type,
+ enum s5p_vp_mem_mode mem_mode,
+ enum s5p_vp_chroma_expansion chroma_exp,
+ bool auto_toggling)
+{
+ u32 temp_reg;
+
+ temp_reg = (mem_type) ?
+ S5P_VP_MODE_IMG_TYPE_YUV420_NV21 : S5P_VP_MODE_IMG_TYPE_YUV420_NV12;
+ temp_reg |= (line_skip) ?
+ S5P_VP_MODE_LINE_SKIP_ON : S5P_VP_MODE_LINE_SKIP_OFF;
+ temp_reg |= (mem_mode == VP_2D_TILE_MODE) ?
+ S5P_VP_MODE_MEM_MODE_2D_TILE :
+ S5P_VP_MODE_MEM_MODE_LINEAR;
+ temp_reg |= (chroma_exp == VP_C_TOP_BOTTOM) ?
+ S5P_VP_MODE_CROMA_EXP_C_TOPBOTTOM_PTR :
+ S5P_VP_MODE_CROMA_EXP_C_TOP_PTR;
+ temp_reg |= (auto_toggling) ?
+ S5P_VP_MODE_FIELD_ID_AUTO_TOGGLING :
+ S5P_VP_MODE_FIELD_ID_MAN_TOGGLING;
+
+ writel(temp_reg, vp_base + S5P_VP_MODE);
+}
+
+void s5p_vp_set_pixel_rate_control(enum s5p_vp_pxl_rate rate)
+{
+ writel(S5P_VP_PEL_RATE_CTRL(rate), vp_base + S5P_VP_PER_RATE_CTRL);
+}
+
+void s5p_vp_set_endian(enum s5p_tvout_endian endian)
+{
+ writel(endian, vp_base + S5P_VP_ENDIAN_MODE);
+}
+
+void s5p_vp_set_bypass_post_process(bool bypass)
+{
+ writel((bypass) ? S5P_VP_BY_PASS_ENABLE : S5P_VP_BY_PASS_DISABLE,
+ vp_base + S5P_PP_BYPASS);
+}
+
+void s5p_vp_set_saturation(u32 sat)
+{
+ writel(S5P_VP_SATURATION(sat), vp_base + S5P_PP_SATURATION);
+}
+
+void s5p_vp_set_sharpness(u32 th_h_noise,
+ enum s5p_vp_sharpness_control sharpness)
+{
+ writel(S5P_VP_TH_HNOISE(th_h_noise) | S5P_VP_SHARPNESS(sharpness),
+ vp_base + S5P_PP_SHARPNESS);
+}
+
+void s5p_vp_set_brightness_contrast(u16 b, u8 c)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ writel(S5P_VP_LINE_INTC(b) | S5P_VP_LINE_SLOPE(c),
+ vp_base + S5P_PP_LINE_EQ0 + i*4);
+}
+
+void s5p_vp_set_brightness_offset(u32 offset)
+{
+ writel(S5P_VP_BRIGHT_OFFSET(offset), vp_base + S5P_PP_BRIGHT_OFFSET);
+}
+
+int s5p_vp_set_brightness_contrast_control(enum s5p_vp_line_eq eq_num,
+ u32 intc, u32 slope)
+{
+ if (eq_num > VP_LINE_EQ_7 || eq_num < VP_LINE_EQ_0) {
+ tvout_err("invaild eq_num parameter\n");
+
+ return -1;
+ }
+
+ writel(S5P_VP_LINE_INTC(intc) | S5P_VP_LINE_SLOPE(slope),
+ vp_base + S5P_PP_LINE_EQ0 + eq_num*4);
+
+ return 0;
+}
+
+void s5p_vp_set_csc_control(bool sub_y_offset_en, bool csc_en)
+{
+ u32 temp_reg;
+
+ temp_reg = (sub_y_offset_en) ? S5P_VP_SUB_Y_OFFSET_ENABLE :
+ S5P_VP_SUB_Y_OFFSET_DISABLE;
+ temp_reg |= (csc_en) ? S5P_VP_CSC_ENABLE : S5P_VP_CSC_DISABLE;
+
+ writel(temp_reg, vp_base + S5P_PP_CSC_EN);
+}
+
+int s5p_vp_set_csc_coef(enum s5p_vp_csc_coeff csc_coeff, u32 coeff)
+{
+ if (csc_coeff > VP_CSC_CR2CR_COEF ||
+ csc_coeff < VP_CSC_Y2Y_COEF) {
+ tvout_err("invaild csc_coeff parameter\n");
+
+ return -1;
+ }
+
+ writel(S5P_PP_CSC_COEF(coeff),
+ vp_base + S5P_PP_CSC_Y2Y_COEF + csc_coeff*4);
+
+ return 0;
+}
+
+int s5p_vp_set_csc_coef_default(enum s5p_vp_csc_type csc_type)
+{
+ switch (csc_type) {
+ case VP_CSC_SD_HD:
+ writel(S5P_PP_Y2Y_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_Y2Y_COEF);
+ writel(S5P_PP_CB2Y_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_CB2Y_COEF);
+ writel(S5P_PP_CR2Y_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_CR2Y_COEF);
+ writel(S5P_PP_Y2CB_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_Y2CB_COEF);
+ writel(S5P_PP_CB2CB_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_CB2CB_COEF);
+ writel(S5P_PP_CR2CB_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_CR2CB_COEF);
+ writel(S5P_PP_Y2CR_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_Y2CR_COEF);
+ writel(S5P_PP_CB2CR_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_CB2CR_COEF);
+ writel(S5P_PP_CR2CR_COEF_601_TO_709,
+ vp_base + S5P_PP_CSC_CR2CR_COEF);
+ break;
+
+ case VP_CSC_HD_SD:
+ writel(S5P_PP_Y2Y_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_Y2Y_COEF);
+ writel(S5P_PP_CB2Y_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_CB2Y_COEF);
+ writel(S5P_PP_CR2Y_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_CR2Y_COEF);
+ writel(S5P_PP_Y2CB_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_Y2CB_COEF);
+ writel(S5P_PP_CB2CB_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_CB2CB_COEF);
+ writel(S5P_PP_CR2CB_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_CR2CB_COEF);
+ writel(S5P_PP_Y2CR_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_Y2CR_COEF);
+ writel(S5P_PP_CB2CR_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_CB2CR_COEF);
+ writel(S5P_PP_CR2CR_COEF_709_TO_601,
+ vp_base + S5P_PP_CSC_CR2CR_COEF);
+ break;
+
+ default:
+ tvout_err("invalid csc_type parameter = %d\n", csc_type);
+ return -1;
+ }
+
+ return 0;
+}
+
+int s5p_vp_update(void)
+{
+ writel(readl(vp_base + S5P_VP_SHADOW_UPDATE) |
+ S5P_VP_SHADOW_UPDATE_ENABLE,
+ vp_base + S5P_VP_SHADOW_UPDATE);
+
+ return 0;
+}
+
+int s5p_vp_get_update_status(void)
+{
+ if (readl(vp_base + S5P_VP_SHADOW_UPDATE) & S5P_VP_SHADOW_UPDATE_ENABLE)
+ return 0;
+ else
+ return -1;
+}
+
+void s5p_vp_sw_reset(void)
+{
+ writel((readl(vp_base + S5P_VP_SRESET) | S5P_VP_SRESET_PROCESSING),
+ vp_base + S5P_VP_SRESET);
+
+ while (readl(vp_base + S5P_VP_SRESET) & S5P_VP_SRESET_PROCESSING)
+ msleep(20);
+}
+
+int s5p_vp_start(void)
+{
+ writel(S5P_VP_ENABLE_ON, vp_base + S5P_VP_ENABLE);
+
+ s5p_vp_update();
+
+ return 0;
+}
+
+int s5p_vp_stop(void)
+{
+ writel((readl(vp_base + S5P_VP_ENABLE) & ~S5P_VP_ENABLE_ON),
+ vp_base + S5P_VP_ENABLE);
+
+ s5p_vp_update();
+
+ while (!(readl(vp_base + S5P_VP_ENABLE) & S5P_VP_ENABLE_OPERATING))
+ msleep(20);
+
+ return 0;
+}
+
+void s5p_vp_init(void __iomem *addr)
+{
+ vp_base = addr;
+}
new file mode 100644
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * S5P TVOUT driver main
+ *
+ * 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/io.h>
+#include <linux/mm.h>
+
+#include "s5p_tvout_common_lib.h"
+#include "s5p_tvout_ctrl.h"
+#include "s5p_tvout_fb.h"
+#include "s5p_tvout_v4l2.h"
+
+#define TV_CLK_GET_WITH_ERR_CHECK(clk, pdev, clk_name) \
+ do { \
+ clk = clk_get(&pdev->dev, clk_name); \
+ if (IS_ERR(clk)) { \
+ printk(KERN_ERR \
+ "failed to find clock %s\n", clk_name); \
+ return -ENOENT; \
+ } \
+ } while (0);
+
+struct s5p_tvout_status s5ptv_status;
+
+static int __devinit s5p_tvout_clk_get(struct platform_device *pdev,
+ struct s5p_tvout_status *ctrl)
+{
+ struct clk *ext_xtal_clk, *mout_vpll_src, *fout_vpll, *mout_vpll;
+
+ TV_CLK_GET_WITH_ERR_CHECK(ctrl->i2c_phy_clk, pdev, "i2c-hdmiphy");
+
+ TV_CLK_GET_WITH_ERR_CHECK(ctrl->sclk_dac, pdev, "sclk_dac");
+ TV_CLK_GET_WITH_ERR_CHECK(ctrl->sclk_hdmi, pdev, "sclk_hdmi");
+
+ TV_CLK_GET_WITH_ERR_CHECK(ctrl->sclk_pixel, pdev, "sclk_pixel");
+ TV_CLK_GET_WITH_ERR_CHECK(ctrl->sclk_hdmiphy, pdev, "sclk_hdmiphy");
+
+ TV_CLK_GET_WITH_ERR_CHECK(ext_xtal_clk, pdev, "ext_xtal");
+ TV_CLK_GET_WITH_ERR_CHECK(mout_vpll_src, pdev, "vpll_src");
+ TV_CLK_GET_WITH_ERR_CHECK(fout_vpll, pdev, "fout_vpll");
+ TV_CLK_GET_WITH_ERR_CHECK(mout_vpll, pdev, "sclk_vpll");
+
+ if (clk_set_rate(fout_vpll, 54000000) < 0)
+ return -1;
+
+ if (clk_set_parent(mout_vpll_src, ext_xtal_clk) < 0)
+ return -1;
+
+ if (clk_set_parent(mout_vpll, fout_vpll) < 0)
+ return -1;
+
+ /* sclk_dac's parent is fixed as mout_vpll */
+ if (clk_set_parent(ctrl->sclk_dac, mout_vpll) < 0)
+ return -1;
+
+ /* It'll be moved in the future */
+ if (clk_enable(ctrl->i2c_phy_clk) < 0)
+ return -1;
+
+ if (clk_enable(mout_vpll_src) < 0)
+ return -1;
+
+ if (clk_enable(fout_vpll) < 0)
+ return -1;
+
+ if (clk_enable(mout_vpll) < 0)
+ return -1;
+
+ clk_put(ext_xtal_clk);
+ clk_put(mout_vpll_src);
+ clk_put(fout_vpll);
+ clk_put(mout_vpll);
+
+ return 0;
+}
+
+static int __devinit s5p_tvout_probe(struct platform_device *pdev)
+{
+ s5p_tvout_pm_runtime_enable(&pdev->dev);
+
+ /* The feature of System MMU will be turned on later */
+
+ if (s5p_tvout_clk_get(pdev, &s5ptv_status) < 0)
+ return -ENODEV;
+
+ if (s5p_vp_ctrl_constructor(pdev) < 0)
+ return -ENODEV;
+
+ /*
+ * s5p_mixer_ctrl_constructor() must be
+ * called before s5p_tvif_ctrl_constructor
+ */
+ if (s5p_mixer_ctrl_constructor(pdev) < 0)
+ return -ENODEV;
+
+ if (s5p_tvif_ctrl_constructor(pdev) < 0)
+ return -ENODEV;
+
+ if (s5p_tvout_v4l2_constructor(pdev) < 0)
+ return -ENODEV;
+
+ if (s5p_tvif_ctrl_start(TVOUT_720P_60, TVOUT_HDMI) < 0)
+ return -ENODEV;
+
+ /* prepare memory */
+ if (s5p_tvout_fb_alloc_framebuffer(&pdev->dev))
+ return -ENODEV;
+
+ if (s5p_tvout_fb_register_framebuffer(&pdev->dev))
+ return -ENODEV;
+
+ return 0;
+}
+
+static void s5p_tvout_remove(struct platform_device *pdev)
+{
+ s5p_vp_ctrl_destructor();
+ s5p_tvif_ctrl_destructor();
+ s5p_mixer_ctrl_destructor();
+
+ s5p_tvout_v4l2_destructor();
+
+ clk_disable(s5ptv_status.sclk_hdmi);
+
+ clk_put(s5ptv_status.sclk_hdmi);
+ clk_put(s5ptv_status.sclk_dac);
+ clk_put(s5ptv_status.sclk_pixel);
+ clk_put(s5ptv_status.sclk_hdmiphy);
+
+ s5p_tvout_pm_runtime_disable(&pdev->dev);
+}
+
+#ifdef CONFIG_PM
+static int s5p_tvout_suspend(struct device *dev)
+{
+ s5p_vp_ctrl_suspend();
+ s5p_mixer_ctrl_suspend();
+ s5p_tvif_ctrl_suspend();
+
+ return 0;
+}
+
+static int s5p_tvout_resume(struct device *dev)
+{
+ s5p_tvif_ctrl_resume();
+ s5p_mixer_ctrl_resume();
+ s5p_vp_ctrl_resume();
+
+ return 0;
+}
+
+static int s5p_tvout_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int s5p_tvout_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+#else
+#define s5p_tvout_suspend NULL
+#define s5p_tvout_resume NULL
+#define s5p_tvout_runtime_suspend NULL
+#define s5p_tvout_runtime_resume NULL
+#endif
+
+static const struct dev_pm_ops s5p_tvout_pm_ops = {
+ .suspend = s5p_tvout_suspend,
+ .resume = s5p_tvout_resume,
+ .runtime_suspend = s5p_tvout_runtime_suspend,
+ .runtime_resume = s5p_tvout_runtime_resume
+};
+
+static struct platform_driver s5p_tvout_driver = {
+ .probe = s5p_tvout_probe,
+ .remove = s5p_tvout_remove,
+ .driver = {
+ .name = "s5p-tvout",
+ .owner = THIS_MODULE,
+ .pm = &s5p_tvout_pm_ops
+ },
+};
+
+static int __init s5p_tvout_init(void)
+{
+ printk(KERN_INFO "S5P TVOUT Driver, Copyright (c) 2011 Samsung Electronics Co., LTD.\n");
+
+ return platform_driver_register(&s5p_tvout_driver);
+}
+
+static void __exit s5p_tvout_exit(void)
+{
+ platform_driver_unregister(&s5p_tvout_driver);
+}
+late_initcall(s5p_tvout_init);
+module_exit(s5p_tvout_exit);
+
+MODULE_AUTHOR("Jiun Yu <jiun.yu@samsung.com>");
+MODULE_DESCRIPTION("Samsung S5P TVOUT driver");
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Common library 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.
+*/
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <linux/pm_runtime.h>
+
+#include "s5p_tvout_common_lib.h"
+
+int s5p_tvout_map_resource_mem(struct platform_device *pdev, char *name,
+ void __iomem **base, struct resource **res)
+{
+ size_t size;
+ void __iomem *tmp_base;
+ struct resource *tmp_res;
+
+ tmp_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+
+ if (!tmp_res)
+ goto not_found;
+
+ size = resource_size(tmp_res);
+
+ tmp_res = request_mem_region(tmp_res->start, size, tmp_res->name);
+
+ if (!tmp_res) {
+ tvout_err("%s: fail to get memory region\n", __func__);
+ goto err_on_request_mem_region;
+ }
+
+ tmp_base = ioremap(tmp_res->start, size);
+
+ if (!tmp_base) {
+ tvout_err("%s: fail to ioremap address region\n", __func__);
+ goto err_on_ioremap;
+ }
+
+ *res = tmp_res;
+ *base = tmp_base;
+ return 0;
+
+err_on_ioremap:
+ release_resource(tmp_res);
+ kfree(tmp_res);
+
+err_on_request_mem_region:
+ return -ENXIO;
+
+not_found:
+ tvout_err("%s: fail to get IORESOURCE_MEM for %s\n", __func__, name);
+ return -ENODEV;
+}
+
+void s5p_tvout_unmap_resource_mem(void __iomem *base, struct resource *res)
+{
+ if (!base)
+ iounmap(base);
+
+ if (res != NULL) {
+ release_resource(res);
+ kfree(res);
+ }
+}
+
+/* Libraries for runtime PM */
+
+static struct device *s5p_tvout_dev;
+
+void s5p_tvout_pm_runtime_enable(struct device *dev)
+{
+ pm_runtime_enable(dev);
+
+ s5p_tvout_dev = dev;
+}
+
+void s5p_tvout_pm_runtime_disable(struct device *dev)
+{
+ pm_runtime_disable(dev);
+}
+
+void s5p_tvout_pm_runtime_get(void)
+{
+ pm_runtime_get_sync(s5p_tvout_dev);
+}
+
+void s5p_tvout_pm_runtime_put(void)
+{
+ pm_runtime_put_sync(s5p_tvout_dev);
+}
new file mode 100644
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file of common library 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_COMMON_LIB_H_
+#define __S5P_TVOUT_COMMON_LIB_H_ __FILE__
+
+#include <linux/stddef.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/interrupt.h>
+
+/*
+ * This file includes declarations for TVOUT driver's common library.
+ * All files in TVOUT driver can access function or definition in this file.
+ */
+
+#define DRV_NAME "S5P-TVOUT"
+
+#define tvout_err(fmt, ...) \
+ printk(KERN_ERR "[%s] %s(): " fmt, \
+ DRV_NAME, __func__, ##__VA_ARGS__)
+
+#ifndef tvout_dbg
+#ifdef CONFIG_S5P_TVOUT_DEBUG
+#define tvout_dbg(fmt, ...) \
+ printk(KERN_INFO "[%s] %s(): " fmt, \
+ DRV_NAME, __func__, ##__VA_ARGS__)
+#else
+#define tvout_dbg(fmt, ...)
+#endif
+#endif
+
+#define S5PTV_FB_CNT 2
+#define HDMI_START_NUM 0x1000
+
+#define to_tvout_plat(d) (to_platform_device(d)->dev.platform_data)
+
+enum s5p_tvout_disp_mode {
+ TVOUT_NTSC_M,
+ TVOUT_PAL_BDGHI,
+ TVOUT_PAL_M,
+ TVOUT_PAL_N,
+ TVOUT_PAL_NC,
+ TVOUT_PAL_60,
+ TVOUT_NTSC_443,
+
+ TVOUT_480P_60_16_9 = HDMI_START_NUM,
+ TVOUT_480P_60_4_3,
+ TVOUT_480P_59,
+
+ TVOUT_576P_50_16_9,
+ TVOUT_576P_50_4_3,
+
+ TVOUT_720P_60,
+ TVOUT_720P_50,
+ TVOUT_720P_59,
+
+ TVOUT_1080P_60,
+ TVOUT_1080P_50,
+ TVOUT_1080P_59,
+ TVOUT_1080P_30,
+
+ TVOUT_1080I_60,
+ TVOUT_1080I_50,
+ TVOUT_1080I_59,
+ TVOUT_INIT_DISP_VALUE
+};
+
+enum s5p_tvout_o_mode {
+ TVOUT_COMPOSITE,
+ TVOUT_HDMI,
+ TVOUT_HDMI_RGB,
+ TVOUT_DVI,
+ TVOUT_INIT_O_VALUE
+};
+
+enum s5p_mixer_burst_mode {
+ MIXER_BURST_8,
+ MIXER_BURST_16,
+};
+
+enum s5ptvfb_data_path_t {
+ DATA_PATH_FIFO,
+ DATA_PATH_DMA,
+};
+
+enum s5ptvfb_alpha_t {
+ LAYER_BLENDING,
+ PIXEL_BLENDING,
+ NONE_BLENDING,
+};
+
+enum s5ptvfb_ver_scaling_t {
+ VERTICAL_X1,
+ VERTICAL_X2,
+};
+
+enum s5ptvfb_hor_scaling_t {
+ HORIZONTAL_X1,
+ HORIZONTAL_X2,
+};
+
+struct s5ptvfb_alpha {
+ enum s5ptvfb_alpha_t mode;
+ int channel;
+ unsigned int value;
+};
+
+struct s5ptvfb_chroma {
+ int enabled;
+ unsigned int key;
+};
+
+struct s5ptvfb_user_window {
+ int x;
+ int y;
+};
+
+struct s5ptvfb_user_plane_alpha {
+ int channel;
+ unsigned char alpha;
+};
+
+struct s5ptvfb_user_chroma {
+ int enabled;
+ unsigned char red;
+ unsigned char green;
+ unsigned char blue;
+};
+
+struct s5ptvfb_user_scaling {
+ enum s5ptvfb_ver_scaling_t ver;
+ enum s5ptvfb_hor_scaling_t hor;
+};
+
+struct s5p_tvout_status {
+ struct clk *i2c_phy_clk;
+ struct clk *sclk_hdmiphy;
+ struct clk *sclk_pixel;
+ struct clk *sclk_dac;
+ struct clk *sclk_hdmi;
+};
+
+struct reg_mem_info {
+ char *name;
+ struct resource *res;
+ void __iomem *base;
+};
+
+struct irq_info {
+ char *name;
+ irq_handler_t handler;
+ int no;
+};
+
+struct s5p_tvout_clk_info {
+ char *name;
+ struct clk *ptr;
+};
+
+extern struct s5p_tvout_status s5ptv_status;
+
+extern int s5p_tvout_vcm_create_unified(void);
+extern int s5p_tvout_vcm_init(void);
+extern void s5p_tvout_vcm_activate(void);
+extern void s5p_tvout_vcm_deactivate(void);
+
+extern int s5p_tvout_map_resource_mem(struct platform_device *pdev,
+ char *name, void __iomem **base,
+ struct resource **res);
+extern void s5p_tvout_unmap_resource_mem(void __iomem *base,
+ struct resource *res);
+
+extern void s5p_tvout_pm_runtime_enable(struct device *dev);
+extern void s5p_tvout_pm_runtime_disable(struct device *dev);
+extern void s5p_tvout_pm_runtime_get(void);
+extern void s5p_tvout_pm_runtime_put(void);
+
+#endif /* __S5P_TVOUT_COMMON_LIB_H_ */
new file mode 100644
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Header file for tvout control class of 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.
+*/
+
+#ifndef __S5P_TVOUT_CTRL_H_
+#define __S5P_TVOUT_CTRL_H_ __FILE__
+
+/*
+ * This file includes declarations for external functions of
+ * TVOUT driver's control class. So only external functions
+ * to be used by higher layer must exist in this file.
+ *
+ * Higher layer must use only the declarations included in this file.
+ */
+
+#include "hw_if/hw_if.h"
+#include "s5p_tvout_common_lib.h"
+
+/* for Mixer control class */
+
+extern void s5p_mixer_ctrl_init_fb_addr_phy(enum s5p_mixer_layer layer,
+ dma_addr_t fb_addr);
+extern void s5p_mixer_ctrl_init_grp_layer(enum s5p_mixer_layer layer);
+extern int s5p_mixer_ctrl_set_pixel_format(enum s5p_mixer_layer layer,
+ u32 bpp, u32 trans_len);
+extern int s5p_mixer_ctrl_enable_layer(enum s5p_mixer_layer layer);
+extern int s5p_mixer_ctrl_disable_layer(enum s5p_mixer_layer layer);
+extern int s5p_mixer_ctrl_set_priority(enum s5p_mixer_layer layer, int prio);
+extern int s5p_mixer_ctrl_set_dst_win_pos(enum s5p_mixer_layer layer,
+ int dst_x, int dst_y, u32 w, u32 h);
+extern int s5p_mixer_ctrl_set_src_win_pos(enum s5p_mixer_layer layer,
+ u32 src_x, u32 src_y, u32 w, u32 h);
+extern int s5p_mixer_ctrl_set_buffer_address(enum s5p_mixer_layer layer,
+ dma_addr_t start_addr);
+extern int s5p_mixer_ctrl_set_chroma_key(enum s5p_mixer_layer layer,
+ struct s5ptvfb_chroma chroma);
+extern int s5p_mixer_ctrl_set_alpha(enum s5p_mixer_layer layer, u32 alpha);
+extern int s5p_mixer_ctrl_set_blend_mode(enum s5p_mixer_layer layer,
+ enum s5ptvfb_alpha_t mode);
+extern int s5p_mixer_ctrl_set_alpha_blending(enum s5p_mixer_layer layer,
+ enum s5ptvfb_alpha_t blend_mode,
+ unsigned int alpha);
+extern int s5p_mixer_ctrl_scaling(enum s5p_mixer_layer,
+ struct s5ptvfb_user_scaling scaling);
+extern int s5p_mixer_ctrl_mux_clk(struct clk *ptr);
+extern void s5p_mixer_ctrl_set_int_enable(bool en);
+extern void s5p_mixer_ctrl_set_vsync_interrupt(bool en);
+extern void s5p_mixer_ctrl_clear_pend_all(void);
+extern void s5p_mixer_ctrl_stop(void);
+extern void s5p_mixer_ctrl_internal_start(void);
+extern int s5p_mixer_ctrl_start(enum s5p_tvout_disp_mode disp,
+ enum s5p_tvout_o_mode out);
+extern int s5p_mixer_ctrl_constructor(struct platform_device *pdev);
+extern void s5p_mixer_ctrl_destructor(void);
+extern void s5p_mixer_ctrl_suspend(void);
+extern void s5p_mixer_ctrl_resume(void);
+
+/* Interrupt for Vsync */
+
+struct s5p_tv_irq {
+ wait_queue_head_t wq;
+ unsigned int wq_count;
+};
+
+extern wait_queue_head_t s5ptv_wq;
+
+/* for TV interface control class */
+
+extern int s5p_tvif_ctrl_set_audio(bool en);
+extern int s5p_tvif_ctrl_set_av_mute(bool en);
+extern int s5p_tvif_ctrl_get_std_if(enum s5p_tvout_disp_mode *std,
+ enum s5p_tvout_o_mode *inf);
+extern bool s5p_tvif_ctrl_get_run_state(void);
+extern int s5p_tvif_ctrl_start(enum s5p_tvout_disp_mode std,
+ enum s5p_tvout_o_mode inf);
+extern void s5p_tvif_ctrl_stop(void);
+
+extern int s5p_tvif_ctrl_constructor(struct platform_device *pdev);
+extern void s5p_tvif_ctrl_destructor(void);
+extern void s5p_tvif_ctrl_suspend(void);
+extern void s5p_tvif_ctrl_resume(void);
+
+extern u8 s5p_hdmi_ctrl_get_mute(void);
+extern void s5p_hdmi_ctrl_set_hdcp(bool en);
+
+extern int s5p_hpd_set_hdmiint(void);
+extern int s5p_hpd_set_eint(void);
+extern void s5p_hpd_set_kobj(struct kobject *tvout_kobj,
+ struct kobject *video_kobj);
+
+/* for VP control class */
+enum s5p_vp_src_color {
+ VP_SRC_COLOR_NV12,
+ VP_SRC_COLOR_NV12IW,
+ VP_SRC_COLOR_TILE_NV12,
+ VP_SRC_COLOR_TILE_NV12IW,
+ VP_SRC_COLOR_NV21,
+ VP_SRC_COLOR_NV21IW,
+ VP_SRC_COLOR_TILE_NV21,
+ VP_SRC_COLOR_TILE_NV21IW
+};
+
+extern void s5p_vp_ctrl_set_src_plane(u32 base_y, u32 base_c,
+ u32 width, u32 height,
+ enum s5p_vp_src_color color,
+ enum s5p_vp_field field);
+extern void s5p_vp_ctrl_set_src_win(u32 left, u32 top, u32 width, u32 height);
+extern void s5p_vp_ctrl_set_dest_win(u32 left, u32 top, u32 width, u32 height);
+extern void s5p_vp_ctrl_set_dest_win_alpha_val(u32 alpha);
+extern void s5p_vp_ctrl_set_dest_win_blend(bool enable);
+extern void s5p_vp_ctrl_set_dest_win_priority(u32 prio);
+extern int s5p_vp_ctrl_start(void);
+extern void s5p_vp_ctrl_stop(void);
+extern int s5p_vp_ctrl_constructor(struct platform_device *pdev);
+extern void s5p_vp_ctrl_destructor(void);
+extern void s5p_vp_ctrl_suspend(void);
+void s5p_vp_ctrl_resume(void);
+
+#endif /* __S5P_TVOUT_CTRL_H_ */
new file mode 100644
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Frame buffer 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.
+*/
+
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+
+#include "s5p_tvout_common_lib.h"
+#include "s5p_tvout_ctrl.h"
+#include "s5p_tvout_v4l2.h"
+
+#define S5PTVFB_NAME "s5ptvfb"
+
+#define S5PTV_FB_LAYER0_MINOR 10
+#define S5PTV_FB_LAYER1_MINOR 11
+
+#define FB_INDEX(id) (id - S5PTV_FB_LAYER0_MINOR)
+
+#define S5PTVFB_CHROMA(r, g, b) \
+ (((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b & 0xff) << 0))
+
+#define S5PTVFB_WIN_POSITION _IOW('F', 213, struct s5ptvfb_user_window)
+#define S5PTVFB_WIN_SET_PLANE_ALPHA _IOW('F', 214, struct s5ptvfb_user_plane_alpha)
+#define S5PTVFB_WIN_SET_CHROMA _IOW('F', 215, struct s5ptvfb_user_chroma)
+#define S5PTVFB_WAITFORVSYNC _IO('F', 32)
+#define S5PTVFB_SET_VSYNC_INT _IOW('F', 216, u32)
+#define S5PTVFB_WIN_SET_ADDR _IOW('F', 219, u32)
+#define S5PTVFB_SCALING _IOW('F', 222, struct s5ptvfb_user_scaling)
+
+struct s5ptvfb_window {
+ int id;
+ struct device *dev_fb;
+ int enabled;
+ atomic_t in_use;
+ int x;
+ int y;
+ enum s5ptvfb_data_path_t path;
+ int local_channel;
+ int dma_burst;
+ unsigned int pseudo_pal[16];
+ struct s5ptvfb_alpha alpha;
+ struct s5ptvfb_chroma chroma;
+ int (*suspend_fifo)(void);
+ int (*resume_fifo)(void);
+};
+
+struct s5ptvfb_lcd_timing {
+ int h_fp;
+ int h_bp;
+ int h_sw;
+ int v_fp;
+ int v_fpe;
+ int v_bp;
+ int v_bpe;
+ int v_sw;
+};
+
+struct s5ptvfb_lcd_polarity {
+ int rise_vclk;
+ int inv_hsync;
+ int inv_vsync;
+ int inv_vden;
+};
+
+struct s5ptvfb_lcd {
+ int width;
+ int height;
+ int bpp;
+ int freq;
+ struct s5ptvfb_lcd_timing timing;
+ struct s5ptvfb_lcd_polarity polarity;
+
+ void (*init_ldi)(void);
+};
+
+static struct mutex fb_lock;
+
+static struct fb_info *fb[S5PTV_FB_CNT];
+static struct s5ptvfb_lcd lcd = {
+ .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,
+ },
+};
+
+static int s5p_tvout_fb_wait_for_vsync(void)
+{
+ sleep_on_timeout(&s5ptv_wq, HZ / 10);
+
+ return 0;
+}
+
+static inline unsigned int s5p_tvout_fb_chan_to_field(unsigned int chan,
+ struct fb_bitfield bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf.length;
+
+ return chan << bf.offset;
+}
+
+static int s5p_tvout_fb_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 = NONE_BLENDING;
+
+ return 0;
+}
+
+static int s5p_tvout_fb_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 s5p_tvout_fb_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 |= s5p_tvout_fb_chan_to_field(red, fb->var.red);
+ val |= s5p_tvout_fb_chan_to_field(green, fb->var.green);
+ val |= s5p_tvout_fb_chan_to_field(blue, fb->var.blue);
+ val |= s5p_tvout_fb_chan_to_field(transp, fb->var.transp);
+
+ pal[regno] = val;
+ }
+
+ return 0;
+}
+
+static int s5p_tvout_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
+{
+ dma_addr_t start_addr;
+ enum s5p_mixer_layer layer;
+ struct fb_fix_screeninfo *fix = &fb->fix;
+
+ if (var->yoffset + var->yres > var->yres_virtual) {
+ tvout_err("invalid y offset value\n");
+ return -1;
+ }
+
+ fb->var.yoffset = var->yoffset;
+
+ switch (fb->node) {
+ case S5PTV_FB_LAYER0_MINOR:
+ layer = MIXER_GPR0_LAYER;
+ break;
+
+ case S5PTV_FB_LAYER1_MINOR:
+ layer = MIXER_GPR1_LAYER;
+ break;
+
+ default:
+ tvout_err("invalid layer\n");
+ return -1;
+ }
+
+ start_addr = fix->smem_start + (var->xres_virtual * (var->bits_per_pixel / 8) * var->yoffset);
+
+ s5p_mixer_ctrl_set_buffer_address(layer, start_addr);
+
+ return 0;
+}
+
+static int s5p_tvout_fb_blank(int blank_mode, struct fb_info *fb)
+{
+ enum s5p_mixer_layer layer = MIXER_GPR0_LAYER;
+
+ tvout_dbg("change blank mode\n");
+
+ switch (fb->node) {
+ case S5PTV_FB_LAYER0_MINOR:
+ layer = MIXER_GPR0_LAYER;
+ break;
+
+ case S5PTV_FB_LAYER1_MINOR:
+ layer = MIXER_GPR1_LAYER;
+ break;
+
+ default:
+ tvout_err("not supported layer\n");
+ return -1;
+ }
+
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ if (fb->fix.smem_start)
+ s5p_mixer_ctrl_enable_layer(layer);
+ else
+ tvout_dbg("[fb%d] no alloc memory for unblank\n", fb->node);
+ break;
+
+ case FB_BLANK_POWERDOWN:
+ s5p_mixer_ctrl_disable_layer(layer);
+ break;
+
+ default:
+ tvout_err("not supported blank mode\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int s5p_tvout_fb_set_par(struct fb_info *fb)
+{
+ u32 bpp, trans_len;
+ u32 src_x, src_y, w, h;
+ struct s5ptvfb_window *win = fb->par;
+ enum s5p_mixer_layer layer = MIXER_GPR0_LAYER;
+
+ tvout_dbg("[fb%d] set_par\n", win->id);
+
+ if (!fb->fix.smem_start)
+ printk(KERN_INFO " The frame buffer is allocated here\n");
+
+ bpp = fb->var.bits_per_pixel;
+ trans_len = fb->var.transp.length;
+ w = fb->var.xres;
+ h = fb->var.yres;
+ src_x = fb->var.xoffset;
+ src_y = fb->var.yoffset;
+
+ switch (fb->node) {
+ case S5PTV_FB_LAYER0_MINOR:
+ layer = MIXER_GPR0_LAYER;
+ break;
+
+ case S5PTV_FB_LAYER1_MINOR:
+ layer = MIXER_GPR1_LAYER;
+ break;
+
+ default:
+ break;
+ }
+
+ s5p_mixer_ctrl_init_grp_layer(layer);
+ s5p_mixer_ctrl_set_pixel_format(layer, bpp, trans_len);
+ s5p_mixer_ctrl_set_src_win_pos(layer, src_x, src_y, w, h);
+ s5p_mixer_ctrl_set_alpha_blending(layer, win->alpha.mode,
+ win->alpha.value);
+ return 0;
+}
+
+static int s5p_tvout_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
+{
+ struct fb_fix_screeninfo *fix = &fb->fix;
+ struct s5ptvfb_window *win = fb->par;
+
+ tvout_dbg("[fb%d] check_var\n", win->id);
+
+ if (var->bits_per_pixel != 16 && var->bits_per_pixel != 24 &&
+ var->bits_per_pixel != 32) {
+ tvout_err("invalid bits per pixel\n");
+ return -1;
+ }
+
+ 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;
+
+ s5p_tvout_fb_set_bitfield(var);
+ s5p_tvout_fb_set_alpha_info(var, win);
+
+ return 0;
+}
+
+static int s5p_tvout_fb_release(struct fb_info *fb, int user)
+{
+ struct s5ptvfb_window *win = fb->par;
+
+ atomic_dec(&win->in_use);
+
+ return 0;
+}
+
+static int s5p_tvout_fb_ioctl(struct fb_info *fb, unsigned int cmd,
+ unsigned long arg)
+{
+ dma_addr_t start_addr;
+ enum s5p_mixer_layer layer;
+ struct fb_var_screeninfo *var = &fb->var;
+ struct s5ptvfb_window *win = fb->par;
+ 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;
+ struct s5ptvfb_user_scaling user_scaling;
+ int vsync;
+ } p;
+
+ switch (fb->node) {
+ case S5PTV_FB_LAYER0_MINOR:
+ layer = MIXER_GPR0_LAYER;
+ break;
+ case S5PTV_FB_LAYER1_MINOR:
+ layer = MIXER_GPR1_LAYER;
+ break;
+ default:
+ printk(KERN_ERR "[Error] invalid layer\n");
+ return -1;
+ }
+
+ switch (cmd) {
+ case S5PTVFB_WIN_POSITION:
+ if (copy_from_user(&p.user_window, (struct s5ptvfb_user_window __user *) arg, sizeof(p.user_window))) {
+ ret = -EFAULT;
+ } else {
+ s5p_mixer_ctrl_set_dst_win_pos(layer,
+ p.user_window.x, p.user_window.y,
+ var->xres, var->yres);
+ }
+ 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 = LAYER_BLENDING;
+ win->alpha.value = p.user_alpha.alpha;
+ s5p_mixer_ctrl_set_alpha_blending(layer,
+ win->alpha.mode, win->alpha.value);
+ }
+ 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);
+
+ s5p_mixer_ctrl_set_chroma_key(layer, win->chroma);
+ }
+ break;
+
+ case S5PTVFB_SET_VSYNC_INT:
+ s5p_mixer_ctrl_set_vsync_interrupt((int)argp);
+ break;
+
+ case S5PTVFB_WAITFORVSYNC:
+ s5p_tvout_fb_wait_for_vsync();
+ break;
+
+ case S5PTVFB_WIN_SET_ADDR:
+ fb->fix.smem_start = (unsigned long)argp;
+ start_addr = fb->fix.smem_start + (var->xres_virtual * (var->bits_per_pixel / 8) * var->yoffset);
+
+ s5p_mixer_ctrl_set_buffer_address(layer, start_addr);
+ break;
+
+ case S5PTVFB_SCALING:
+ if (copy_from_user(&p.user_scaling, (struct s5ptvfb_user_scaling __user *) arg, sizeof(p.user_scaling)))
+ ret = -EFAULT;
+ else
+ s5p_mixer_ctrl_scaling(layer, p.user_scaling);
+ break;
+ }
+
+ return 0;
+}
+
+static int s5p_tvout_fb_open(struct fb_info *fb, int user)
+{
+ struct s5ptvfb_window *win = fb->par;
+ int ret = 0;
+
+ mutex_lock(&fb_lock);
+
+ if (atomic_read(&win->in_use)) {
+ tvout_dbg("do not allow multiple open for tvout framebuffer\n");
+ ret = -EBUSY;
+ } else
+ atomic_inc(&win->in_use);
+
+ mutex_unlock(&fb_lock);
+
+ return ret;
+}
+
+struct fb_ops s5ptvfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_check_var = s5p_tvout_fb_check_var,
+ .fb_set_par = s5p_tvout_fb_set_par,
+ .fb_blank = s5p_tvout_fb_blank,
+ .fb_pan_display = s5p_tvout_fb_pan_display,
+ .fb_setcolreg = s5p_tvout_fb_setcolreg,
+ .fb_ioctl = s5p_tvout_fb_ioctl,
+ .fb_open = s5p_tvout_fb_open,
+ .fb_release = s5p_tvout_fb_release,
+};
+
+static int s5p_tvout_fb_init_fbinfo(int id, struct device *dev_fb)
+{
+ struct fb_fix_screeninfo *fix = &fb[FB_INDEX(id)]->fix;
+ struct fb_var_screeninfo *var = &fb[FB_INDEX(id)]->var;
+ struct s5ptvfb_window *win = fb[FB_INDEX(id)]->par;
+ struct s5ptvfb_alpha *alpha = &win->alpha;
+
+ memset(win, 0, sizeof(struct s5ptvfb_window));
+
+ platform_set_drvdata(to_platform_device(dev_fb), fb[FB_INDEX(id)]);
+
+ strcpy(fix->id, S5PTVFB_NAME);
+
+ /* fimd specific */
+ win->id = id;
+ win->path = DATA_PATH_DMA;
+ win->dma_burst = 16;
+ win->dev_fb = dev_fb;
+ alpha->mode = LAYER_BLENDING;
+ alpha->value = 0xff;
+
+ /* fbinfo */
+ fb[FB_INDEX(id)]->fbops = &s5ptvfb_ops;
+ fb[FB_INDEX(id)]->flags = FBINFO_FLAG_DEFAULT;
+ fb[FB_INDEX(id)]->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;
+
+ fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ fix->smem_len = fix->line_length * var->yres_virtual;
+
+ var->nonstd = 0;
+ var->activate = FB_ACTIVATE_NOW;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->hsync_len = lcd.timing.h_sw;
+ var->vsync_len = lcd.timing.v_sw;
+ var->left_margin = lcd.timing.h_fp;
+ var->right_margin = lcd.timing.h_bp;
+ var->upper_margin = lcd.timing.v_fp;
+ var->lower_margin = lcd.timing.v_bp;
+
+ 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);
+
+ tvout_dbg("pixclock: %d\n", var->pixclock);
+
+ s5p_tvout_fb_set_bitfield(var);
+ s5p_tvout_fb_set_alpha_info(var, win);
+
+ mutex_init(&fb_lock);
+
+ return 0;
+}
+
+int s5p_tvout_fb_alloc_framebuffer(struct device *dev_fb)
+{
+ int ret, i;
+
+ /* alloc for each framebuffer */
+ for (i = 0; i < S5PTV_FB_CNT; i++) {
+ fb[i] = framebuffer_alloc(sizeof(struct s5ptvfb_window), dev_fb);
+ if (!fb[i]) {
+ tvout_err("not enough memory\n");
+ ret = -1;
+ goto err_alloc_fb;
+ }
+
+ ret = s5p_tvout_fb_init_fbinfo(i + S5PTV_FB_LAYER0_MINOR, dev_fb);
+ if (ret) {
+ tvout_err("fail to allocate memory for tv fb\n");
+ ret = -1;
+ goto err_alloc_fb;
+ }
+ }
+
+ return 0;
+
+err_alloc_fb:
+ for (i = 0; i < S5PTV_FB_CNT; i++) {
+ if (fb[i])
+ framebuffer_release(fb[i]);
+ }
+
+ return ret;
+}
+
+int s5p_tvout_fb_register_framebuffer(struct device *dev_fb)
+{
+ int ret, i = 0;
+
+ do {
+ ret = register_framebuffer(fb[0]);
+ if (ret) {
+ tvout_err("fail to register framebuffer device\n");
+ return -1;
+ }
+ } while (fb[0]->node < S5PTV_FB_LAYER0_MINOR);
+
+ for (i = 1; i < S5PTV_FB_CNT; i++) {
+ ret = register_framebuffer(fb[i]);
+ if (ret) {
+ tvout_err("fail to register framebuffer device\n");
+ return -1;
+ }
+ }
+
+ for (i = 0; i < S5PTV_FB_CNT; i++)
+ tvout_dbg("fb[%d] = %d\n", i, fb[i]->node);
+
+ for (i = 0; i < S5PTV_FB_CNT; i++) {
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+ s5p_tvout_fb_check_var(&fb[i]->var, fb[i]);
+ s5p_tvout_fb_set_par(fb[i]);
+#endif
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Frame Buffer 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_FB_H_
+#define __S5P_TVOUT_FB_H_ __FILE__
+
+#include <linux/fb.h>
+
+extern int s5p_tvout_fb_alloc_framebuffer(struct device *dev_fb);
+extern int s5p_tvout_fb_register_framebuffer(struct device *dev_fb);
+
+#endif /* __S5P_TVOUT_FB_H_ */
new file mode 100644
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Control class for Samsung S5P Video Processor
+ *
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include "hw_if/hw_if.h"
+#include "s5p_tvout_ctrl.h"
+
+#define INTERLACED 0
+#define PROGRESSIVE 1
+
+struct s5p_vp_ctrl_op_mode {
+ bool ipc;
+ bool line_skip;
+ bool auto_toggling;
+};
+
+struct s5p_vp_ctrl_bc_line_eq {
+ enum s5p_vp_line_eq eq_num;
+ u32 intc;
+ u32 slope;
+};
+
+struct s5p_vp_ctrl_rect {
+ u32 x;
+ u32 y;
+ u32 w;
+ u32 h;
+};
+
+struct s5p_vp_ctrl_plane {
+ u32 top_y_addr;
+ u32 top_c_addr;
+ u32 w;
+ u32 h;
+
+ enum s5p_vp_src_color color_t;
+ enum s5p_vp_field field_id;
+ enum s5p_vp_mem_type mem_type;
+ enum s5p_vp_mem_mode mem_mode;
+};
+
+struct s5p_vp_ctrl_pp_param {
+ bool bypass;
+
+ bool csc_en;
+ enum s5p_vp_csc_type csc_t;
+ bool csc_default_coef;
+ bool csc_sub_y_offset_en;
+
+ u32 saturation;
+ u8 contrast;
+ bool brightness;
+ u32 bright_offset;
+ struct s5p_vp_ctrl_bc_line_eq bc_line_eq[8];
+
+ /* sharpness */
+ u32 th_hnoise;
+ enum s5p_vp_sharpness_control sharpness;
+
+
+ bool default_poly_filter;
+
+ enum s5p_vp_chroma_expansion chroma_exp;
+};
+
+struct s5p_vp_ctrl_mixer_param {
+ bool blend;
+ u32 alpha;
+ u32 prio;
+};
+
+struct s5p_vp_ctrl_private_data {
+ struct s5p_vp_ctrl_plane src_plane;
+ struct s5p_vp_ctrl_rect src_win;
+
+ struct s5p_vp_ctrl_rect dst_win;
+ struct s5p_vp_ctrl_op_mode op_mode;
+
+ struct s5p_vp_ctrl_pp_param pp_param;
+ struct s5p_vp_ctrl_mixer_param mixer_param;
+
+ bool running;
+
+ struct reg_mem_info reg_mem;
+
+ struct s5p_tvout_clk_info clk;
+ char *pow_name;
+};
+
+static struct s5p_vp_ctrl_private_data s5p_vp_ctrl_private = {
+ .reg_mem = {
+ .name = "s5p-vp",
+ .res = NULL,
+ .base = NULL
+ },
+
+ .clk = {
+ .name = "vp",
+ .ptr = NULL
+ },
+
+ .pow_name = "vp_pd",
+
+ .src_plane = {
+ .field_id = VP_TOP_FIELD,
+ },
+
+ .pp_param = {
+ .default_poly_filter = true,
+ .bypass = false,
+
+ .saturation = 0x80,
+ .brightness = 0x00,
+ .bright_offset = 0x00,
+ .contrast = 0x80,
+
+ .th_hnoise = 0,
+ .sharpness = VP_SHARPNESS_NO,
+
+ .chroma_exp = 0,
+
+ .csc_en = false,
+ .csc_default_coef = true,
+ .csc_sub_y_offset_en = false,
+ },
+
+ .running = false
+};
+
+static u8 s5p_vp_ctrl_get_src_scan_mode(void)
+{
+ struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
+ u8 ret = PROGRESSIVE;
+
+ if (src_plane->color_t == VP_SRC_COLOR_NV12IW ||
+ src_plane->color_t == VP_SRC_COLOR_TILE_NV12IW ||
+ src_plane->color_t == VP_SRC_COLOR_NV21IW ||
+ src_plane->color_t == VP_SRC_COLOR_TILE_NV21IW)
+ ret = INTERLACED;
+
+ return ret;
+}
+
+static u8 s5p_vp_ctrl_get_dest_scan_mode(
+ enum s5p_tvout_disp_mode display, enum s5p_tvout_o_mode out)
+{
+ u8 ret = PROGRESSIVE;
+
+ switch (out) {
+ case TVOUT_COMPOSITE:
+ ret = INTERLACED;
+ break;
+
+ case TVOUT_HDMI_RGB:
+ case TVOUT_HDMI:
+ case TVOUT_DVI:
+ if (display == TVOUT_1080I_60 || display == TVOUT_1080I_59 ||
+ display == TVOUT_1080I_50)
+ ret = INTERLACED;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void s5p_vp_ctrl_set_src_dst_win(struct s5p_vp_ctrl_rect src_win,
+ struct s5p_vp_ctrl_rect dst_win,
+ enum s5p_tvout_disp_mode disp,
+ enum s5p_tvout_o_mode out,
+ enum s5p_vp_src_color color_t,
+ bool ipc)
+{
+ src_win.y /= 2;
+ src_win.h /= 2;
+
+ if (s5p_vp_ctrl_get_dest_scan_mode(disp, out) == INTERLACED) {
+ dst_win.y /= 2;
+ dst_win.h /= 2;
+ }
+
+ s5p_vp_set_src_position(src_win.x, 0, src_win.y);
+ s5p_vp_set_dest_position(dst_win.x, dst_win.y);
+ s5p_vp_set_src_dest_size(src_win.w, src_win.h, dst_win.w, dst_win.h, ipc);
+}
+
+static int s5p_vp_ctrl_set_src_addr(u32 top_y_addr, u32 top_c_addr,
+ u32 img_w, enum s5p_vp_src_color color_t)
+{
+ if (s5p_vp_set_top_field_address(top_y_addr, top_c_addr))
+ return -1;
+
+ if (s5p_vp_ctrl_get_src_scan_mode() == INTERLACED) {
+ u32 bot_y = 0;
+ u32 bot_c = 0;
+
+ if (color_t == VP_SRC_COLOR_NV12IW || color_t == VP_SRC_COLOR_NV21IW) {
+ bot_y = top_y_addr + img_w;
+ bot_c = top_c_addr + img_w;
+ } else if (color_t == VP_SRC_COLOR_TILE_NV12IW || color_t == VP_SRC_COLOR_TILE_NV21IW) {
+ bot_y = top_y_addr + 0x40;
+ bot_c = top_c_addr + 0x40;
+ }
+
+ if (s5p_vp_set_bottom_field_address(bot_y, bot_c))
+ return -1;
+ }
+
+ return 0;
+}
+
+static void s5p_vp_ctrl_init_private(void)
+{
+ int i;
+ struct s5p_vp_ctrl_pp_param *pp_param = &s5p_vp_ctrl_private.pp_param;
+
+ for (i = 0; i < 8; i++)
+ pp_param->bc_line_eq[i].eq_num = VP_LINE_EQ_DEFAULT;
+}
+
+static int s5p_vp_ctrl_set_reg(void)
+{
+ int i;
+ int ret = 0;
+
+ enum s5p_tvout_disp_mode tv_std;
+ enum s5p_tvout_o_mode tv_if;
+
+ struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
+ struct s5p_vp_ctrl_pp_param *pp_param = &s5p_vp_ctrl_private.pp_param;
+ struct s5p_vp_ctrl_op_mode *op_mode = &s5p_vp_ctrl_private.op_mode;
+
+ s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
+
+ s5p_vp_sw_reset();
+
+ s5p_vp_set_endian(TVOUT_BIG_ENDIAN);
+
+ s5p_vp_set_op_mode(op_mode->line_skip, src_plane->mem_type,
+ src_plane->mem_mode, pp_param->chroma_exp,
+ op_mode->auto_toggling);
+
+ s5p_vp_set_field_id(src_plane->field_id);
+
+ s5p_vp_set_img_size(src_plane->w, src_plane->h);
+
+ s5p_vp_ctrl_set_src_addr(src_plane->top_y_addr, src_plane->top_c_addr,
+ src_plane->w, src_plane->color_t);
+
+ s5p_vp_ctrl_set_src_dst_win(s5p_vp_ctrl_private.src_win,
+ s5p_vp_ctrl_private.dst_win,
+ tv_std, tv_if,
+ s5p_vp_ctrl_private.src_plane.color_t,
+ op_mode->ipc);
+
+ if (pp_param->default_poly_filter)
+ s5p_vp_set_poly_filter_coef_default(s5p_vp_ctrl_private.src_win.w,
+ s5p_vp_ctrl_private.src_win.h,
+ s5p_vp_ctrl_private.dst_win.w,
+ s5p_vp_ctrl_private.dst_win.h,
+ op_mode->ipc);
+
+ s5p_vp_set_bypass_post_process(pp_param->bypass);
+ s5p_vp_set_sharpness(pp_param->th_hnoise, pp_param->sharpness);
+ s5p_vp_set_saturation(pp_param->saturation);
+ s5p_vp_set_brightness_contrast(pp_param->brightness, pp_param->contrast);
+
+ for (i = VP_LINE_EQ_0; i <= VP_LINE_EQ_7; i++) {
+ if (pp_param->bc_line_eq[i].eq_num == i)
+ ret = s5p_vp_set_brightness_contrast_control(pp_param->bc_line_eq[i].eq_num,
+ pp_param->bc_line_eq[i].intc,
+ pp_param->bc_line_eq[i].slope);
+
+ if (ret != 0)
+ return -1;
+ }
+
+ s5p_vp_set_brightness_offset(pp_param->bright_offset);
+
+ s5p_vp_set_csc_control(pp_param->csc_sub_y_offset_en, pp_param->csc_en);
+
+ if (pp_param->csc_en && pp_param->csc_default_coef) {
+ if (s5p_vp_set_csc_coef_default(pp_param->csc_t))
+ return -1;
+ }
+
+ if (s5p_vp_start())
+ return -1;
+
+ s5p_mixer_ctrl_enable_layer(MIXER_VIDEO_LAYER);
+
+ return 0;
+}
+
+static void s5p_vp_ctrl_internal_stop(void)
+{
+ s5p_vp_stop();
+
+ s5p_mixer_ctrl_disable_layer(MIXER_VIDEO_LAYER);
+}
+
+static void s5p_vp_ctrl_clock(bool on)
+{
+ if (on)
+ clk_enable(s5p_vp_ctrl_private.clk.ptr);
+ else
+ clk_disable(s5p_vp_ctrl_private.clk.ptr);
+}
+
+void s5p_vp_ctrl_set_src_plane(u32 base_y, u32 base_c, u32 width, u32 height,
+ enum s5p_vp_src_color color, enum s5p_vp_field field)
+{
+ struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
+
+ src_plane->color_t = color;
+ src_plane->field_id = field;
+
+ src_plane->top_y_addr = base_y;
+ src_plane->top_c_addr = base_c;
+
+ src_plane->w = width;
+ src_plane->h = height;
+
+ if (s5p_vp_ctrl_private.running) {
+ s5p_vp_set_img_size(width, height);
+
+ s5p_vp_set_field_id(field);
+ s5p_vp_ctrl_set_src_addr(base_y, base_c, width, color);
+
+ s5p_vp_update();
+ }
+}
+
+void s5p_vp_ctrl_set_src_win(u32 left, u32 top, u32 width, u32 height)
+{
+ struct s5p_vp_ctrl_rect *src_win = &s5p_vp_ctrl_private.src_win;
+
+ src_win->x = left;
+ src_win->y = top;
+ src_win->w = width;
+ src_win->h = height;
+
+ if (s5p_vp_ctrl_private.running) {
+ enum s5p_tvout_disp_mode tv_std;
+ enum s5p_tvout_o_mode tv_if;
+
+ s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
+
+ s5p_vp_ctrl_set_src_dst_win(*src_win,
+ s5p_vp_ctrl_private.dst_win,
+ tv_std, tv_if,
+ s5p_vp_ctrl_private.src_plane.color_t,
+ s5p_vp_ctrl_private.op_mode.ipc);
+
+ s5p_vp_update();
+ }
+}
+
+void s5p_vp_ctrl_set_dest_win(u32 left, u32 top, u32 width, u32 height)
+{
+ struct s5p_vp_ctrl_rect *dst_win = &s5p_vp_ctrl_private.dst_win;
+
+ dst_win->x = left;
+ dst_win->y = top;
+ dst_win->w = width;
+ dst_win->h = height;
+
+ if (s5p_vp_ctrl_private.running) {
+ enum s5p_tvout_disp_mode tv_std;
+ enum s5p_tvout_o_mode tv_if;
+
+ s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
+
+ s5p_vp_ctrl_set_src_dst_win(s5p_vp_ctrl_private.src_win,
+ *dst_win, tv_std, tv_if,
+ s5p_vp_ctrl_private.src_plane.color_t,
+ s5p_vp_ctrl_private.op_mode.ipc);
+
+ s5p_vp_update();
+ }
+}
+
+void s5p_vp_ctrl_set_dest_win_alpha_val(u32 alpha)
+{
+ s5p_vp_ctrl_private.mixer_param.alpha = alpha;
+
+ if (s5p_vp_ctrl_private.running)
+ s5p_mixer_ctrl_set_alpha(MIXER_VIDEO_LAYER, alpha);
+}
+
+void s5p_vp_ctrl_set_dest_win_blend(bool enable)
+{
+ s5p_vp_ctrl_private.mixer_param.blend = enable;
+
+ if (s5p_vp_ctrl_private.running)
+ s5p_mixer_ctrl_set_blend_mode(MIXER_VIDEO_LAYER, LAYER_BLENDING);
+}
+
+void s5p_vp_ctrl_set_dest_win_priority(u32 prio)
+{
+ s5p_vp_ctrl_private.mixer_param.prio = prio;
+
+ if (s5p_vp_ctrl_private.running)
+ s5p_mixer_ctrl_set_priority(MIXER_VIDEO_LAYER, (int)prio);
+}
+
+void s5p_vp_ctrl_stop(void)
+{
+ if (s5p_vp_ctrl_private.running) {
+ s5p_vp_ctrl_internal_stop();
+ s5p_vp_ctrl_clock(0);
+
+ s5p_vp_ctrl_private.running = false;
+ }
+}
+
+int s5p_vp_ctrl_start(void)
+{
+ struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
+ enum s5p_tvout_disp_mode disp;
+ enum s5p_tvout_o_mode out;
+
+ /* 0 for interlaced, 1 for progressive */
+ bool i_mode, o_mode;
+
+ s5p_tvif_ctrl_get_std_if(&disp, &out);
+
+ switch (disp) {
+ case TVOUT_480P_60_16_9:
+ case TVOUT_480P_60_4_3:
+ case TVOUT_576P_50_16_9:
+ case TVOUT_576P_50_4_3:
+ case TVOUT_480P_59:
+ s5p_vp_ctrl_private.pp_param.csc_t = VP_CSC_SD_HD;
+ break;
+
+ case TVOUT_1080I_50:
+ case TVOUT_1080I_60:
+ case TVOUT_1080P_50:
+ case TVOUT_1080P_30:
+ case TVOUT_1080P_60:
+ case TVOUT_720P_59:
+ case TVOUT_1080I_59:
+ case TVOUT_1080P_59:
+ case TVOUT_720P_50:
+ case TVOUT_720P_60:
+ s5p_vp_ctrl_private.pp_param.csc_t = VP_CSC_HD_SD;
+ break;
+
+ default:
+ break;
+ }
+
+ i_mode = s5p_vp_ctrl_get_src_scan_mode();
+ o_mode = s5p_vp_ctrl_get_dest_scan_mode(disp, out);
+
+ /* check o_mode */
+ if (i_mode == INTERLACED) {
+ if (o_mode == INTERLACED) {
+ /* i to i : line skip 1, ipc 0, auto toggle 0 */
+ s5p_vp_ctrl_private.op_mode.line_skip = true;
+ s5p_vp_ctrl_private.op_mode.ipc = false;
+ s5p_vp_ctrl_private.op_mode.auto_toggling = false;
+ } else {
+ /* i to p : line skip 1, ipc 1, auto toggle 0 */
+ s5p_vp_ctrl_private.op_mode.line_skip = true;
+ s5p_vp_ctrl_private.op_mode.ipc = true;
+ s5p_vp_ctrl_private.op_mode.auto_toggling = false;
+ }
+ } else {
+ if (o_mode == INTERLACED) {
+ /* p to i : line skip 1, ipc 0, auto toggle 0 */
+ s5p_vp_ctrl_private.op_mode.line_skip = true;
+ s5p_vp_ctrl_private.op_mode.ipc = false;
+ s5p_vp_ctrl_private.op_mode.auto_toggling = false;
+ } else {
+ /* p to p : line skip 0, ipc 0, auto toggle 0 */
+ s5p_vp_ctrl_private.op_mode.line_skip = false;
+ s5p_vp_ctrl_private.op_mode.ipc = false;
+ s5p_vp_ctrl_private.op_mode.auto_toggling = false;
+ }
+ }
+
+ src_plane->mem_type = ((src_plane->color_t == VP_SRC_COLOR_NV12) ||
+ (src_plane->color_t == VP_SRC_COLOR_NV12IW) ||
+ (src_plane->color_t == VP_SRC_COLOR_TILE_NV12) ||
+ (src_plane->color_t == VP_SRC_COLOR_TILE_NV12IW)) ?
+ VP_YUV420_NV12 : VP_YUV420_NV21;
+ src_plane->mem_mode = ((src_plane->color_t == VP_SRC_COLOR_NV12) ||
+ (src_plane->color_t == VP_SRC_COLOR_NV12IW) ||
+ (src_plane->color_t == VP_SRC_COLOR_NV21) ||
+ (src_plane->color_t == VP_SRC_COLOR_NV21IW)) ?
+ VP_LINEAR_MODE : VP_2D_TILE_MODE;
+
+ if (s5p_vp_ctrl_private.running) {
+ s5p_vp_ctrl_internal_stop();
+ } else {
+ s5p_vp_ctrl_clock(1);
+
+ s5p_vp_ctrl_private.running = true;
+ }
+
+ s5p_vp_ctrl_set_reg();
+
+ return 0;
+}
+
+int s5p_vp_ctrl_constructor(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = s5p_tvout_map_resource_mem(pdev, s5p_vp_ctrl_private.reg_mem.name,
+ &(s5p_vp_ctrl_private.reg_mem.base),
+ &(s5p_vp_ctrl_private.reg_mem.res));
+
+ if (ret)
+ goto err_on_res;
+
+ s5p_vp_ctrl_private.clk.ptr = clk_get(&pdev->dev, s5p_vp_ctrl_private.clk.name);
+
+ if (IS_ERR(s5p_vp_ctrl_private.clk.ptr)) {
+ tvout_err("Failed to find clock %s\n", s5p_vp_ctrl_private.clk.name);
+ ret = -ENOENT;
+ goto err_on_clk;
+ }
+
+ s5p_vp_init(s5p_vp_ctrl_private.reg_mem.base);
+ s5p_vp_ctrl_init_private();
+
+ return 0;
+
+err_on_clk:
+ iounmap(s5p_vp_ctrl_private.reg_mem.base);
+ release_resource(s5p_vp_ctrl_private.reg_mem.res);
+ kfree(s5p_vp_ctrl_private.reg_mem.res);
+
+err_on_res:
+ return ret;
+}
+
+void s5p_vp_ctrl_destructor(void)
+{
+ if (s5p_vp_ctrl_private.reg_mem.base)
+ iounmap(s5p_vp_ctrl_private.reg_mem.base);
+
+ if (s5p_vp_ctrl_private.reg_mem.res) {
+ release_resource(s5p_vp_ctrl_private.reg_mem.res);
+ kfree(s5p_vp_ctrl_private.reg_mem.res);
+ }
+
+ if (s5p_vp_ctrl_private.clk.ptr) {
+ if (s5p_vp_ctrl_private.running)
+ clk_disable(s5p_vp_ctrl_private.clk.ptr);
+ clk_put(s5p_vp_ctrl_private.clk.ptr);
+ }
+}
+
+void s5p_vp_ctrl_suspend(void)
+{
+ if (s5p_vp_ctrl_private.running) {
+ s5p_vp_stop();
+ s5p_vp_ctrl_clock(0);
+ }
+}
+
+void s5p_vp_ctrl_resume(void)
+{
+ if (s5p_vp_ctrl_private.running) {
+ s5p_vp_ctrl_clock(1);
+ s5p_vp_ctrl_set_reg();
+ }
+}