diff options
Diffstat (limited to 'recipes/linux/linux-2.6.24/smartq5/mer/0006-tv-encoder.patch')
-rw-r--r-- | recipes/linux/linux-2.6.24/smartq5/mer/0006-tv-encoder.patch | 2624 |
1 files changed, 2624 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.24/smartq5/mer/0006-tv-encoder.patch b/recipes/linux/linux-2.6.24/smartq5/mer/0006-tv-encoder.patch new file mode 100644 index 0000000000..624b9c317e --- /dev/null +++ b/recipes/linux/linux-2.6.24/smartq5/mer/0006-tv-encoder.patch @@ -0,0 +1,2624 @@ +From 002a069a3a52b8a2286e8c559a2d3d9a0a954501 Mon Sep 17 00:00:00 2001 +From: Carsten V. Munk <carsten.munk@gmail.com> +Date: Thu, 6 Aug 2009 11:32:58 +0000 +Subject: [PATCH 06/13] tv encoder + +--- + drivers/media/video/Kconfig | 16 + + drivers/media/video/samsung/Makefile | 2 + + drivers/media/video/samsung/s3c-tvenc.c | 1479 ++++++++++++++++++++++++++++ + drivers/media/video/samsung/s3c-tvenc.h | 165 +++ + drivers/media/video/samsung/s3c-tvscaler.c | 802 +++++++++++++++ + drivers/media/video/samsung/s3c-tvscaler.h | 96 ++ + 6 files changed, 2560 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/samsung/s3c-tvenc.c + create mode 100644 drivers/media/video/samsung/s3c-tvenc.h + create mode 100644 drivers/media/video/samsung/s3c-tvscaler.c + create mode 100644 drivers/media/video/samsung/s3c-tvscaler.h + +diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig +index 6506263..8797184 100644 +--- a/drivers/media/video/Kconfig ++++ b/drivers/media/video/Kconfig +@@ -545,6 +545,22 @@ config VIDEO_ADV7180 + + endchoice + ++config VIDEO_SAMSUNG_TVENC ++ tristate "Samsung TV encoder" ++ depends on I2C && VIDEO_V4L2 ++ help ++ Samsung encoder ++ ++ To compile this driver as a module, choose M here ++ ++config VIDEO_SAMSUNG_TVSCALER ++ tristate "Samsung TV scaler" ++ depends on I2C && VIDEO_V4L2 ++ help ++ Samsung TV scaler ++ ++ To compile this driver as a module, choose M here ++ + + config VIDEO_VINO + tristate "SGI Vino Video For Linux (EXPERIMENTAL)" +diff --git a/drivers/media/video/samsung/Makefile b/drivers/media/video/samsung/Makefile +index 885512c..ede0269 100644 +--- a/drivers/media/video/samsung/Makefile ++++ b/drivers/media/video/samsung/Makefile +@@ -7,3 +7,5 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5K4BA) += 4xa_sensor.o + obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o + obj-$(CONFIG_VIDEO_APTINA_MT9P012) += mt9p012.o + ++obj-$(CONFIG_VIDEO_SAMSUNG_TVENC) += s3c_tvenc.o ++obj-$(CONFIG_VIDEO_SAMSUNG_TVSCALER) += s3c_tvenc.o +diff --git a/drivers/media/video/samsung/s3c-tvenc.c b/drivers/media/video/samsung/s3c-tvenc.c +new file mode 100644 +index 0000000..11dfd37 +--- /dev/null ++++ b/drivers/media/video/samsung/s3c-tvenc.c +@@ -0,0 +1,1479 @@ ++ ++/* ++ * linux/drivers/tvenc/s3c-tvenc.c ++ * ++ * Revision 1.0 ++ * ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ * S3C TV Encoder driver ++ * ++ */ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/types.h> ++#include <linux/timer.h> ++#include <linux/kernel.h> ++#include <linux/fs.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <asm/uaccess.h> ++#include <linux/errno.h> /* error codes */ ++#include <asm/div64.h> ++#include <linux/mm.h> ++#include <linux/tty.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/hardware.h> ++#include <asm/uaccess.h> ++#include <asm/arch/map.h> ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,16) ++#include <linux/config.h> ++#include <asm/arch/registers.h> ++#include <linux/videodev2.h> ++#else ++#include <asm/arch/regs-tvenc.h> ++#include <asm/arch/regs-lcd.h> ++#include <media/v4l2-common.h> ++#endif ++ ++#include "s3c-tvenc.h" ++ ++#define PFX "s3c_tvenc" ++ ++static struct clk *tvenc_clock; ++static struct clk *h_clk; ++static int s3c_tvenc_irq = NO_IRQ; ++static struct resource *s3c_tvenc_mem; ++static void __iomem *base; ++static wait_queue_head_t waitq; ++static tv_out_params_t tv_param = {0,}; ++ ++/* Backup SFR value */ ++static u32 backup_reg[2]; ++ ++ ++/* Structure that declares the access functions*/ ++ ++static void s3c_tvenc_switch(tv_enc_switch_t sw) ++{ ++ if(sw == OFF) { ++ __raw_writel(__raw_readl(base + S3C_TVCTRL) ++ &~ S3C_TVCTRL_ON, base + S3C_TVCTRL); ++ } else if(sw == ON) { ++ __raw_writel(__raw_readl(base + S3C_TVCTRL) ++ | S3C_TVCTRL_ON, base + S3C_TVCTRL); ++ } else ++ printk("Error func:%s line:%d\n", __FUNCTION__, __LINE__); ++} ++ ++static void s3c_tvenc_set_image_size(u32 width, u32 height) ++{ ++ __raw_writel(IIS_WIDTH(width)| IIS_HEIGHT(height), ++ base + S3C_INIMAGESIZE); ++} ++ ++#if 0 ++static void s3c_tvenc_enable_macrovision(tv_standard_t tvmode, macro_pattern_t pattern) ++{ ++ switch(pattern) { ++ case AGC4L : ++ break; ++ case AGC2L : ++ break; ++ case N01 : ++ break; ++ case N02 : ++ break; ++ case P01 : ++ break; ++ case P02 : ++ break; ++ default : ++ break; ++ } ++} ++ ++static void s3c_tvenc_disable_macrovision(void) ++{ ++ __raw_writel(__raw_readl(base + S3C_MACROVISION0) ++ &~0xff, base + S3C_MACROVISION0); ++} ++#endif ++ ++static void s3c_tvenc_set_tv_mode(tv_standard_t mode, tv_conn_type_t out) ++{ ++ u32 signal_type = 0, output_type = 0; ++ ++ switch(mode) { ++ case PAL_N : ++ __raw_writel(VBP_VEFBPD_PAL|VBP_VOFBPD_PAL, ++ base + S3C_VBPORCH); ++ __raw_writel(HBP_HSPW_PAL|HBP_HBPD_PAL, ++ base + S3C_HBPORCH); ++ __raw_writel(HEO_DTO_PAL|HEO_HEOV_PAL, ++ base + S3C_HENHOFFSET); ++ __raw_writel(EPC_PED_ON, ++ base + S3C_PEDCTRL); ++ __raw_writel(YFB_YBW_26|YFB_CBW_06, ++ base + S3C_YCFILTERBW); ++ __raw_writel(SSC_HSYNC_PAL, ++ base + S3C_SYNCSIZECTRL); ++ __raw_writel(BSC_BEND_PAL|BSC_BSTART_PAL, ++ base + S3C_BURSTCTRL); ++ __raw_writel(MBS_BSTART_PAL, ++ base + S3C_MACROBURSTCTRL); ++ __raw_writel(AVP_AVEND_PAL|AVP_AVSTART_PAL, ++ base + S3C_ACTVIDPOCTRL); ++ break; ++ case PAL_NC : ++ case PAL_BGHID : ++ __raw_writel(VBP_VEFBPD_PAL|VBP_VOFBPD_PAL, ++ base + S3C_VBPORCH); ++ __raw_writel(HBP_HSPW_PAL|HBP_HBPD_PAL, ++ base + S3C_HBPORCH); ++ __raw_writel(HEO_DTO_PAL|HEO_HEOV_PAL, ++ base + S3C_HENHOFFSET); ++ __raw_writel(EPC_PED_OFF, ++ base + S3C_PEDCTRL); ++ __raw_writel(YFB_YBW_26|YFB_CBW_06, ++ base + S3C_YCFILTERBW); ++ __raw_writel(SSC_HSYNC_PAL, ++ base + S3C_SYNCSIZECTRL); ++ __raw_writel(BSC_BEND_PAL|BSC_BSTART_PAL, ++ base + S3C_BURSTCTRL); ++ __raw_writel(MBS_BSTART_PAL, ++ base + S3C_MACROBURSTCTRL); ++ __raw_writel(AVP_AVEND_PAL|AVP_AVSTART_PAL, ++ base + S3C_ACTVIDPOCTRL); ++ break; ++ case NTSC_443 : ++ __raw_writel(VBP_VEFBPD_NTSC|VBP_VOFBPD_NTSC, ++ base + S3C_VBPORCH); ++ __raw_writel(HBP_HSPW_NTSC|HBP_HBPD_NTSC, ++ base + S3C_HBPORCH); ++ __raw_writel(HEO_DTO_NTSC|HEO_HEOV_NTSC, ++ base + S3C_HENHOFFSET); ++ __raw_writel(EPC_PED_ON, ++ base + S3C_PEDCTRL); ++ __raw_writel(YFB_YBW_26|YFB_CBW_06, ++ base + S3C_YCFILTERBW); ++ __raw_writel(SSC_HSYNC_NTSC, ++ base + S3C_SYNCSIZECTRL); ++ __raw_writel(BSC_BEND_NTSC|BSC_BSTART_NTSC, ++ base + S3C_BURSTCTRL); ++ __raw_writel(MBS_BSTART_NTSC, ++ base + S3C_MACROBURSTCTRL); ++ __raw_writel(AVP_AVEND_NTSC|AVP_AVSTART_NTSC, ++ base + S3C_ACTVIDPOCTRL); ++ break; ++ case NTSC_J : ++ __raw_writel(VBP_VEFBPD_NTSC|VBP_VOFBPD_NTSC, ++ base + S3C_VBPORCH); ++ __raw_writel(HBP_HSPW_NTSC|HBP_HBPD_NTSC, ++ base + S3C_HBPORCH); ++ __raw_writel(HEO_DTO_NTSC|HEO_HEOV_NTSC, ++ base + S3C_HENHOFFSET); ++ __raw_writel(EPC_PED_OFF, ++ base + S3C_PEDCTRL); ++ __raw_writel(YFB_YBW_21|YFB_CBW_06, ++ base + S3C_YCFILTERBW); ++ __raw_writel(SSC_HSYNC_NTSC, ++ base + S3C_SYNCSIZECTRL); ++ __raw_writel(BSC_BEND_NTSC|BSC_BSTART_NTSC, ++ base + S3C_BURSTCTRL); ++ __raw_writel(MBS_BSTART_NTSC, ++ base + S3C_MACROBURSTCTRL); ++ __raw_writel(AVP_AVEND_NTSC|AVP_AVSTART_NTSC, ++ base + S3C_ACTVIDPOCTRL); ++ break; ++ case PAL_M : ++ case NTSC_M : ++ default : ++ __raw_writel(VBP_VEFBPD_NTSC|VBP_VOFBPD_NTSC, ++ base + S3C_VBPORCH); ++ __raw_writel(HBP_HSPW_NTSC|HBP_HBPD_NTSC, ++ base + S3C_HBPORCH); ++ __raw_writel(HEO_DTO_NTSC|HEO_HEOV_NTSC, ++ base + S3C_HENHOFFSET); ++ __raw_writel(EPC_PED_ON, ++ base + S3C_PEDCTRL); ++ __raw_writel(YFB_YBW_21|YFB_CBW_06, ++ base + S3C_YCFILTERBW); ++ __raw_writel(SSC_HSYNC_NTSC, ++ base + S3C_SYNCSIZECTRL); ++ __raw_writel(BSC_BEND_NTSC|BSC_BSTART_NTSC, ++ base + S3C_BURSTCTRL); ++ __raw_writel(MBS_BSTART_NTSC, ++ base + S3C_MACROBURSTCTRL); ++ __raw_writel(AVP_AVEND_NTSC|AVP_AVSTART_NTSC, ++ base + S3C_ACTVIDPOCTRL); ++ break; ++ } ++ ++ if(out == S_VIDEO) { ++ __raw_writel(YFB_YBW_60|YFB_CBW_06, ++ base + S3C_YCFILTERBW); ++ output_type = S3C_TVCTRL_OUTTYPE_S; ++ } else ++ output_type = S3C_TVCTRL_OUTTYPE_C; ++ ++ switch(mode) { ++ case NTSC_M : ++ signal_type = S3C_TVCTRL_OUTFMT_NTSC_M; ++ break; ++ case NTSC_J : ++ signal_type = S3C_TVCTRL_OUTFMT_NTSC_J; ++ break; ++ case PAL_BGHID : ++ signal_type = S3C_TVCTRL_OUTFMT_PAL_BDG; ++ break; ++ case PAL_M : ++ signal_type = S3C_TVCTRL_OUTFMT_PAL_M; ++ break; ++ case PAL_NC : ++ signal_type = S3C_TVCTRL_OUTFMT_PAL_NC; ++ break; ++ default: ++ printk("s3c_tvenc_set_tv_mode : No matching signal_type!\n"); ++ break; ++ } ++ ++ __raw_writel((__raw_readl(base + S3C_TVCTRL) ++ &~(0x1f<<4))| output_type | signal_type, ++ base + S3C_TVCTRL); ++ ++ __raw_writel(0x01, base + S3C_FSCAUXCTRL); ++} ++ ++#if 0 ++static void s3c_tvenc_set_pedestal(tv_enc_switch_t sw) ++{ ++ if(sw) ++ __raw_writel(EPC_PED_ON, base + S3C_PEDCTRL); ++ else ++ __raw_writel(EPC_PED_OFF, base + S3C_PEDCTRL); ++} ++ ++static void s3c_tvenc_set_sub_carrier_freq(u32 freq) ++{ ++ __raw_writel(FSC_CTRL(freq), base + S3C_FSCCTRL); ++} ++ ++static void s3c_tvenc_set_fsc_dto(u32 val) ++{ ++ unsigned int temp; ++ ++ temp = (0x1<<31)|(val&0x7fffffff); ++ __raw_writel(temp, base + S3C_FSCDTOMANCTRL); ++} ++ ++static void s3c_tvenc_disable_fsc_dto(void) ++{ ++ __raw_writel(__raw_readl(base + S3C_FSCDTOMANCTRL)&~(1<<31), ++ base + S3C_FSCDTOMANCTRL); ++} ++ ++static void s3c_tvenc_set_bg(u32 soft_mix, u32 color, u32 lum_offset) ++{ ++ unsigned int bg_color; ++ switch(color) { ++ case 0 : ++ bg_color = BGC_BGCS_BLACK; ++ break; ++ case 1 : ++ bg_color = BGC_BGCS_BLUE; ++ break; ++ case 2 : ++ bg_color = BGC_BGCS_RED; ++ break; ++ case 3 : ++ bg_color = BGC_BGCS_MAGENTA; ++ break; ++ case 4 : ++ bg_color = BGC_BGCS_GREEN; ++ break; ++ case 5 : ++ bg_color = BGC_BGCS_CYAN; ++ break; ++ case 6 : ++ bg_color = BGC_BGCS_YELLOW; ++ break; ++ case 7 : ++ bg_color = BGC_BGCS_WHITE; ++ break; ++ } ++ if(soft_mix) ++ __raw_writel(BGC_SME_ENA|bg_color|BGC_BGYOFS(lum_offset), ++ base + S3C_BGCTRL); ++ else ++ __raw_writel(BGC_SME_DIS|bg_color|BGC_BGYOFS(lum_offset), ++ base + S3C_BGCTRL); ++ ++} ++ ++static void s3c_tvenc_set_bg_vav_hav(u32 hav_len, u32 vav_len, u32 hav_st, u32 vav_st) ++{ ++ __raw_writel(BVH_BG_HL(hav_len)|BVH_BG_HS(hav_st)|BVH_BG_VL(vav_len)|BVH_BG_VS(vav_st), ++ base + S3C_BGHVAVCTRL); ++} ++#endif ++ ++static void s3c_tvenc_set_hue_phase(u32 phase_val) ++{ ++ __raw_writel(HUE_CTRL(phase_val), ++ base + S3C_HUECTRL); ++} ++ ++#if 0 ++static u32 s3c_tvenc_get_hue_phase(void) ++{ ++ return __raw_readl(base + S3C_HUECTRL)&0xff; ++} ++#endif ++ ++static void s3c_tvenc_set_contrast(u32 contrast) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_CONTRABRIGHT); ++ ++ __raw_writel((temp &~0xff)|contrast, ++ base + S3C_CONTRABRIGHT); ++} ++ ++#if 0 ++static u32 s3c_tvenc_get_contrast(void) ++{ ++ return (__raw_readl(base + S3C_CONTRABRIGHT)&0xff); ++} ++#endif ++ ++static void s3c_tvenc_set_bright(u32 bright) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_CONTRABRIGHT); ++ ++ __raw_writel((temp &~(0xff<<16))| (bright<<16), ++ base + S3C_CONTRABRIGHT); ++} ++ ++#if 0 ++static u32 s3c_tvenc_get_bright(void) ++{ ++ return ((__raw_readl(base + S3C_CONTRABRIGHT)&(0xff<<16))>>16); ++} ++ ++ ++static void s3c_tvenc_set_cbgain(u32 cbgain) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_CBCRGAINCTRL); ++ ++ __raw_writel((temp &~0xff)|cbgain, ++ base + S3C_CBCRGAINCTRL); ++} ++ ++ ++static u32 s3c_tvenc_get_cbgain(void) ++{ ++ return (__raw_readl(base + S3C_CBCRGAINCTRL)&0xff); ++} ++ ++static void s3c_tvenc_set_crgain(u32 crgain) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_CBCRGAINCTRL); ++ ++ __raw_writel((temp &~(0xff<<16))| (crgain<<16), ++ base + S3C_CBCRGAINCTRL); ++} ++ ++static u32 s3c_tvenc_get_crgain(void) ++{ ++ return ((__raw_readl(base + S3C_CBCRGAINCTRL)&(0xff<<16))>>16); ++} ++#endif ++ ++static void s3c_tvenc_enable_gamma_control(tv_enc_switch_t enable) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_GAMMACTRL); ++ if(enable == ON) ++ temp |= (1<<12); ++ else ++ temp &= ~(1<<12); ++ ++ __raw_writel(temp, base + S3C_GAMMACTRL); ++} ++ ++static void s3c_tvenc_set_gamma_gain(u32 ggain) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_GAMMACTRL); ++ ++ __raw_writel((temp &~(0x7<<8))| (ggain<<8), ++ base + S3C_GAMMACTRL); ++} ++ ++#if 0 ++static u32 s3c_tvenc_get_gamma_gain(void) ++{ ++ return ((__raw_readl(base + S3C_GAMMACTRL)&(0x7<<8))>>8); ++} ++ ++static void s3c_tvenc_enable_mute_control(tv_enc_switch_t enable) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_GAMMACTRL); ++ if(enable == ON) ++ temp |= (1<<12); ++ else ++ temp &= ~(1<<12); ++ ++ __raw_writel(temp, base + S3C_GAMMACTRL); ++} ++ ++static void s3c_tvenc_set_mute(u32 y, u32 cb, u32 cr) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_MUTECTRL); ++ ++ temp &=~(0xffffff<<8); ++ temp |= (cr & 0xff)<<24; ++ temp |= (cb & 0xff)<<16; ++ temp |= (y & 0xff)<<8; ++ ++ __raw_writel(temp, base + S3C_MUTECTRL); ++} ++ ++static void s3c_tvenc_get_mute(u32 *y, u32 *cb, u32 *cr) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_MUTECTRL); ++ ++ *y = (temp&(0xff<<8))>>8; ++ *cb = (temp&(0xff<<16))>>16; ++ *cr = (temp&(0xff<<24))>>24; ++} ++#endif ++ ++static void s3c_tvenc_get_active_win_center(u32 *vert, u32 *horz) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_HENHOFFSET); ++ ++ *vert = (temp&(0x3f<<24))>>24; ++ *horz = (temp&(0xff<<16))>>16; ++} ++ ++static void s3c_tvenc_set_active_win_center(u32 vert, u32 horz) ++{ ++ u32 temp; ++ ++ temp = __raw_readl(base + S3C_HENHOFFSET); ++ ++ temp &=~(0x3ffff<<16); ++ temp |= (vert&0x3f)<<24; ++ temp |= (horz&0xff)<<16; ++ ++ __raw_writel(temp, base + S3C_HENHOFFSET); ++} ++ ++// LCD display controller configuration functions ++static void s3c_lcd_set_output_path(lcd_local_output_t out) ++{ ++#if 0 // peter for 2.6.21 kernel ++ s3c_fb_set_output_path(out); ++#else // peter for 2.6.24 kernel ++ s3cfb_set_output_path(out); ++#endif ++} ++ ++static void s3c_lcd_set_clkval(u32 clkval) ++{ ++#if 0 // peter for 2.6.21 kernel ++ s3c_fb_set_clkval(clkval); ++#else // peter for 2.6.24 kernel ++ s3cfb_set_clock(clkval); ++#endif ++} ++ ++static void s3c_lcd_enable_rgbport(u32 on_off) ++{ ++#if 0 // peter for 2.6.21 kernel ++ s3c_fb_enable_rgbport(on_off); ++#else // peter for 2.6.24 kernel ++ s3cfb_enable_rgbport(on_off); ++#endif ++} ++ ++static void s3c_lcd_start(void) ++{ ++#if 0 // peter for 2.6.21 kernel ++ s3c_fb_start_lcd(); ++#else // peter for 2.6.24 kernel ++ s3cfb_start_lcd(); ++#endif ++} ++ ++static void s3c_lcd_stop(void) ++{ ++#if 0 // peter for 2.6.21 kernel ++ s3c_fb_stop_lcd(); ++#else // peter for 2.6.24 kernel ++ s3cfb_stop_lcd(); ++#endif ++} ++ ++ ++static void s3c_lcd_set_config(void) ++{ ++ backup_reg[0] = __raw_readl(S3C_VIDCON0); ++ backup_reg[1] = __raw_readl(S3C_VIDCON2); ++ ++ s3c_lcd_set_output_path(LCD_TVRGB); ++ tv_param.lcd_output_mode = LCD_TVRGB; ++ ++ s3c_lcd_set_clkval(4); ++ s3c_lcd_enable_rgbport(1); ++} ++ ++static void s3c_lcd_exit_config(void) ++{ ++ __raw_writel(backup_reg[0], S3C_VIDCON0); ++ __raw_writel(backup_reg[1], S3C_VIDCON2); ++ tv_param.lcd_output_mode = LCD_RGB; ++} ++ ++static int scaler_test_start(void) ++{ ++ tv_param.sp.DstFullWidth = 640; ++ tv_param.sp.DstFullHeight= 480; ++ tv_param.sp.DstCSpace = RGB16; ++ ++ s3c_tvscaler_config(&tv_param.sp); ++ ++ s3c_tvscaler_int_enable(1); ++ ++ s3c_tvscaler_start(); ++ ++ return 0; ++} ++ ++static int scaler_test_stop(void) ++{ ++ s3c_tvscaler_int_disable(); ++ ++ return 0; ++} ++ ++ ++static int tvout_start(void) ++{ ++ u32 width, height; ++ tv_standard_t type; ++ tv_conn_type_t conn; ++ ++ tv_param.sp.DstFullWidth *= 2; // For TV OUT ++ ++ width = tv_param.sp.DstFullWidth; ++ height = tv_param.sp.DstFullHeight; ++ type = tv_param.sig_type; ++ conn = tv_param.connect; ++ ++ /* Set TV-SCALER parameter */ ++ switch(tv_param.v2.input->type) { ++ case V4L2_INPUT_TYPE_FIFO: // LCD FIFO-OUT ++ tv_param.sp.Mode = FREE_RUN; ++ tv_param.sp.DstCSpace = YCBYCR; ++ /* Display controller setting */ ++ s3c_lcd_stop(); ++ s3c_lcd_set_config(); ++ break; ++ case V4L2_INPUT_TYPE_MSDMA: // MSDMA ++ tv_param.sp.Mode = FREE_RUN; ++ tv_param.sp.DstCSpace = YCBYCR; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ s3c_tvenc_set_tv_mode(type, conn); ++ s3c_tvenc_set_image_size(width, height); ++ s3c_tvenc_switch(ON); ++ ++ s3c_tvscaler_config(&tv_param.sp); // for setting DstStartX/Y, DstWidth/Height ++ s3c_tvscaler_set_interlace(1); ++ if(tv_param.v2.input->type == V4L2_INPUT_TYPE_FIFO) ++ s3c_tvscaler_int_disable(); ++ else ++ s3c_tvscaler_int_enable(1); ++ s3c_tvscaler_start(); ++ ++ if(tv_param.v2.input->type == V4L2_INPUT_TYPE_FIFO) ++ s3c_lcd_start(); ++ ++ return 0; ++} ++ ++static int tvout_stop(void) ++{ ++ ++ s3c_tvscaler_set_interlace(0); ++ s3c_tvscaler_stop_freerun(); ++ s3c_tvscaler_int_disable(); ++ s3c_tvenc_switch(OFF); ++ ++ switch(tv_param.v2.input->type) { ++ case V4L2_INPUT_TYPE_FIFO: // LCD FIFO-OUT ++ /* Display controller setting */ ++ s3c_lcd_stop(); ++ s3c_lcd_exit_config(); ++ s3c_lcd_start(); ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++/* ------------------------------------------ V4L2 SUPPORT ----------------------------------------------*/ ++/* ------------- In FIFO and MSDMA, v4l2_input supported by S3C TVENC controller ------------------*/ ++static struct v4l2_input tvenc_inputs[] = { ++ { ++ .index = 0, ++ .name = "LCD FIFO_OUT", ++ .type = V4L2_INPUT_TYPE_FIFO, ++ .audioset = 1, ++ .tuner = 0, /* ignored */ ++ .std = 0, ++ .status = 0, ++ }, ++ { ++ .index = 1, ++ .name = "Memory input (MSDMA)", ++ .type = V4L2_INPUT_TYPE_MSDMA, ++ .audioset = 2, ++ .tuner = 0, ++ .std = 0, ++ .status = 0, ++ } ++}; ++ ++/* ------------ Out FIFO and MADMA, v4l2_output supported by S3C TVENC controller ----------------*/ ++static struct v4l2_output tvenc_outputs[] = { ++ { ++ .index = 0, ++ .name = "TV-OUT", ++ .type = V4L2_OUTPUT_TYPE_ANALOG, ++ .audioset = 0, ++ .modulator = 0, ++ .std = V4L2_STD_PAL | V4L2_STD_NTSC_M, ++ }, ++ { ++ .index = 1, ++ .name = "Memory output (MSDMA)", ++ .type = V4L2_OUTPUT_TYPE_MSDMA, ++ .audioset = 0, ++ .modulator = 0, ++ .std = 0, ++ }, ++ ++}; ++ ++const struct v4l2_fmtdesc tvenc_input_formats[] = { ++ { ++ .index = 0, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .description = "16 bpp RGB, le", ++ .pixelformat = V4L2_PIX_FMT_RGB565, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, ++ { ++ .index = 1, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .flags = FORMAT_FLAGS_PACKED, ++ .description = "24 bpp RGB, le", ++ .pixelformat = V4L2_PIX_FMT_RGB24, ++ }, ++ { ++ .index = 2, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .description = "4:2:2, planar, Y-Cb-Cr", ++ .pixelformat = V4L2_PIX_FMT_YUV422P, ++ ++ }, ++ { ++ .index = 3, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .description = "4:2:0, planar, Y-Cb-Cr", ++ .pixelformat = V4L2_PIX_FMT_YUV420, ++ } ++}; ++ ++ ++const struct v4l2_fmtdesc tvenc_output_formats[] = { ++ { ++ .index = 0, ++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, ++ .description = "16 bpp RGB, le", ++ .pixelformat = V4L2_PIX_FMT_RGB565, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, ++ { ++ .index = 1, ++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, ++ .flags = FORMAT_FLAGS_PACKED, ++ .description = "24 bpp RGB, le", ++ .pixelformat = V4L2_PIX_FMT_RGB24, ++ }, ++ { ++ .index = 2, ++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .description = "4:2:2, planar, Y-Cb-Cr", ++ .pixelformat = V4L2_PIX_FMT_YUV422P, ++ ++ }, ++ { ++ .index = 3, ++ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .description = "4:2:0, planar, Y-Cb-Cr", ++ .pixelformat = V4L2_PIX_FMT_YUV420, ++ } ++}; ++ ++const struct v4l2_standard tvout_standards[] = { ++ { ++ .index = 0, ++ .id = V4L2_STD_NTSC_M, ++ .name = "NTSC type", ++ }, ++ { ++ .index = 1, ++ .id = V4L2_STD_PAL, ++ .name = "PAL type", ++ } ++}; ++ ++#define NUMBER_OF_INPUT_FORMATS ARRAY_SIZE(tvenc_input_formats) ++#define NUMBER_OF_OUTPUT_FORMATS ARRAY_SIZE(tvenc_output_formats) ++#define NUMBER_OF_INPUTS ARRAY_SIZE(tvenc_inputs) ++#define NUMBER_OF_OUTPUTS ARRAY_SIZE(tvenc_outputs) ++#define NUMBER_OF_STANDARDS ARRAY_SIZE(tvout_standards) ++ ++static int s3c_tvenc_g_fmt(struct v4l2_format *f) ++{ ++ int size = sizeof(struct v4l2_pix_format); ++ ++ memset(&f->fmt.pix, 0, size); ++ memcpy(&f->fmt.pix, &tv_param.v2.pixfmt, size); ++ ++ return 0; ++} ++ ++static int s3c_tvenc_s_fmt(struct v4l2_format *f) ++{ ++ /* update our state informations */ ++ tv_param.v2.pixfmt= f->fmt.pix; ++ ++ // peter LCD output related operation ++ if (tv_param.v2.pixfmt.pixelformat == V4L2_PIX_FMT_RGB565 ) { ++ ++ tv_param.sp.SrcFullWidth = tv_param.v2.pixfmt.width; ++ tv_param.sp.SrcFullHeight = tv_param.v2.pixfmt.height; ++ tv_param.sp.SrcStartX = 0; ++ tv_param.sp.SrcStartY = 0; ++ tv_param.sp.SrcWidth = tv_param.sp.SrcFullWidth; ++ tv_param.sp.SrcHeight = tv_param.sp.SrcFullHeight; ++ ++ printk("TV-OUT: LCD path operation set\n"); ++ ++ // peter for padded data of mfc output ++ } else if (tv_param.v2.pixfmt.pixelformat == V4L2_PIX_FMT_YUV420) { ++ ++#ifdef DIVX_TEST // padded output ++ tv_param.sp.SrcFullWidth = tv_param.v2.pixfmt.width + 2*16; ++ tv_param.sp.SrcFullHeight = tv_param.v2.pixfmt.height + 2*16; ++ tv_param.sp.SrcStartX = 16; ++ tv_param.sp.SrcStartY = 16; ++ tv_param.sp.SrcWidth = tv_param.sp.SrcFullWidth - 2*tv_param.sp.SrcStartX; ++ tv_param.sp.SrcHeight = tv_param.sp.SrcFullHeight - 2*tv_param.sp.SrcStartY; ++#else // not padded output ++ tv_param.sp.SrcFullWidth = tv_param.v2.pixfmt.width; ++ tv_param.sp.SrcFullHeight = tv_param.v2.pixfmt.height; ++ tv_param.sp.SrcStartX = 0; ++ tv_param.sp.SrcStartY = 0; ++ tv_param.sp.SrcWidth = tv_param.sp.SrcFullWidth; ++ tv_param.sp.SrcHeight = tv_param.sp.SrcFullHeight; ++#endif ++ ++ printk("TV-OUT: MFC path operation set\n"); ++ ++ } ++ ++ switch(tv_param.v2.pixfmt.pixelformat) { ++ case V4L2_PIX_FMT_RGB565: ++ tv_param.sp.SrcCSpace = RGB16; ++ break; ++ case V4L2_PIX_FMT_RGB24: ++ tv_param.sp.SrcCSpace = RGB24; ++ break; ++ case V4L2_PIX_FMT_YUV420: ++ tv_param.sp.SrcCSpace = YC420; ++ break; ++ case V4L2_PIX_FMT_YUV422P: ++ tv_param.sp.SrcCSpace = YC422; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++// camif_convert_into_camif_cfg_t(cfg, 1); ++ return 0; ++} ++ ++static int s3c_tvenc_s_input(int index) ++{ ++ ++ tv_param.v2.input = &tvenc_inputs[index]; ++ switch(tv_param.v2.input->type) { ++ case V4L2_INPUT_TYPE_FIFO: // LCD FIFO-OUT ++ tv_param.sp.InPath = POST_FIFO; ++ break; ++ case V4L2_INPUT_TYPE_MSDMA: // MSDMA ++ tv_param.sp.InPath = POST_DMA; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int s3c_tvenc_s_output(int index) ++{ ++ tv_param.v2.output = &tvenc_outputs[index]; ++ switch(tv_param.v2.output->type) { ++ case V4L2_OUTPUT_TYPE_ANALOG: // TV-OUT (FIFO-OUT) ++ tv_param.sp.OutPath = POST_FIFO; ++ break; ++ case V4L2_OUTPUT_TYPE_MSDMA: // MSDMA ++ tv_param.sp.OutPath = POST_DMA; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int s3c_tvenc_s_std(v4l2_std_id *id) ++{ ++// printk("s3c_tvenc_s_std: *id=0x%x",*id); ++ switch(*id) { ++ case V4L2_STD_NTSC_M: ++ tv_param.sig_type = NTSC_M; ++ tv_param.sp.DstFullWidth = 720; ++ tv_param.sp.DstFullHeight = 480; ++ break; ++ case V4L2_STD_PAL: ++ tv_param.sig_type = PAL_M; ++ tv_param.sp.DstFullWidth = 720; ++ tv_param.sp.DstFullHeight = 576; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int s3c_tvenc_v4l2_control(struct v4l2_control *ctrl) ++{ ++ switch(ctrl->id) { ++ ++ // peter added for MFC related op. ++ case V4L2_CID_MPEG_STREAM_PID_VIDEO: ++ { ++ tv_param.sp.SrcFrmSt = ctrl->value; ++ return 0; ++ } ++ ++ case V4L2_CID_CONNECT_TYPE: ++ { ++ if(ctrl->value == 0) { // COMPOSITE ++ tv_param.connect = COMPOSITE; ++ } else if(ctrl->value == 1) { //S-VIDEO ++ tv_param.connect = S_VIDEO; ++ } else { ++ return -EINVAL; ++ } ++ return 0; ++ } ++ ++ case V4L2_CID_BRIGHTNESS: ++ { ++ s32 val = ctrl->value; ++ if((val > 0xff)||(val < 0)) ++ return -EINVAL; ++ else ++ s3c_tvenc_set_bright(val); ++ ++ return 0; ++ } ++ ++ case V4L2_CID_CONTRAST: ++ { ++ s32 val = ctrl->value; ++ if((val > 0xff)||(val < 0)) ++ return -EINVAL; ++ else ++ s3c_tvenc_set_contrast(val); ++ ++ return 0; ++ } ++ ++ case V4L2_CID_GAMMA: ++ { ++ s32 val = ctrl->value; ++ if((val > 0x3)||(val < 0)) { ++ return -EINVAL; ++ } else { ++ s3c_tvenc_enable_gamma_control(ON); ++ s3c_tvenc_set_gamma_gain(val); ++ s3c_tvenc_enable_gamma_control(OFF); ++ } ++ return 0; ++ } ++ ++ case V4L2_CID_HUE: ++ { ++ s32 val = ctrl->value; ++ if((val > 0xff)||(val < 0)) ++ return -EINVAL; ++ else ++ s3c_tvenc_set_hue_phase(val); ++ ++ return 0; ++ } ++ ++ case V4L2_CID_HCENTER: ++ { ++ s32 val = ctrl->value; ++ u32 curr_horz, curr_vert; ++ ++ if((val > 0xff)||(val < 0)) { ++ return -EINVAL; ++ } else { ++ s3c_tvenc_get_active_win_center(&curr_vert, &curr_horz); ++ s3c_tvenc_set_active_win_center(curr_vert, val); ++ } ++ ++ return 0; ++ } ++ ++ case V4L2_CID_VCENTER: ++ { ++ s32 val = ctrl->value; ++ u32 curr_horz, curr_vert; ++ ++ if((val > 0x3f)||(val < 0)) { ++ return -EINVAL; ++ } else { ++ s3c_tvenc_get_active_win_center(&curr_vert, &curr_horz); ++ s3c_tvenc_set_active_win_center(val, curr_horz); ++ } ++ ++ return 0; ++ } ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int s3c_tvenc_open(struct inode *inode, struct file *filp) ++{ ++ int err; ++ ++ err = video_exclusive_open(inode, filp); // One function of V4l2 driver ++ ++ if(err < 0) ++ return err; ++ filp->private_data = &tv_param; ++ ++ s3c_tvscaler_init(); ++ ++ /* Success */ ++ return 0; ++} ++ ++int s3c_tvenc_release(struct inode *inode, struct file *filp) ++{ ++ video_exclusive_release(inode, filp); ++ ++ /* Success */ ++ return 0; ++} ++ ++static int s3c_tvenc_do_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,void *arg) ++{ ++ int ret; ++ ++ switch(cmd){ ++ case VIDIOC_QUERYCAP: ++ { ++ struct v4l2_capability *cap = arg; ++ strcpy(cap->driver, "S3C TV-OUT driver"); ++ strlcpy(cap->card, tv_param.v->name, sizeof(cap->card)); ++ sprintf(cap->bus_info, "ARM AHB BUS"); ++ cap->version = 0; ++ cap->capabilities = tv_param.v->type2; ++ return 0; ++ } ++ ++ case VIDIOC_OVERLAY: ++ { ++ int on = *(int *)arg; ++ ++ printk("TV-OUT: VIDIOC_OVERLAY on:%d\n", on); ++ if (on != 0) { ++ ret = tvout_start(); ++ } else { ++ ret = tvout_stop(); ++ } ++ return ret; ++ } ++ ++ case VIDIOC_ENUMINPUT: ++ { ++ struct v4l2_input *i = arg; ++ printk("TV-OUT: VIDIOC_ENUMINPUT : index = %d\n", i->index); ++ ++ if ((i->index) >= NUMBER_OF_INPUTS) { ++ return -EINVAL; ++ } ++ memcpy(i, &tvenc_inputs[i->index], sizeof(struct v4l2_input)); ++ return 0; ++ } ++ ++ case VIDIOC_S_INPUT: // 0 -> LCD FIFO-OUT, 1 -> MSDMA ++ { ++ int index = *((int *)arg); ++ printk("TV-OUT: VIDIOC_S_INPUT \n"); ++ ++ if (index >= NUMBER_OF_INPUTS) { ++ return -EINVAL; ++ } ++ else { ++ s3c_tvenc_s_input(index); ++ return 0; ++ } ++ } ++ ++ case VIDIOC_G_INPUT: ++ { ++ u32 *i = arg; ++ printk("TV-OUT: VIDIOC_G_INPUT \n"); ++ *i = tv_param.v2.input->type; ++ return 0; ++ } ++ ++ case VIDIOC_ENUMOUTPUT: ++ { ++ struct v4l2_output *i = arg; ++ printk("TV-OUT: VIDIOC_ENUMOUTPUT : index = %d\n", i->index); ++ ++ if ((i->index) >= NUMBER_OF_OUTPUTS) { ++ return -EINVAL; ++ } ++ memcpy(i, &tvenc_outputs[i->index], sizeof(struct v4l2_output)); ++ return 0; ++ } ++ ++ case VIDIOC_S_OUTPUT: // 0 -> TV / FIFO , 1 -> MSDMA ++ { ++ int index = *((int *)arg); ++ printk("TV-OUT: VIDIOC_S_OUTPUT \n"); ++ ++ if (index >= NUMBER_OF_OUTPUTS) { ++ return -EINVAL; ++ } ++ else { ++ s3c_tvenc_s_output(index); ++ return 0; ++ } ++ } ++ ++ case VIDIOC_G_OUTPUT: ++ { ++ u32 *i = arg; ++ printk("VIDIOC_G_OUTPUT \n"); ++ *i = tv_param.v2.output->type; ++ return 0; ++ } ++ ++ case VIDIOC_ENUM_FMT: ++ { struct v4l2_fmtdesc *f = arg; ++ enum v4l2_buf_type type = f->type; ++ int index = f->index; ++ ++ printk("C: VIDIOC_ENUM_FMT : index = %d\n", index); ++ if (index >= NUMBER_OF_INPUT_FORMATS) ++ return -EINVAL; ++ ++ switch (type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ break; ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ default: ++ return -EINVAL; ++ } ++ memset(f, 0, sizeof(*f)); ++ memcpy(f, tv_param.v2.fmtdesc+index, sizeof(*f)); ++ return 0; ++ } ++ ++ case VIDIOC_G_FMT: ++ { ++ struct v4l2_format *f = arg; ++ printk("C: VIDIOC_G_FMT \n"); ++ ret = s3c_tvenc_g_fmt(f); ++ return ret; ++ } ++ ++ case VIDIOC_S_FMT: ++ { ++ struct v4l2_format *f = arg; ++ printk("C: VIDIOC_S_FMT \n"); ++ ret = s3c_tvenc_s_fmt(f); ++ if(ret != 0) { ++ printk("s3c_tvenc_set_fmt() failed !\n"); ++ return -EINVAL; ++ } ++ return ret; ++ } ++ ++ case VIDIOC_S_CTRL: ++ { ++ struct v4l2_control *ctrl = arg; ++ //printk("P: VIDIOC_S_CTRL \n"); ++ ret = s3c_tvenc_v4l2_control(ctrl); ++ return ret; ++ } ++ ++ case VIDIOC_ENUMSTD: ++ { ++ struct v4l2_standard *e = arg; ++ unsigned int index = e->index; ++ ++ if (index >= NUMBER_OF_STANDARDS) ++ return -EINVAL; ++ v4l2_video_std_construct(e, tvout_standards[e->index].id, ++ &tvout_standards[e->index].name); ++ e->index = index; ++ return 0; ++ } ++ ++ case VIDIOC_G_STD: ++ { ++ v4l2_std_id *id = arg; ++ *id = tvout_standards[0].id; ++ return 0; ++ } ++ ++ case VIDIOC_S_STD: ++ { ++ v4l2_std_id *id = arg; ++ unsigned int i; ++ ++ for (i = 0; i < NUMBER_OF_STANDARDS; i++) { ++ //printk("P: *id = %d, tvout_standards[i].id = %d\n", *id, tvout_standards[i].id); ++ if (*id & tvout_standards[i].id) ++ break; ++ } ++ if (i == NUMBER_OF_STANDARDS) ++ return -EINVAL; ++ ++ ret = s3c_tvenc_s_std(id); ++ return ret; ++ } ++ ++ case VIDIOC_S_TVOUT_ON: ++ { ++ //int *SrcFrmSt = arg; ++ //printk("---peter VIDIOC_S_TVOUT_ON : SrcFrmSt = 0x%08x\n", *SrcFrmSt); ++ ret = tvout_start(); ++ return ret; ++ } ++ ++ case VIDIOC_S_TVOUT_OFF: ++ { ++ ret = tvout_stop(); ++ return ret; ++ } ++ ++ case VIDIOC_S_SCALER_TEST: ++ { ++ ret = scaler_test_start(); ++ mdelay(1); ++ ret = scaler_test_stop(); ++ return ret; ++ } ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int s3c_tvenc_ioctl_v4l2(struct inode *inode, struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ return video_usercopy(inode, filp, cmd, arg, s3c_tvenc_do_ioctl); ++} ++ ++int s3c_tvenc_read(struct file *filp, char *buf, size_t count, ++ loff_t *f_pos) ++{ ++ return 0; ++} ++ ++int s3c_tvenc_write(struct file *filp, const char *buf, size_t ++ count, loff_t *f_pos) ++{ ++ return 0; ++} ++ ++int s3c_tvenc_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ u32 size = vma->vm_end - vma->vm_start; ++ u32 max_size; ++ u32 page_frame_no; ++ ++ page_frame_no = __phys_to_pfn(POST_BUFF_BASE_ADDR); ++ ++ max_size = RESERVE_POST_MEM + PAGE_SIZE - (RESERVE_POST_MEM % PAGE_SIZE); ++ ++ if(size > max_size) { ++ return -EINVAL; ++ } ++ ++ vma->vm_flags |= VM_RESERVED; ++ ++ if( remap_pfn_range(vma, vma->vm_start, page_frame_no, ++ size, vma->vm_page_prot)) { ++ printk(KERN_ERR "%s: mmap_error\n", __FUNCTION__); ++ return -EAGAIN; ++ ++ } ++ ++ return 0; ++} ++ ++struct file_operations s3c_tvenc_fops = { ++ .owner = THIS_MODULE, ++ .open = s3c_tvenc_open, ++ .ioctl = s3c_tvenc_ioctl_v4l2, ++ .release = s3c_tvenc_release, ++ .read = s3c_tvenc_read, ++ .write = s3c_tvenc_write, ++ .mmap = s3c_tvenc_mmap, ++}; ++ ++void s3c_tvenc_vdev_release (struct video_device *vdev) { ++ kfree(vdev); ++} ++ ++struct video_device tvencoder = { ++ .name = "TVENCODER", ++ .type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_SCALES, ++ .type2 = V4L2_CAP_VIDEO_OUTPUT| V4L2_CAP_VIDEO_CAPTURE, /* V4L2 */ ++ //.hardware = 0x01, // peter for 2.6.24 kernel ++ .fops = &s3c_tvenc_fops, ++ .release = s3c_tvenc_vdev_release, ++ .minor = TVENC_MINOR, ++}; ++ ++irqreturn_t s3c_tvenc_isr(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ u32 mode; ++ ++ mode = __raw_readl(base + S3C_TVCTRL); ++ ++ // Clear FIFO under-run status pending bit ++ mode |= (1<<12); ++ ++ __raw_writel(mode, base + S3C_TVCTRL); ++ ++ wake_up_interruptible(&waitq); ++ return IRQ_HANDLED; ++} ++ ++static int s3c_tvenc_probe(struct platform_device *pdev) ++{ ++ ++ struct resource *res; ++ ++ int ret; ++ ++ /* find the IRQs */ ++ s3c_tvenc_irq = platform_get_irq(pdev, 0); ++ if(s3c_tvenc_irq <= 0) { ++ printk(KERN_ERR PFX "failed to get irq resouce\n"); ++ return -ENOENT; ++ } ++ ++ /* get the memory region for the tv scaler driver */ ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if(res == NULL) { ++ printk(KERN_ERR PFX "failed to get memory region resouce\n"); ++ return -ENOENT; ++ } ++ ++ s3c_tvenc_mem = request_mem_region(res->start, res->end-res->start+1, pdev->name); ++ if(s3c_tvenc_mem == NULL) { ++ printk(KERN_ERR PFX "failed to reserve memory region\n"); ++ return -ENOENT; ++ } ++ ++ ++ base = ioremap(s3c_tvenc_mem->start, s3c_tvenc_mem->end - res->start + 1); ++ if(s3c_tvenc_mem == NULL) { ++ printk(KERN_ERR PFX "failed ioremap\n"); ++ return -ENOENT; ++ } ++ ++ tvenc_clock = clk_get(&pdev->dev, "tv_encoder"); ++ if(tvenc_clock == NULL) { ++ printk(KERN_ERR PFX "failed to find tvenc clock source\n"); ++ return -ENOENT; ++ } ++ ++ clk_enable(tvenc_clock); ++ ++ h_clk = clk_get(&pdev->dev, "hclk"); ++ if(h_clk == NULL) { ++ printk(KERN_ERR PFX "failed to find h_clk clock source\n"); ++ return -ENOENT; ++ } ++ ++ init_waitqueue_head(&waitq); ++ ++ tv_param.v = video_device_alloc(); ++ if(!tv_param.v) { ++ printk(KERN_ERR "s3c-tvenc: video_device_alloc() failed\n"); ++ return -ENOMEM; ++ } ++ memcpy(tv_param.v, &tvencoder, sizeof(tvencoder)); ++ if(video_register_device(tv_param.v, VFL_TYPE_GRABBER, TVENC_MINOR) != 0) { ++ printk("s3c_camera_driver.c : Couldn't register this codec driver.\n"); ++ return 0; ++ } ++ ++ ret = request_irq(s3c_tvenc_irq, s3c_tvenc_isr, SA_INTERRUPT, ++ "TV_ENCODER", NULL); ++ if (ret) { ++ printk("request_irq(TV_ENCODER) failed.\n"); ++ return ret; ++ } ++ ++ printk(" Success\n"); ++ return 0; ++} ++ ++static int s3c_tvenc_remove(struct platform_device *dev) ++{ ++ printk(KERN_INFO "s3c_tvenc_remove called !\n"); ++ clk_disable(tvenc_clock); ++ free_irq(s3c_tvenc_irq, NULL); ++ if (s3c_tvenc_mem != NULL) { ++ pr_debug("s3-tvenc: releasing s3c_tvenc_mem\n"); ++ iounmap(base); ++ release_resource(s3c_tvenc_mem); ++ kfree(s3c_tvenc_mem); ++ } ++// video_unregister_device(tv_param.v); ++ return 0; ++} ++ ++static int s3c_tvenc_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ clk_disable(tvenc_clock); ++ return 0; ++} ++ ++static int s3c_tvenc_resume(struct platform_device *pdev) ++{ ++ clk_enable(tvenc_clock); ++ return 0; ++} ++ ++static struct platform_driver s3c_tvenc_driver = { ++ .probe = s3c_tvenc_probe, ++ .remove = s3c_tvenc_remove, ++ .suspend = s3c_tvenc_suspend, ++ .resume = s3c_tvenc_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "s3c-tvenc", ++ }, ++}; ++ ++static char banner[] __initdata = KERN_INFO "S3C6410 TV encoder Driver, (c) 2008 Samsung Electronics\n"; ++ ++static int __init s3c_tvenc_init(void) ++{ ++ ++ printk(banner); ++ ++ if(platform_driver_register(&s3c_tvenc_driver) != 0) ++ { ++ printk("Platform Device Register Failed \n"); ++ return -1; ++ } ++ ++ printk(" S3C6410 TV encoder Driver module init OK. \n"); ++ return 0; ++} ++ ++static void __exit s3c_tvenc_exit(void) ++{ ++ ++ video_unregister_device(tv_param.v); ++ platform_driver_unregister(&s3c_tvenc_driver); ++ ++ printk("S3C6410 TV encoder Driver module exit. \n"); ++} ++ ++ ++module_init(s3c_tvenc_init); ++module_exit(s3c_tvenc_exit); ++ ++ ++MODULE_AUTHOR("Peter, Oh"); ++MODULE_DESCRIPTION("S3C TV Encoder Device Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/video/samsung/s3c-tvenc.h b/drivers/media/video/samsung/s3c-tvenc.h +new file mode 100644 +index 0000000..30a66a2 +--- /dev/null ++++ b/drivers/media/video/samsung/s3c-tvenc.h +@@ -0,0 +1,165 @@ ++#ifndef __S3CTVENC_H_ ++#define __S3CTVENC_H_ ++ ++#include "s3c-tvscaler.h" ++ ++ ++#define TVENC_IOCTL_MAGIC 'T' ++ ++typedef struct { ++ ++} s3c_tvenc_info; ++ ++#define TV_ON _IO(TVENC_IOCTL_MAGIC, 0) ++#define TV_OFF _IO(TVENC_IOCTL_MAGIC, 1) ++#define SELECT_TV_OUT_FORMAT _IO(TVENC_IOCTL_MAGIC, 2) ++ ++#define TVENC_IOCTL_MAXNR 6 ++ ++#define TVENC_MINOR 14 // Just some number ++ ++typedef enum { ++ OFF, ++ ON ++} tv_enc_switch_t; ++ ++typedef enum { ++ NTSC_M, ++ PAL_M, ++ PAL_BGHID, ++ PAL_N, ++ PAL_NC, ++ PAL_60, ++ NTSC_443, ++ NTSC_J ++} tv_standard_t; ++ ++typedef enum { ++ QCIF, CIF/*352x288*/, ++ QQVGA, QVGA, VGA, SVGA/*800x600*/, SXGA/*1280x1024*/, UXGA/*1600x1200*/, QXGA/*2048x1536*/, ++ WVGA/*854x480*/, HD720/*1280x720*/, HD1080/*1920x1080*/ ++} img_size_t; ++ ++typedef enum { ++ BLACKSTRETCH, WHITESTRETCH, BLUESTRETCH ++} stretch_color_t; ++ ++typedef enum { ++ COMPOSITE, S_VIDEO ++} tv_conn_type_t; ++ ++typedef enum { ++ BLACK, BLUE, RED, MAGENTA, GREEN, CYAN, YELLOW, WHITE ++} bg_color_t; ++ ++typedef enum { ++ MUTE_Y, MUTE_CB, MUTE_CR ++} mute_type_t; ++ ++typedef enum { ++ AGC4L, AGC2L, N01, N02, P01, P02 ++} macro_pattern_t; ++ ++typedef enum { ++ LCD_RGB, LCD_TV, LCD_I80F, LCD_I80S, ++ LCD_TVRGB, LCD_TVI80F, LCD_TVI80S ++} lcd_local_output_t; ++ ++/* when App want to change v4l2 parameter, ++ * we instantly store it into v4l2_t v2 ++ * and then reflect it to hardware ++ */ ++typedef struct v4l2 { ++ struct v4l2_fmtdesc *fmtdesc; ++// struct v4l2_framebuffer frmbuf; /* current frame buffer */ ++ struct v4l2_pix_format pixfmt; ++ struct v4l2_input *input; ++ struct v4l2_output *output; ++// enum v4l2_status status; ++} v4l2_t; ++ ++ ++typedef struct { ++ tv_standard_t sig_type; ++ tv_conn_type_t connect; ++ /* Width of input image. The input value is twice original output image ++ * width. For example, you must set 1440 when the image width is 720. ++ * Max value is 1440 ++ */ ++ unsigned int in_width; ++ /* Height of input image ++ * Max value is 576 ++ */ ++ unsigned int in_height; ++ ++ // Setting value of VIDOUT[28:26] in Display ++ // controller(VIDCON0) ++ lcd_local_output_t lcd_output_mode; ++ // Set CLKVAL_F[13:6] of VIDCON0 with ++ // this value ++ unsigned int lcd_clkval_f; ++ ++ // Flag of lcd rgb port ++ // 0 : disable, 1 : enable ++ unsigned int lcd_rgb_port_flag; ++ ++ scaler_params_t sp; ++ ++ struct video_device *v; ++ v4l2_t v2; ++ ++} tv_out_params_t; ++ ++#define V4L2_INPUT_TYPE_MSDMA 3 ++#define V4L2_INPUT_TYPE_FIFO 4 ++#define V4L2_OUTPUT_TYPE_MSDMA 4 ++ ++#define FORMAT_FLAGS_DITHER 0x01 ++#define FORMAT_FLAGS_PACKED 0x02 ++#define FORMAT_FLAGS_PLANAR 0x04 ++#define FORMAT_FLAGS_RAW 0x08 ++#define FORMAT_FLAGS_CrCb 0x10 ++ ++/**************************************************************** ++* struct v4l2_control ++* Control IDs defined by S3C ++*****************************************************************/ ++ ++/* TV-OUT connector type */ ++#define V4L2_CID_CONNECT_TYPE (V4L2_CID_PRIVATE_BASE+0) ++ ++/**************************************************************** ++* I O C T L C O D E S F O R V I D E O D E V I C E S ++* It's only for S3C ++*****************************************************************/ ++#define VIDIOC_S_TVOUT_ON _IO ('V', BASE_VIDIOC_PRIVATE+0) ++#define VIDIOC_S_TVOUT_OFF _IO ('V', BASE_VIDIOC_PRIVATE+1) ++#define VIDIOC_S_SCALER_TEST _IO ('V', BASE_VIDIOC_PRIVATE+3) ++ ++ ++extern void s3c_tvscaler_config(scaler_params_t * sp); ++extern void s3c_tvscaler_int_enable(unsigned int int_type); ++extern void s3c_tvscaler_int_disable(void); ++extern void s3c_tvscaler_start(void); ++extern void s3c_tvscaler_stop_freerun(void); ++extern void s3c_tvscaler_init(void); ++extern void s3c_tvscaler_set_interlace(unsigned int on_off); ++extern int video_exclusive_release(struct inode * inode, struct file * file); ++extern int video_exclusive_open(struct inode * inode, struct file * file); ++ ++#if 0 // peter for 2.6.21 kernel ++extern void s3c_fb_start_lcd(void); ++extern void s3c_fb_stop_lcd(void); ++extern void s3c_fb_set_output_path(int out); ++extern void s3c_fb_set_clkval(unsigned int clkval); ++extern void s3c_fb_enable_rgbport(unsigned int on_off); ++#else // peter for 2.6.24 kernel ++extern void s3cfb_start_lcd(void); ++extern void s3cfb_stop_lcd(void); ++extern void s3cfb_set_output_path(int out); ++extern void s3cfb_set_clock(unsigned int clkval); ++extern void s3cfb_enable_rgbport(unsigned int on_off); ++#endif ++ ++ ++#endif // __S3CTVENC_H_ +diff --git a/drivers/media/video/samsung/s3c-tvscaler.c b/drivers/media/video/samsung/s3c-tvscaler.c +new file mode 100644 +index 0000000..376c866 +--- /dev/null ++++ b/drivers/media/video/samsung/s3c-tvscaler.c +@@ -0,0 +1,802 @@ ++ ++/* ++ * linux/drivers/tvenc/s3c-tvscaler.c ++ * ++ * Revision 1.0 ++ * ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ * S3C TV Scaler driver ++ * ++ */ ++ ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/types.h> ++#include <linux/timer.h> ++#include <linux/kernel.h> ++#include <linux/fs.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <asm/uaccess.h> ++#include <linux/errno.h> /* error codes */ ++#include <asm/div64.h> ++#include <linux/mm.h> ++#include <linux/tty.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/hardware.h> ++#include <asm/uaccess.h> ++#include <asm/arch/map.h> ++#include <linux/miscdevice.h> ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,16) ++#include <linux/config.h> ++#include <asm/arch/registers.h> ++#else ++#include <asm/arch/regs-tvscaler.h> ++#include <asm/arch/regs-s3c6400-clock.h> ++#endif ++ ++#include "s3c-tvscaler.h" ++ ++#define PFX "s3c_tv_scaler" ++ ++#define SINGLE_BUF 1 // Single buffer mode ++ ++ ++static struct clk *h_clk; ++static struct clk *tvscaler_clock; ++static void __iomem *base; ++static int s3c_tvscaler_irq = NO_IRQ; ++static struct resource *s3c_tvscaler_mem; ++ ++ ++//static unsigned char *addr_start_y; ++//static unsigned char *addr_start_rgb; ++ ++static wait_queue_head_t waitq; ++ ++irqreturn_t s3c_tvscaler_isr(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ u32 mode; ++ mode = __raw_readl(base + S3C_MODE); ++ mode &= ~(1 << 6); /* Clear Source in POST Processor */ ++ __raw_writel(mode, base + S3C_MODE); ++ ++// wake_up_interruptible(&waitq); ++ return IRQ_HANDLED; ++} ++ ++#if 0 ++static buff_addr_t buf_addr = { NULL }; ++ ++ ++static u32 post_alloc_pre_buff(scaler_params_t *sp) ++{ ++ u32 size; ++ ++#ifdef USE_DEDICATED_MEM ++ ++ buf_addr.pre_phy_addr = PHYS_OFFSET + (SYSTEM_RAM - RESERVE_POST_MEM); ++ buf_addr.pre_virt_addr = ioremap_nocache(buf_addr.pre_phy_addr, PRE_BUFF_SIZE); ++ if( !buf_addr.pre_virt_addr ) { ++ printk(KERN_ERR "%s: Failed to allocate pre buffer \n",__FUNCTION__); ++ return -ENOMEM; ++ } ++ ++ sp->SrcFrmSt = buf_addr.pre_phy_addr; ++#else ++ size = sp->SrcWidth * sp->SrcHeight * 2; ++ addr_start_y = kmalloc(size, GFP_DMA); ++ if(addr_start_y != NULL) return -ENOMEM; ++#endif ++ return 0; ++} ++ ++static u32 post_alloc_post_buff(scaler_params_t *sp) ++{ ++ u32 size; ++ ++#ifdef USE_DEDICATED_MEM ++ ++ buf_addr.post_phy_addr = PHYS_OFFSET + (SYSTEM_RAM - RESERVE_POST_MEM + PRE_BUFF_SIZE); ++ buf_addr.post_virt_addr = ioremap_nocache(buf_addr.post_phy_addr, POST_BUFF_SIZE); ++ if( !buf_addr.post_virt_addr ) { ++ printk(KERN_ERR "%s: Failed to allocate post buffer \n",__FUNCTION__); ++ return -ENOMEM; ++ } ++ ++ sp->DstFrmSt = buf_addr.post_phy_addr; ++#else ++ size = sp->DstWidth * sp->DstHeight * 2; ++ addr_start_rgb = kmalloc(size, GFP_DMA); ++ if(addr_start_rgb != NULL) return -ENOMEM; ++#endif ++ return 0; ++} ++ ++static u32 post_free_all_buffer(void) ++{ ++#ifdef USE_DEDICATED_MEM ++ if( buf_addr.pre_virt_addr ) { ++ iounmap(buf_addr.pre_virt_addr); ++ } ++ if( buf_addr.post_virt_addr ) { ++ iounmap(buf_addr.post_virt_addr); ++ } ++#endif ++ return 0; ++} ++#endif ++ ++static void s3c_tvscaler_set_clk_src(scaler_clk_src_t clk_src) ++{ ++ u32 tmp, rate; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ h_clk = clk_get(NULL, "hclk"); ++ ++ rate = clk_get_rate(h_clk); ++ ++ if(clk_src == HCLK) { ++ if(rate > 66000000) { ++ tmp &= ~(0x7f<<23); ++ tmp |= (1<<24); ++ tmp |= (1<<23); ++ } else { ++ tmp &=~ (0x7f<<23); ++ } ++ ++ } else if(clk_src == PLL_EXT) { ++ } else { ++ tmp &=~(0x7f<<23); ++ } ++ ++ tmp = (tmp &~ (0x3<<21)) | (clk_src<<21); ++ ++ __raw_writel(tmp, base + S3C_MODE); ++} ++ ++static void s3c_tvscaler_set_fmt(cspace_t src, cspace_t dst, s3c_scaler_path_t in, ++ s3c_scaler_path_t out, u32 *in_pixel_size, ++ u32 *out_pixel_size) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ tmp |= (0x1<<16); ++ tmp |= (0x2<<10); ++ ++ if(in == POST_DMA) { ++ ++ switch(src) { ++ case YC420: ++ tmp &=~((0x1<<3)|(0x1<<2)); ++ tmp |= (0x1<<8)|(0x1<<1); ++ *in_pixel_size = 1; ++ break; ++ case CRYCBY: ++ tmp &= ~((0x1<<15)|(0x1<<8)|(0x1<<3)|(0x1<<0)); ++ tmp |= (0x1<<2)|(0x1<<1); ++ *in_pixel_size = 2; ++ break; ++ case CBYCRY: ++ tmp &= ~((0x1<<8)|(0x1<<3)|(0x1<<0)); ++ tmp |= (0x1<<15)|(0x1<<2)|(0x1<<1); ++ *in_pixel_size = 2; ++ break; ++ case YCRYCB: ++ tmp &= ~((0x1<<15)|(0x1<<8)|(0x1<<3)); ++ tmp |= (0x1<<2)|(0x1<<1)|(0x1<<0); ++ *in_pixel_size = 2; ++ break; ++ case YCBYCR: ++ tmp &= ~((0x1<<8)|(0x1<<3)); ++ tmp |= (0x1<<15)|(0x1<<2)|(0x1<<1)|(0x1<<0); ++ *in_pixel_size = 2; ++ break; ++ case RGB24: ++ tmp &= ~(0x1<<8); ++ tmp |= (0x1<<3)|(0x1<<2)|(0x1<<1); ++ *in_pixel_size = 4; ++ break; ++ case RGB16: ++ tmp &= ~((0x1<<8)|(0x1<<1)); ++ tmp |= (0x1<<3)|(0x1<<2); ++ *in_pixel_size = 2; ++ break; ++ default: ++ break; ++ } ++ ++ } ++ else if(in == POST_FIFO) { ++ } ++ ++ if(out == POST_DMA) { ++ switch(dst) { ++ case YC420: ++ tmp &= ~(0x1<<18); ++ tmp |= (0x1<<17); ++ *out_pixel_size = 1; ++ break; ++ case CRYCBY: ++ tmp &= ~((0x1<<20)|(0x1<<19)|(0x1<<18)|(0x1<<17)); ++ *out_pixel_size = 2; ++ break; ++ case CBYCRY: ++ tmp &= ~((0x1<<19)|(0x1<<18)|(0x1<<17)); ++ tmp |= (0x1<<20); ++ *out_pixel_size = 2; ++ break; ++ case YCRYCB: ++ tmp &= ~((0x1<<20)|(0x1<<18)|(0x1<<17)); ++ tmp |= (0x1<<19); ++ *out_pixel_size = 2; ++ break; ++ case YCBYCR: ++ tmp &= ~((0x1<<18)|(0x1<<17)); ++ tmp |= (0x1<<20)|(0x1<<19); ++ *out_pixel_size = 2; ++ break; ++ case RGB24: ++ tmp |= (0x1<<18)|(0x1<<4); ++ *out_pixel_size = 4; ++ break; ++ case RGB16: ++ tmp &= ~(0x1<<4); ++ tmp |= (0x1<<18); ++ *out_pixel_size = 2; ++ break; ++ default: ++ break; ++ } ++ } ++ else if(out == POST_FIFO) { ++ if(dst == RGB24) { ++ tmp |= (0x1<<18)|(0x1<<13); ++ ++ } else if(dst == YCBYCR) { ++ tmp |= (0x1<<13); ++ tmp &= ~(0x1<<18)|(0x1<<17); ++ } else { ++ } ++ } ++ ++ __raw_writel(tmp, base + S3C_MODE); ++} ++ ++static void s3c_tvscaler_set_path(s3c_scaler_path_t in, s3c_scaler_path_t out) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ tmp &=~(0x1<<12); // 0: progressive mode, 1: interlace mode ++ ++ if(in == POST_FIFO) { ++ tmp |= (0x1<<31); ++ } else if(in == POST_DMA) { ++ tmp &=~(0x1<<31); ++ } ++ ++ if(out == POST_FIFO) { ++ tmp |= (0x1<<13); ++ } else if(out == POST_DMA) { ++ tmp &=~(0x1<<13); ++ } ++ ++ __raw_writel(tmp, base + S3C_MODE); ++} ++ ++static void s3c_tvscaler_set_addr(scaler_params_t *sp, u32 in_pixel_size, u32 out_pixel_size) ++{ ++ u32 offset_y, offset_cb, offset_cr; ++ u32 src_start_y, src_start_cb, src_start_cr; ++ u32 src_end_y, src_end_cb, src_end_cr; ++ u32 start_pos_y, end_pos_y; ++ u32 start_pos_cb, end_pos_cb; ++ u32 start_pos_cr, end_pos_cr; ++ u32 start_pos_rgb, end_pos_rgb; ++ u32 dst_start_rgb, dst_end_rgb; ++ u32 src_frm_start_addr; ++ ++ u32 offset_rgb, out_offset_cb, out_offset_cr; ++ u32 out_start_pos_cb, out_start_pos_cr; ++ u32 out_end_pos_cb, out_end_pos_cr; ++ u32 out_src_start_cb, out_src_start_cr; ++ u32 out_src_end_cb, out_src_end_cr; ++ ++ if(sp->InPath == POST_DMA) { ++ offset_y = (sp->SrcFullWidth - sp->SrcWidth) * in_pixel_size; ++ start_pos_y = (sp->SrcFullWidth*sp->SrcStartY+sp->SrcStartX)*in_pixel_size; ++ end_pos_y = sp->SrcWidth*sp->SrcHeight*in_pixel_size + offset_y*(sp->SrcHeight-1); ++ src_frm_start_addr = sp->SrcFrmSt; ++ src_start_y = sp->SrcFrmSt + start_pos_y; ++ src_end_y = src_start_y + end_pos_y; ++ ++ __raw_writel(src_start_y, base + S3C_ADDRSTART_Y); ++ __raw_writel(offset_y, base + S3C_OFFSET_Y); ++ __raw_writel(src_end_y, base + S3C_ADDREND_Y); ++ ++ if(sp->SrcCSpace == YC420) { ++ offset_cb = offset_cr = ((sp->SrcFullWidth - sp->SrcWidth) / 2) * in_pixel_size; ++ start_pos_cb = sp->SrcFullWidth * sp->SrcFullHeight * 1 \ ++ + (sp->SrcFullWidth * sp->SrcStartY / 2 + sp->SrcStartX) /2 * 1; ++ ++ end_pos_cb = sp->SrcWidth/2*sp->SrcHeight/2*in_pixel_size \ ++ + (sp->SrcHeight/2 -1)*offset_cb; ++ start_pos_cr = sp->SrcFullWidth * sp->SrcFullHeight *1 \ ++ + sp->SrcFullWidth*sp->SrcFullHeight/4 *1 \ ++ + (sp->SrcFullWidth*sp->SrcStartY/2 + sp->SrcStartX)/2*1; ++ end_pos_cr = sp->SrcWidth/2*sp->SrcHeight/2*in_pixel_size \ ++ + (sp->SrcHeight/2-1)*offset_cr; ++ ++ src_start_cb = sp->SrcFrmSt + start_pos_cb; ++ src_end_cb = src_start_cb + end_pos_cb; ++ ++ src_start_cr = sp->SrcFrmSt + start_pos_cr; ++ src_end_cr = src_start_cr + end_pos_cr; ++ ++ __raw_writel(src_start_cb, base + S3C_ADDRSTART_CB); ++ __raw_writel(offset_cr, base + S3C_OFFSET_CB); ++ __raw_writel(src_end_cb, base + S3C_ADDREND_CB); ++ __raw_writel(src_start_cr, base + S3C_ADDRSTART_CR); ++ __raw_writel(offset_cb, base + S3C_OFFSET_CR); ++ __raw_writel(src_end_cr, base + S3C_ADDREND_CR); ++ } ++ } ++ if(sp->OutPath == POST_DMA) { ++ offset_rgb = (sp->DstFullWidth - sp->DstWidth)*out_pixel_size; ++ start_pos_rgb = (sp->DstFullWidth*sp->DstStartY + sp->DstStartX)*out_pixel_size; ++ end_pos_rgb = sp->DstWidth*sp->DstHeight*out_pixel_size + offset_rgb*(sp->DstHeight - 1); ++ dst_start_rgb = sp->DstFrmSt + start_pos_rgb; ++ dst_end_rgb = dst_start_rgb + end_pos_rgb; ++ ++ __raw_writel(dst_start_rgb, base + S3C_ADDRSTART_RGB); ++ __raw_writel(offset_rgb, base + S3C_OFFSET_RGB); ++ __raw_writel(dst_end_rgb, base + S3C_ADDREND_RGB); ++ ++ if(sp->DstCSpace == YC420) { ++ out_offset_cb = out_offset_cr = ((sp->DstFullWidth - sp->DstWidth)/2)*out_pixel_size; ++ out_start_pos_cb = sp->DstFullWidth*sp->DstFullHeight*1 \ ++ + (sp->DstFullWidth*sp->DstStartY/2 + sp->DstStartX)/2*1; ++ out_end_pos_cb = sp->DstWidth/2*sp->DstHeight/2*out_pixel_size \ ++ + (sp->DstHeight/2 -1)*out_offset_cr; ++ ++ out_start_pos_cr = sp->DstFullWidth*sp->DstFullHeight*1 \ ++ + (sp->DstFullWidth*sp->DstFullHeight/4)*1 \ ++ + (sp->DstFullWidth*sp->DstStartY/2 +sp->DstStartX)/2*1; ++ out_end_pos_cr = sp->DstWidth/2*sp->DstHeight/2*out_pixel_size \ ++ + (sp->DstHeight/2 -1)*out_offset_cb; ++ ++ out_src_start_cb = sp->DstFrmSt + out_start_pos_cb; ++ out_src_end_cb = out_src_start_cb + out_end_pos_cb; ++ out_src_start_cr = sp->DstFrmSt + out_start_pos_cr; ++ out_src_end_cr = out_src_start_cr + out_end_pos_cr; ++ ++ __raw_writel(out_src_start_cb, base + S3C_ADDRSTART_OCB); ++ __raw_writel(out_offset_cb, base + S3C_OFFSET_OCB); ++ __raw_writel(out_src_end_cb, base + S3C_ADDREND_OCB); ++ __raw_writel(out_src_start_cr, base + S3C_ADDRSTART_OCR); ++ __raw_writel(out_offset_cr, base + S3C_OFFSET_OCR); ++ __raw_writel(out_src_end_cr, base + S3C_ADDREND_OCR); ++ ++ } ++ } ++ ++ ++} ++ ++#if 0 ++static void s3c_tvscaler_set_fifo_in(s3c_scaler_path_t in_path) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ if(in_path == POST_FIFO) tmp |= (0x1<<31); ++ else tmp &=~(0x1<<31); ++ ++ __raw_writel(tmp, base + S3C_MODE); ++ ++} ++#endif ++ ++void s3c_tvscaler_set_interlace(u32 on_off) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ if(on_off == 1) tmp |=(1<<12); ++ else tmp &=~(1<<12); ++ ++ __raw_writel(tmp, base + S3C_MODE); ++} ++EXPORT_SYMBOL(s3c_tvscaler_set_interlace); ++ ++static void s3c_tvscaler_set_size(scaler_params_t *sp) ++{ ++ u32 pre_h_ratio, pre_v_ratio, h_shift, v_shift, sh_factor; ++ u32 pre_dst_width, pre_dst_height, dx, dy; ++ ++ if (sp->SrcWidth >= (sp->DstWidth<<6)) { ++ printk("Out of PreScalar range !!!\n"); ++ return; ++ } ++ if(sp->SrcWidth >= (sp->DstWidth<<5)) { ++ pre_h_ratio = 32; ++ h_shift = 5; ++ } else if(sp->SrcWidth >= (sp->DstWidth<<4)) { ++ pre_h_ratio = 16; ++ h_shift = 4; ++ } else if(sp->SrcWidth >= (sp->DstWidth<<3)) { ++ pre_h_ratio = 8; ++ h_shift = 3; ++ } else if(sp->SrcWidth >= (sp->DstWidth<<2)) { ++ pre_h_ratio = 4; ++ h_shift = 2; ++ } else if(sp->SrcWidth >= (sp->DstWidth<<1)) { ++ pre_h_ratio = 2; ++ h_shift = 1; ++ } else { ++ pre_h_ratio = 1; ++ h_shift = 0; ++ } ++ ++ pre_dst_width = sp->SrcWidth / pre_h_ratio; ++ dx = (sp->SrcWidth<<8) / (sp->DstWidth<<h_shift); ++ ++ ++ if (sp->SrcHeight >= (sp->DstHeight<<6)) { ++ printk("Out of PreScalar range !!!\n"); ++ return; ++ } ++ if(sp->SrcHeight>= (sp->DstHeight<<5)) { ++ pre_v_ratio = 32; ++ v_shift = 5; ++ } else if(sp->SrcHeight >= (sp->DstHeight<<4)) { ++ pre_v_ratio = 16; ++ v_shift = 4; ++ } else if(sp->SrcHeight >= (sp->DstHeight<<3)) { ++ pre_v_ratio = 8; ++ v_shift = 3; ++ } else if(sp->SrcHeight >= (sp->DstHeight<<2)) { ++ pre_v_ratio = 4; ++ v_shift = 2; ++ } else if(sp->SrcHeight >= (sp->DstHeight<<1)) { ++ pre_v_ratio = 2; ++ v_shift = 1; ++ } else { ++ pre_v_ratio = 1; ++ v_shift = 0; ++ } ++ ++ pre_dst_height = sp->SrcHeight / pre_v_ratio; ++ dy = (sp->SrcHeight<<8) / (sp->DstHeight<<v_shift); ++ sh_factor = 10 - (h_shift + v_shift); ++ ++ __raw_writel((pre_v_ratio<<7)|(pre_h_ratio<<0), base + S3C_PRESCALE_RATIO); ++ __raw_writel((pre_dst_height<<12)|(pre_dst_width<<0), base + S3C_PRESCALEIMGSIZE); ++ __raw_writel(sh_factor, base + S3C_PRESCALE_SHFACTOR); ++ __raw_writel(dx, base + S3C_MAINSCALE_H_RATIO); ++ __raw_writel(dy, base + S3C_MAINSCALE_V_RATIO); ++ __raw_writel((sp->SrcHeight<<12)|(sp->SrcWidth), base + S3C_SRCIMGSIZE); ++ __raw_writel((sp->DstHeight<<12)|(sp->DstWidth), base + S3C_DSTIMGSIZE); ++ ++} ++ ++ ++static void s3c_tvscaler_set_auto_load(scaler_params_t *sp) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ if(sp->Mode == FREE_RUN) { ++ tmp |= (1<<14); ++ } else if(sp->Mode == ONE_SHOT) { ++ tmp &=~(1<<14); ++ } ++ ++ __raw_writel(tmp, base + S3C_MODE); ++ ++} ++ ++void s3c_tvscaler_set_base_addr(void __iomem * base_addr) ++{ ++ base = base_addr; ++} ++EXPORT_SYMBOL(s3c_tvscaler_set_base_addr); ++ ++void s3c_tvscaler_free_base_addr(void) ++{ ++ base = NULL; ++} ++EXPORT_SYMBOL(s3c_tvscaler_free_base_addr); ++ ++void s3c_tvscaler_int_enable(u32 int_type) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ if(int_type == 0) { //Edge triggering ++ tmp &= ~(S3C_MODE_IRQ_LEVEL); ++ } else if(int_type == 1) { //level triggering ++ tmp |= S3C_MODE_IRQ_LEVEL; ++ } ++ ++ tmp |= S3C_MODE_POST_INT_ENABLE; ++ ++ __raw_writel(tmp, base + S3C_MODE); ++} ++EXPORT_SYMBOL(s3c_tvscaler_int_enable); ++ ++void s3c_tvscaler_int_disable(void) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ tmp &=~ (S3C_MODE_POST_INT_ENABLE); ++ ++ __raw_writel(tmp, base + S3C_MODE); ++ ++} ++EXPORT_SYMBOL(s3c_tvscaler_int_disable); ++ ++ ++void s3c_tvscaler_start(void) ++{ ++ __raw_writel(S3C_POSTENVID_ENABLE, base + S3C_POSTENVID); ++ ++} ++EXPORT_SYMBOL(s3c_tvscaler_start); ++ ++void s3c_tvscaler_stop_freerun(void) ++{ ++ u32 tmp; ++ ++ tmp = __raw_readl(base + S3C_MODE); ++ ++ tmp &=~(1<<14); ++ ++ __raw_writel(tmp, base + S3C_MODE); ++} ++EXPORT_SYMBOL(s3c_tvscaler_stop_freerun); ++ ++ ++void s3c_tvscaler_config(scaler_params_t *sp) ++{ ++ u32 tmp = 0; ++ u32 in_pixel_size = 0; ++ u32 out_pixel_size = 0; ++ u32 loop = 0; ++ ++ tmp = __raw_readl(base + S3C_POSTENVID); ++ tmp &= ~S3C_POSTENVID_ENABLE; ++ __raw_writel(tmp, base + S3C_POSTENVID); ++#ifdef SINGLE_BUF ++ tmp = S3C_MODE2_ADDR_CHANGE_DISABLE |S3C_MODE2_CHANGE_AT_FRAME_END |S3C_MODE2_SOFTWARE_TRIGGER; ++#else ++ tmp = S3C_MODE2_ADDR_CHANGE_ENABLE |S3C_MODE2_CHANGE_AT_FRAME_END |S3C_MODE2_SOFTWARE_TRIGGER; ++#endif ++ __raw_writel(tmp, base + S3C_MODE2); ++ ++// peter mod. start ++ sp->DstStartX = sp->DstStartY = 0; ++ sp->DstWidth = sp->DstFullWidth; ++ sp->DstHeight = sp->DstFullHeight; ++// peter mod. end ++ ++ sp->DstFrmSt = ( POST_BUFF_BASE_ADDR + PRE_BUFF_SIZE ); ++ //printk("\n---peter s3c_tvscaler_config : SrcFrmSt = 0x%08x\n", sp->SrcFrmSt); ++ //printk("---peter s3c_tvscaler_config : DstFrmSt = 0x%08x\n", sp->DstFrmSt); ++ ++ s3c_tvscaler_set_clk_src(HCLK); ++ ++ s3c_tvscaler_set_path(sp->InPath, sp->OutPath); ++ ++ s3c_tvscaler_set_fmt(sp->SrcCSpace, sp->DstCSpace, sp->InPath, ++ sp->OutPath, &in_pixel_size, &out_pixel_size); ++ ++ s3c_tvscaler_set_size(sp); ++ ++ s3c_tvscaler_set_addr(sp, in_pixel_size, out_pixel_size); ++ ++ s3c_tvscaler_set_auto_load(sp); ++ ++} ++EXPORT_SYMBOL(s3c_tvscaler_config); ++ ++void s3c_tvscaler_set_param(scaler_params_t *sp) ++{ ++#if 0 ++ param.SrcFullWidth = sp->SrcFullWidth; ++ param.SrcFullHeight = sp->SrcFullHeight; ++ param.SrcStartX = sp->SrcStartX; ++ param.SrcStartY = sp->SrcStartY; ++ param.SrcWidth = sp->SrcWidth; ++ param.SrcHeight = sp->SrcHeight; ++ param.SrcFrmSt = sp->SrcFrmSt; ++ param.SrcCSpace = sp->SrcCSpace; ++ param.DstFullWidth = sp->DstFullWidth; ++ param.DstFullHeight = sp->DstFullHeight; ++ param.DstStartX = sp->DstStartX; ++ param.DstStartY = sp->DstStartY; ++ param.DstWidth = sp->DstWidth; ++ param.DstHeight = sp->DstHeight; ++ param.DstFrmSt = sp->DstFrmSt; ++ param.DstCSpace = sp->DstCSpace; ++ param.SrcFrmBufNum = sp->SrcFrmBufNum; ++ param.DstFrmSt = sp->DstFrmSt; ++ param.Mode = sp->Mode; ++ param.InPath = sp->InPath; ++ param.OutPath = sp->OutPath; ++#endif ++} ++EXPORT_SYMBOL(s3c_tvscaler_set_param); ++ ++void s3c_tvscaler_init(void) ++{ ++ ++ int tmp; ++ ++ // Use DOUTmpll source clock as a scaler clock ++ tmp = __raw_readl(S3C_CLK_SRC); ++ ++ tmp &=~(0x3<<28); ++ tmp |= (0x1<<28); ++ __raw_writel(tmp, S3C_CLK_SRC); ++ ++ printk(" %s \n", __FUNCTION__); ++ ++} ++EXPORT_SYMBOL(s3c_tvscaler_init); ++ ++ ++static int s3c_tvscaler_probe(struct platform_device *pdev) ++{ ++ ++ struct resource *res; ++ ++ int ret; ++ ++ /* find the IRQs */ ++ s3c_tvscaler_irq = platform_get_irq(pdev, 0); ++ if(s3c_tvscaler_irq <= 0) { ++ printk(KERN_ERR PFX "failed to get irq resouce\n"); ++ return -ENOENT; ++ } ++ ++ /* get the memory region for the tv scaler driver */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if(res == NULL) { ++ printk(KERN_ERR PFX "failed to get memory region resouce\n"); ++ return -ENOENT; ++ } ++ ++ s3c_tvscaler_mem = request_mem_region(res->start, res->end-res->start+1, pdev->name); ++ if(s3c_tvscaler_mem == NULL) { ++ printk(KERN_ERR PFX "failed to reserve memory region\n"); ++ return -ENOENT; ++ } ++ ++ base = ioremap(s3c_tvscaler_mem->start, s3c_tvscaler_mem->end - res->start + 1); ++ if(s3c_tvscaler_mem == NULL) { ++ printk(KERN_ERR PFX "failed ioremap\n"); ++ return -ENOENT; ++ } ++ ++ tvscaler_clock = clk_get(&pdev->dev, "tv_encoder"); ++ if(tvscaler_clock == NULL) { ++ printk(KERN_ERR PFX "failed to find tvscaler clock source\n"); ++ return -ENOENT; ++ } ++ ++ clk_enable(tvscaler_clock); ++ ++ h_clk = clk_get(&pdev->dev, "hclk"); ++ if(h_clk == NULL) { ++ printk(KERN_ERR PFX "failed to find h_clk clock source\n"); ++ return -ENOENT; ++ } ++ ++ init_waitqueue_head(&waitq); ++ ++ ret = request_irq(s3c_tvscaler_irq, s3c_tvscaler_isr, SA_INTERRUPT, ++ "TV_SCALER", NULL); ++ if (ret) { ++ printk("request_irq(TV_SCALER) failed.\n"); ++ return ret; ++ } ++ ++ printk(" Success\n"); ++ ++ return 0; ++} ++ ++static int s3c_tvscaler_remove(struct platform_device *dev) ++{ ++ printk(KERN_INFO "s3c_tvscaler_remove called !\n"); ++ clk_disable(tvscaler_clock); ++ free_irq(s3c_tvscaler_irq, NULL); ++ if (s3c_tvscaler_mem != NULL) { ++ pr_debug("s3-tvscaler: releasing s3c_tvscaler_mem\n"); ++ iounmap(base); ++ release_resource(s3c_tvscaler_mem); ++ kfree(s3c_tvscaler_mem); ++ } ++ ++ return 0; ++} ++ ++static int s3c_tvscaler_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ clk_disable(tvscaler_clock); ++ return 0; ++} ++ ++static int s3c_tvscaler_resume(struct platform_device *pdev) ++{ ++ clk_enable(tvscaler_clock); ++ return 0; ++} ++ ++static struct platform_driver s3c_tvscaler_driver = { ++ .probe = s3c_tvscaler_probe, ++ .remove = s3c_tvscaler_remove, ++ .suspend = s3c_tvscaler_suspend, ++ .resume = s3c_tvscaler_resume, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "s3c-tvscaler", ++ }, ++}; ++ ++static char banner[] __initdata = KERN_INFO "S3C6410 TV scaler Driver, (c) 2008 Samsung Electronics\n"; ++ ++ ++int __init s3c_tvscaler_pre_init(void) ++{ ++ ++ printk(banner); ++ ++ if(platform_driver_register(&s3c_tvscaler_driver) != 0) ++ { ++ printk("platform device register Failed \n"); ++ return -1; ++ } ++ ++ printk(" S3C6410 TV scaler Driver module init OK. \n"); ++ ++ return 0; ++} ++ ++void s3c_tvscaler_exit(void) ++{ ++ platform_driver_unregister(&s3c_tvscaler_driver); ++ printk("S3C: tvscaler module exit\n"); ++} ++ ++module_init(s3c_tvscaler_pre_init); ++module_exit(s3c_tvscaler_exit); ++ ++ ++MODULE_AUTHOR("Peter, Oh"); ++MODULE_DESCRIPTION("S3C TV Controller Device Driver"); ++MODULE_LICENSE("GPL"); ++ ++ +diff --git a/drivers/media/video/samsung/s3c-tvscaler.h b/drivers/media/video/samsung/s3c-tvscaler.h +new file mode 100644 +index 0000000..d8079a3 +--- /dev/null ++++ b/drivers/media/video/samsung/s3c-tvscaler.h +@@ -0,0 +1,96 @@ ++#ifndef __S3CTVSCALER_H_ ++#define __S3CTVSCALER_H_ ++ ++#include <asm-arm/arch-s3c2410/reserved_mem.h> ++ ++#define TVSCALER_IOCTL_MAGIC 'S' ++ ++#define PPROC_SET_PARAMS _IO(TVSCALER_IOCTL_MAGIC, 0) ++#define PPROC_START _IO(TVSCALER_IOCTL_MAGIC, 1) ++#define PPROC_STOP _IO(TVSCALER_IOCTL_MAGIC, 2) ++#define PPROC_INTERLACE_MODE _IO(TVSCALER_IOCTL_MAGIC, 3) ++#define PPROC_PROGRESSIVE_MODE _IO(TVSCALER_IOCTL_MAGIC, 4) ++ ++ ++#define QVGA_XSIZE 320 ++#define QVGA_YSIZE 240 ++ ++#define LCD_XSIZE 320 ++#define LCD_YSIZE 240 ++ ++#define SCALER_MINOR 251 // Just some number ++ ++ ++//#define SYSTEM_RAM 0x08000000 // 128mb ++#define SYSTEM_RAM 0x07800000 // 120mb ++#define RESERVE_POST_MEM 8*1024*1024 // 8mb ++#define PRE_BUFF_SIZE 4*1024*1024 //4 // 4mb ++#define POST_BUFF_SIZE ( RESERVE_POST_MEM - PRE_BUFF_SIZE ) ++#if 0 ++#define POST_BUFF_BASE_ADDR (0x50000000 + (SYSTEM_RAM - RESERVE_POST_MEM)) ++#else // TV_RESERVED_MEM_START is defined in the s3c-linux-2.6.21_dev_4_4_15 ++#define POST_BUFF_BASE_ADDR TV_RESERVED_MEM_START ++#endif ++ ++#define USE_DEDICATED_MEM 1 ++ ++typedef enum { ++ INTERLACE_MODE, ++ PROGRESSIVE_MODE ++} s3c_scaler_scan_mode_t; ++ ++typedef enum { ++ POST_DMA, POST_FIFO ++} s3c_scaler_path_t; ++ ++typedef enum { ++ ONE_SHOT, FREE_RUN ++} s3c_scaler_run_mode_t; ++ ++typedef enum { ++ PAL1, PAL2, PAL4, PAL8, ++ RGB8, ARGB8, RGB16, ARGB16, RGB18, RGB24, RGB30, ARGB24, ++ YC420, YC422, // Non-interleave ++ CRYCBY, CBYCRY, YCRYCB, YCBYCR, YUV444 // Interleave ++} cspace_t; ++ ++typedef enum ++{ ++ HCLK = 0, PLL_EXT = 1, EXT_27MHZ = 3 ++} scaler_clk_src_t; ++ ++typedef struct{ ++ unsigned int SrcFullWidth; // Source Image Full Width(Virtual screen size) ++ unsigned int SrcFullHeight; // Source Image Full Height(Virtual screen size) ++ unsigned int SrcStartX; // Source Image Start width offset ++ unsigned int SrcStartY; // Source Image Start height offset ++ unsigned int SrcWidth; // Source Image Width ++ unsigned int SrcHeight; // Source Image Height ++ unsigned int SrcFrmSt; // Base Address of the Source Image : Physical Address ++ cspace_t SrcCSpace; // Color Space ot the Source Image ++ ++ unsigned int DstFullWidth; // Source Image Full Width(Virtual screen size) ++ unsigned int DstFullHeight; // Source Image Full Height(Virtual screen size) ++ unsigned int DstStartX; // Source Image Start width offset ++ unsigned int DstStartY; // Source Image Start height offset ++ unsigned int DstWidth; // Source Image Width ++ unsigned int DstHeight; // Source Image Height ++ unsigned int DstFrmSt; // Base Address of the Source Image : Physical Address ++ cspace_t DstCSpace; // Color Space ot the Source Image ++ ++ unsigned int SrcFrmBufNum; // Frame buffer number ++ s3c_scaler_run_mode_t Mode; // POST running mode(PER_FRAME or FREE_RUN) ++ s3c_scaler_path_t InPath; // Data path of the source image ++ s3c_scaler_path_t OutPath; // Data path of the desitination image ++ ++}scaler_params_t; ++ ++typedef struct{ ++ unsigned int pre_phy_addr; ++ unsigned char *pre_virt_addr; ++ ++ unsigned int post_phy_addr; ++ unsigned char *post_virt_addr; ++} buff_addr_t; ++ ++#endif //__S3CTVSCALER_H_ +-- +1.6.2.4 + |