summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWojciech Bieganski <wbieganski@antmicro.com>2014-03-04 16:05:13 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2014-03-13 08:07:37 +0100
commit58e5e10f28230fc9a9815b73948a6678e886550a (patch)
tree28e72b5f1043b71834e1e723548b81f20e66438f
parent04ffb571ea3747980fca1b5d3295a7dcd7a77934 (diff)
OV7670 and TVP5150 drivers added, updated configs
-rw-r--r--arch/arm/configs/apalis_t30_defconfig2
-rw-r--r--arch/arm/mach-tegra/board-apalis_t30.c48
-rw-r--r--arch/arm/mach-tegra/board-colibri_t20.c47
-rw-r--r--arch/arm/mach-tegra/board-colibri_t30.c48
-rw-r--r--drivers/media/video/Kconfig12
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/ov7670soc.c746
-rw-r--r--drivers/media/video/tvp5150_reg.h3
-rw-r--r--drivers/media/video/tvp5150soc.c538
9 files changed, 1446 insertions, 0 deletions
diff --git a/arch/arm/configs/apalis_t30_defconfig b/arch/arm/configs/apalis_t30_defconfig
index d1c2e57384a9..56a68d00cd95 100644
--- a/arch/arm/configs/apalis_t30_defconfig
+++ b/arch/arm/configs/apalis_t30_defconfig
@@ -275,6 +275,8 @@ CONFIG_TEGRA_NVAVP=y
CONFIG_VIDEO_ADV7180=m
CONFIG_SOC_CAMERA=y
CONFIG_SOC_CAMERA_MAX9526=m
+CONFIG_SOC_CAMERA_OV7670SOC=m
+CONFIG_SOC_CAMERA_TVP5150=m
CONFIG_VIDEO_TEGRA=m
CONFIG_USB_VIDEO_CLASS=y
# CONFIG_RADIO_ADAPTERS is not set
diff --git a/arch/arm/mach-tegra/board-apalis_t30.c b/arch/arm/mach-tegra/board-apalis_t30.c
index f7340b91eb8d..6b2a39e7bcce 100644
--- a/arch/arm/mach-tegra/board-apalis_t30.c
+++ b/arch/arm/mach-tegra/board-apalis_t30.c
@@ -158,6 +158,48 @@ static struct platform_device soc_camera_adv7180 = {
},
};
#endif /* CONFIG_VIDEO_ADV7180 | CONFIG_VIDEO_ADV7180_MODULE */
+
+#if defined(CONFIG_SOC_CAMERA_TVP5150) || defined(CONFIG_SOC_CAMERA_TVP5150_MODULE)
+static struct i2c_board_info camera_i2c_tvp5150soc = {
+ I2C_BOARD_INFO("tvp5150soc", 0x5d),
+};
+
+static struct soc_camera_link iclink_tvp5150soc = {
+ .board_info = &camera_i2c_tvp5150soc,
+ .bus_id = -1, /* This must match the .id of tegra_vi01_device */
+ .i2c_adapter_id = 2,
+};
+
+static struct platform_device soc_camera_tvp5150soc = {
+ .name = "soc-camera-pdrv",
+ .id = 2,
+ .dev = {
+ .platform_data = &iclink_tvp5150soc,
+ },
+};
+#endif /* CONFIG_SOC_CAMERA_TVP5150 | CONFIG_SOC_CAMERA_TVP5150_MODULE */
+
+#if defined(CONFIG_SOC_CAMERA_OV7670SOC) || defined(CONFIG_SOC_CAMERA_OV7670SOC_MODULE)
+static struct i2c_board_info camera_i2c_ov7670soc = {
+ I2C_BOARD_INFO("ov7670soc", 0x21),
+};
+
+static struct soc_camera_link iclink_ov7670soc = {
+ .board_info = &camera_i2c_ov7670soc,
+ .bus_id = -1, /* This must match the .id of tegra_vi01_device */
+ .i2c_adapter_id = 2,
+};
+
+static struct platform_device soc_camera_ov7670soc = {
+ .name = "soc-camera-pdrv",
+ .id = 3,
+ .dev = {
+ .platform_data = &iclink_ov7670soc,
+ },
+};
+
+#endif /* CONFIG_SOC_CAMERA_OV7670SOC | CONFIG_SOC_CAMERA_OV7670SOC_MODULE */
+
#endif /* CONFIG_VIDEO_TEGRA | CONFIG_VIDEO_TEGRA_MODULE */
/* CAN */
@@ -1392,6 +1434,12 @@ static void __init apalis_t30_init(void)
#if defined(CONFIG_VIDEO_ADV7180) || defined(CONFIG_VIDEO_ADV7180_MODULE)
platform_device_register(&soc_camera_adv7180);
#endif
+#if defined(CONFIG_SOC_CAMERA_TVP5150) || defined(CONFIG_SOC_CAMERA_TVP5150_MODULE)
+ platform_device_register(&soc_camera_tvp5150soc);
+#endif
+#if defined(CONFIG_SOC_CAMERA_OV7670SOC) || defined(CONFIG_SOC_CAMERA_OV7670SOC_MODULE)
+ platform_device_register(&soc_camera_ov7670soc);
+#endif
#endif /* CONFIG_VIDEO_TEGRA | CONFIG_VIDEO_TEGRA_MODULE */
tegra_release_bootloader_fb();
diff --git a/arch/arm/mach-tegra/board-colibri_t20.c b/arch/arm/mach-tegra/board-colibri_t20.c
index 470cc004165a..e8bb48e27865 100644
--- a/arch/arm/mach-tegra/board-colibri_t20.c
+++ b/arch/arm/mach-tegra/board-colibri_t20.c
@@ -162,6 +162,47 @@ static struct platform_device soc_camera_adv7180 = {
},
};
#endif /* CONFIG_VIDEO_ADV7180 | CONFIG_VIDEO_ADV7180_MODULE */
+
+#if defined(CONFIG_SOC_CAMERA_TVP5150) || defined(CONFIG_SOC_CAMERA_TVP5150_MODULE)
+static struct i2c_board_info camera_i2c_tvp5150soc = {
+ I2C_BOARD_INFO("tvp5150soc", 0x5d),
+};
+
+static struct soc_camera_link iclink_tvp5150soc = {
+ .board_info = &camera_i2c_tvp5150soc,
+ .bus_id = -1, /* This must match the .id of tegra_vi01_device */
+ .i2c_adapter_id = 0,
+};
+
+static struct platform_device soc_camera_tvp5150soc = {
+ .name = "soc-camera-pdrv",
+ .id = 2,
+ .dev = {
+ .platform_data = &iclink_tvp5150soc,
+ },
+};
+#endif /* CONFIG_SOC_CAMERA_TVP5150 | CONFIG_SOC_CAMERA_TVP5150_MODULE */
+
+#if defined(CONFIG_SOC_CAMERA_OV7670SOC) || defined(CONFIG_SOC_CAMERA_OV7670SOC_MODULE)
+static struct i2c_board_info camera_i2c_ov7670soc = {
+ I2C_BOARD_INFO("ov7670soc", 0x21),
+};
+
+static struct soc_camera_link iclink_ov7670soc = {
+ .board_info = &camera_i2c_ov7670soc,
+ .bus_id = -1, /* This must match the .id of tegra_vi01_device */
+ .i2c_adapter_id = 0,
+};
+
+static struct platform_device soc_camera_ov7670soc = {
+ .name = "soc-camera-pdrv",
+ .id = 3,
+ .dev = {
+ .platform_data = &iclink_ov7670soc,
+ },
+};
+#endif /* CONFIG_SOC_CAMERA_OV7670SOC | CONFIG_SOC_CAMERA_OV7670SOC_MODULE */
+
#endif /* CONFIG_VIDEO_TEGRA | CONFIG_VIDEO_TEGRA_MODULE */
/* CAN */
@@ -1531,6 +1572,12 @@ static void __init colibri_t20_init(void)
#if defined(CONFIG_VIDEO_ADV7180) || defined(CONFIG_VIDEO_ADV7180_MODULE)
platform_device_register(&soc_camera_adv7180);
#endif
+#if defined(CONFIG_SOC_CAMERA_TVP5150) || defined(CONFIG_SOC_CAMERA_TVP5150_MODULE)
+ platform_device_register(&soc_camera_tvp5150soc);
+#endif
+#if defined(CONFIG_SOC_CAMERA_OV7670SOC) || defined(CONFIG_SOC_CAMERA_OV7670SOC_MODULE)
+ platform_device_register(&soc_camera_ov7670soc);
+#endif
#endif /* CONFIG_VIDEO_TEGRA | CONFIG_VIDEO_TEGRA_MODULE */
diff --git a/arch/arm/mach-tegra/board-colibri_t30.c b/arch/arm/mach-tegra/board-colibri_t30.c
index db3ab08cbc3d..c35f2737f9df 100644
--- a/arch/arm/mach-tegra/board-colibri_t30.c
+++ b/arch/arm/mach-tegra/board-colibri_t30.c
@@ -158,6 +158,47 @@ static struct platform_device soc_camera_adv7180 = {
},
};
#endif /* CONFIG_VIDEO_ADV7180 | CONFIG_VIDEO_ADV7180_MODULE */
+
+#if defined(CONFIG_SOC_CAMERA_TVP5150) || defined(CONFIG_SOC_CAMERA_TVP5150_MODULE)
+static struct i2c_board_info camera_i2c_tvp5150soc = {
+ I2C_BOARD_INFO("tvp5150soc", 0x5d),
+};
+
+static struct soc_camera_link iclink_tvp5150soc = {
+ .board_info = &camera_i2c_tvp5150soc,
+ .bus_id = -1, /* This must match the .id of tegra_vi01_device */
+ .i2c_adapter_id = 0,
+};
+
+static struct platform_device soc_camera_tvp5150soc = {
+ .name = "soc-camera-pdrv",
+ .id = 2,
+ .dev = {
+ .platform_data = &iclink_tvp5150soc,
+ },
+};
+#endif /* CONFIG_SOC_CAMERA_TVP5150 | CONFIG_SOC_CAMERA_TVP5150_MODULE */
+
+#if defined(CONFIG_SOC_CAMERA_OV7670SOC) || defined(CONFIG_SOC_CAMERA_OV7670SOC_MODULE)
+static struct i2c_board_info camera_i2c_ov7670soc = {
+ I2C_BOARD_INFO("ov7670soc", 0x21),
+};
+
+static struct soc_camera_link iclink_ov7670soc = {
+ .board_info = &camera_i2c_ov7670soc,
+ .bus_id = -1, /* This must match the .id of tegra_vi01_device */
+ .i2c_adapter_id = 0,
+};
+
+static struct platform_device soc_camera_ov7670soc = {
+ .name = "soc-camera-pdrv",
+ .id = 3,
+ .dev = {
+ .platform_data = &iclink_ov7670soc,
+ },
+};
+#endif /* CONFIG_SOC_CAMERA_OV7670SOC | CONFIG_SOC_CAMERA_OV7670SOC_MODULE */
+
#endif /* CONFIG_VIDEO_TEGRA | CONFIG_VIDEO_TEGRA_MODULE */
/* CAN */
@@ -281,6 +322,7 @@ static struct tegra_clk_init_table colibri_t30_clk_init_table[] __initdata = {
{"spdif_out", "pll_a_out0", 0, false},
{"vi", "pll_p", 0, false},
{"vi_sensor", "pll_p", 150000000, false},
+ {"clk_out_2", "extern2", 24000000, false},
{NULL, NULL, 0, 0},
};
@@ -1511,6 +1553,12 @@ static void __init colibri_t30_init(void)
#if defined(CONFIG_VIDEO_ADV7180) || defined(CONFIG_VIDEO_ADV7180_MODULE)
platform_device_register(&soc_camera_adv7180);
#endif
+#if defined(CONFIG_SOC_CAMERA_TVP5150) || defined(CONFIG_SOC_CAMERA_TVP5150_MODULE)
+ platform_device_register(&soc_camera_tvp5150soc);
+#endif
+#if defined(CONFIG_SOC_CAMERA_OV7670SOC) || defined(CONFIG_SOC_CAMERA_OV7670SOC_MODULE)
+ platform_device_register(&soc_camera_ov7670soc);
+#endif
#endif /* CONFIG_VIDEO_TEGRA | CONFIG_VIDEO_TEGRA_MODULE */
tegra_release_bootloader_fb();
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 0fe628f5912d..9f8b400886e4 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -884,6 +884,12 @@ config SOC_CAMERA_OV6650
---help---
This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
+config SOC_CAMERA_OV7670SOC
+ tristate "ov7670soc sensor support (NEW)"
+ depends on SOC_CAMERA && I2C
+ ---help---
+ This is a V4L2 SoC camera driver for the OmniVision OV7670 sensor
+
config SOC_CAMERA_OV772X
tristate "ov772x camera support"
depends on SOC_CAMERA && I2C
@@ -902,6 +908,12 @@ config SOC_CAMERA_OV9740
help
This is a ov9740 camera driver
+config SOC_CAMERA_TVP5150
+ tristate "tvp5150soc support (NEW)"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a V4L2 SoC camera driver for the Texas Instruments TVP5150 chip
+
config MX1_VIDEO
bool
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 28362059e105..fecfe0fd1075 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -84,10 +84,12 @@ obj-$(CONFIG_SOC_CAMERA_OV5640) += ov5640.o
obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o
obj-$(CONFIG_SOC_CAMERA_OV5650) += ov5650.o
obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o
+obj-$(CONFIG_SOC_CAMERA_OV7670SOC) += ov7670soc.o
obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o
obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o
obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o
obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o
+obj-$(CONFIG_SOC_CAMERA_TVP5150) += tvp5150soc.o
obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o
# And now the v4l2 drivers:
diff --git a/drivers/media/video/ov7670soc.c b/drivers/media/video/ov7670soc.c
new file mode 100644
index 000000000000..5e88d2ea3995
--- /dev/null
+++ b/drivers/media/video/ov7670soc.c
@@ -0,0 +1,746 @@
+/*
+ * drivers/media/video/ov7670soc.c
+ *
+ * OmniVision OV7670 cameras driver
+ *
+ * Copyright (c) 2011 Ming-Yao Chen <mychen0518@gmail.com>
+ * (based on tvp514x.c)
+ *
+ * Copyright (c) 2013 Ant Micro <www.antmicro.com>
+ *
+ * This package 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+
+/* MODULE NAME*/
+#define OV7670SOC_MODULE_NAME "ov7670soc"
+
+/* Private macros for OV7670 */
+#define I2C_RETRY_COUNT (5)
+#define VGA_WIDTH (640)
+#define VGA_HEIGHT (480)
+
+/* Registers */
+#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE 0x01 /* blue gain */
+#define REG_RED 0x02 /* red gain */
+#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1 0x04 /* Control 1 */
+#define COM1_CCIR656 0x40 /* CCIR656 enable */
+#define REG_BAVE 0x05 /* U/B Average level */
+#define REG_GbAVE 0x06 /* Y/Gb Average level */
+#define REG_AECHH 0x07 /* AEC MS 5 bits */
+#define REG_RAVE 0x08 /* V/R Average level */
+#define REG_COM2 0x09 /* Control 2 */
+#define COM2_SSLEEP 0x10 /* Soft sleep mode */
+#define REG_PID 0x0a /* Product ID MSB */
+#define REG_VER 0x0b /* Product ID LSB */
+#define REG_COM3 0x0c /* Control 3 */
+#define COM3_SWAP 0x40 /* Byte swap */
+#define COM3_SCALEEN 0x08 /* Enable scaling */
+#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */
+#define REG_COM4 0x0d /* Control 4 */
+#define REG_COM5 0x0e /* All "reserved" */
+#define REG_COM6 0x0f /* Control 6 */
+#define REG_AECH 0x10 /* More bits of AEC value */
+#define REG_CLKRC 0x11 /* Clocl control */
+#define CLK_EXT 0x40 /* Use external clock directly */
+#define CLK_SCALE 0x3f /* Mask for internal clock scale */
+#define REG_COM7 0x12 /* Control 7 */
+#define COM7_RESET 0x80 /* Register reset */
+#define COM7_FMT_MASK 0x38
+#define COM7_FMT_VGA 0x00
+#define COM7_FMT_CIF 0x20 /* CIF format */
+#define COM7_FMT_QVGA 0x10 /* QVGA format */
+#define COM7_FMT_QCIF 0x08 /* QCIF format */
+#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */
+#define COM7_YUV 0x00 /* YUV */
+#define COM7_BAYER 0x01 /* Bayer format */
+#define COM7_PBAYER 0x05 /* "Processed bayer" */
+#define REG_COM8 0x13 /* Control 8 */
+#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
+#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */
+#define COM8_BFILT 0x20 /* Band filter enable */
+#define COM8_AGC 0x04 /* Auto gain enable */
+#define COM8_AWB 0x02 /* White balance enable */
+#define COM8_AEC 0x01 /* Auto exposure enable */
+#define REG_COM9 0x14 /* Control 9 - gain ceiling */
+#define REG_COM10 0x15 /* Control 10 */
+#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */
+#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */
+#define COM10_HREF_REV 0x08 /* Reverse HREF */
+#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */
+#define COM10_VS_NEG 0x02 /* VSYNC negative */
+#define COM10_HS_NEG 0x01 /* HSYNC negative */
+#define REG_HSTART 0x17 /* Horiz start high bits */
+#define REG_HSTOP 0x18 /* Horiz stop high bits */
+#define REG_VSTART 0x19 /* Vert start high bits */
+#define REG_VSTOP 0x1a /* Vert stop high bits */
+#define REG_PSHFT 0x1b /* Pixel delay after HREF */
+#define REG_MIDH 0x1c /* Manuf. ID high */
+#define REG_MIDL 0x1d /* Manuf. ID low */
+#define REG_MVFP 0x1e /* Mirror / vflip */
+#define MVFP_MIRROR 0x20 /* Mirror image */
+#define MVFP_FLIP 0x10 /* Vertical flip */
+#define REG_AEW 0x24 /* AGC upper limit */
+#define REG_AEB 0x25 /* AGC lower limit */
+#define REG_VPT 0x26 /* AGC/AEC fast mode op region */
+#define REG_HSYST 0x30 /* HSYNC rising edge delay */
+#define REG_HSYEN 0x31 /* HSYNC falling edge delay */
+#define REG_HREF 0x32 /* HREF pieces */
+#define REG_TSLB 0x3a /* lots of stuff */
+#define TSLB_YLAST 0x08 /* UYVY or VYUY - see com13 */
+#define REG_COM11 0x3b /* Control 11 */
+#define COM11_NIGHT 0x80 /* NIght mode enable */
+#define COM11_NMFR 0x60 /* Two bit NM frame rate */
+#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
+#define COM11_50HZ 0x08 /* Manual 50Hz select */
+#define COM11_EXP 0x02
+#define REG_COM12 0x3c /* Control 12 */
+#define COM12_HREF 0x80 /* HREF always */
+#define REG_COM13 0x3d /* Control 13 */
+#define COM13_GAMMA 0x80 /* Gamma enable */
+#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */
+#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */
+#define REG_COM14 0x3e /* Control 14 */
+#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */
+#define REG_EDGE 0x3f /* Edge enhancement factor */
+#define REG_COM15 0x40 /* Control 15 */
+#define COM15_R10F0 0x00 /* Data range 10 to F0 */
+#define COM15_R01FE 0x80 /* 01 to FE */
+#define COM15_R00FF 0xc0 /* 00 to FF */
+#define COM15_RGB565 0x10 /* RGB565 output */
+#define COM15_RGB555 0x30 /* RGB555 output */
+#define REG_COM16 0x41 /* Control 16 */
+#define COM16_AWBGAIN 0x08 /* AWB gain enable */
+#define REG_COM17 0x42 /* Control 17 */
+#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */
+#define COM17_CBAR 0x08 /* DSP Color bar */
+/*
+ * This matrix defines how the colors are generated, must be
+ * tweaked to adjust hue and saturation.
+ *
+ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
+ *
+ * They are nine-bit signed quantities, with the sign bit
+ * stored in 0x58. Sign for v-red is bit 0, and up from there.
+ */
+#define REG_CMATRIX_BASE 0x4f
+#define CMATRIX_LEN 6
+#define REG_MTX1 0x4f
+#define REG_MTX2 0x50
+#define REG_MTX3 0x51
+#define REG_MTX4 0x52
+#define REG_MTX5 0x53
+#define REG_MTX6 0x54
+#define REG_BRIGHTNESS 0x55 /* Brightness */
+#define REG_CONTRAST 0x56 /* Contrast control */
+#define REG_CMATRIX_SIGN 0x58
+#define REG_GFIX 0x69 /* Fix gain control */
+#define REG_DBLV 0x6b /* PLL control an debugging */
+#define DBLV_BYPASS 0x00 /* Bypass PLL */
+#define DBLV_X4 0x01 /* clock x4 */
+#define DBLV_X6 0x10 /* clock x6 */
+#define DBLV_X8 0x11 /* clock x8 */
+#define REG_SCALING_X 0x70
+#define REG_SCALING_Y 0x71
+#define REG_REG76 0x76 /* OV's name */
+#define R76_BLKPCOR 0x80 /* Black pixel correction enable */
+#define R76_WHTPCOR 0x40 /* White pixel correction enable */
+#define REG_RGB444 0x8c /* RGB 444 control */
+#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */
+#define R444_RGBX 0x01 /* Empty nibble at end */
+#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */
+#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
+#define REG_BD50MAX 0xa5 /* 50hz banding step limit */
+#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
+#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
+#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
+#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
+#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */
+#define REG_BD60MAX 0xab /* 60hz banding step limit */
+
+/*
+ * Store information about the video data format. The color matrix
+ * is deeply tied into the format, so keep the relevant values here.
+ * The magic matrix numbers come from OmniVision.
+ */
+static struct ov7670soc_format_struct {
+ enum v4l2_mbus_pixelcode mbus_code;
+ enum v4l2_colorspace colorspace;
+ struct regval_list *regs;
+ int cmatrix[CMATRIX_LEN];
+} ov7670soc_formats[] = {
+ {
+ /* TODO: registers are set for UYUV, but we are present as YUYV, otherwise image is
+ * invalid
+ */
+ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ /*TODO: list of regs with preset values for specified format
+ .regs = ov7670soc_fmt_yuv422,*/
+ .cmatrix = { -64, -52, -12, -23, -41, 132 },
+ },
+};
+#define N_OV7670_FMTS ARRAY_SIZE(ov7670soc_formats)
+
+/**
+ * struct ov7670soc_decoder - OV7670 decoder object
+ * sd: Subdevice Slave handle
+ * TODO: ov7670soc_regs: copy of hw's regs with preset values.
+ * pdata: Board specific
+ * fmt_list: Format list
+ * num_fmts: Number of formats
+ * fmt: Current format
+ * brightness: only for queries
+ * contrast: only for queries
+ * saturation: only for queries
+ * hue: only for queries
+ */
+struct ov7670soc_decoder {
+ struct v4l2_subdev sd;
+ /*
+ TODO: list of def reg values for read queries and RMW operations.
+ Local cache due to impossibility of registers read
+ struct ov7670soc_reg ov7670soc_regs[ARRAY_SIZE(ov7670soc_reg_list_default)];*/
+ const struct ov7670soc_platform_data *pdata;
+ const struct ov7670soc_format_struct *fmt_list;
+ int num_fmts;
+ struct ov7670soc_format_struct *fmt; /* Current format */
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+};
+
+static const struct v4l2_queryctrl ov7670soc_controls[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "contrast",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ },
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "saturation",
+ .minimum = 0,
+ .maximum = 256,
+ .step = 1,
+ .default_value = 128,
+ },
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "hue",
+ .minimum = -180,
+ .maximum = 180,
+ .step = 5,
+ .default_value = 0,
+ },
+};
+
+static inline struct ov7670soc_decoder *to_decoder(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov7670soc_decoder, sd);
+}
+
+/*
+ * Read register prohibited on Tegra T-20 due to
+ * arbitration lost after sending device read addres
+ */
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static int ov7670soc_read_reg(struct i2c_client *client, unsigned char reg, unsigned char *val)
+{
+ int err, retry = 0;
+
+read_again:
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err < 0) {
+ if (retry <= I2C_RETRY_COUNT) {
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ dev_err(&client->dev, "Failed to read register 0x%02X!\n", reg);
+ }
+ *val = (unsigned char)err;
+ return err;
+}
+#endif
+
+static int ov7670soc_write_reg(struct i2c_client *client, unsigned char reg, unsigned char val)
+{
+ int err, retry = 0;
+
+write_again:
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ dev_err(&client->dev, "Failed to write 0x%02X to register 0x%02X!\n", val, reg);
+ }
+
+ return err;
+}
+
+/*
+ * Reset all camera registers to default values
+ */
+static int ov7670soc_reset(struct i2c_client *client)
+{
+ int ret;
+
+ ret = ov7670soc_write_reg(client, REG_COM7, COM7_RESET);
+ if (ret)
+ dev_err(&client->dev, "An error occurred while entering soft reset!\n");
+
+ return ret;
+}
+
+/*
+ * ov7670soc_setup - initializes a list of OV7670 registers
+ */
+static int ov7670soc_setup(struct v4l2_subdev *sd, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ ov7670soc_reset(client);
+
+ /* Configure HSYNC & VSYNC */
+ ov7670soc_write_reg(client, REG_COM10, COM10_HSYNC | COM10_VS_NEG);
+ /* not sure what this does, but improves colors quality */
+ ov7670soc_write_reg(client, 0xb0, 0x84);
+
+ /* Config HSYNC offset */
+ ov7670soc_write_reg(client, REG_HSTART, 0x00);
+ ov7670soc_write_reg(client, REG_HSTOP, 0x00);
+ ov7670soc_write_reg(client, REG_HSYST, 0x00);
+ ov7670soc_write_reg(client, REG_HSYEN, 0x05);
+ /* default MTX1..MTX5 and MTX6 = 0x84 makes better image */
+ ov7670soc_write_reg(client, REG_MTX6, 0x84);
+
+ //TODO: output format, move to mbus_s_fmt
+ ov7670soc_write_reg(client, REG_COM7, COM7_YUV); /* Selects YUV mode */
+ /* U before V */
+ ov7670soc_write_reg(client, REG_COM13, COM13_GAMMA | COM13_UVSAT);
+
+ return 0;
+}
+
+/*
+ * Hue also requires messing with the color matrix. It also requires
+ * trig functions, which tend not to be well supported in the kernel.
+ * So here is a simple table of sine values, 0-90 degrees, in steps
+ * of five degrees. Values are multiplied by 1000.
+ *
+ * The following naive approximate trig functions require an argument
+ * carefully limited to -180 <= theta <= 180.
+ */
+#define SIN_STEP 5
+static const int ov7670soc_sin_table[] = {
+ 0, 87, 173, 258, 342, 422,
+ 499, 573, 642, 707, 766, 819,
+ 866, 906, 939, 965, 984, 996,
+ 1000
+};
+
+static int ov7670soc_sine(int theta)
+{
+ int chs = 1;
+ int sine;
+
+ if (theta < 0) {
+ theta = -theta;
+ chs = -1;
+ }
+ if (theta <= 90)
+ sine = ov7670soc_sin_table[theta/SIN_STEP];
+ else {
+ theta -= 90;
+ sine = 1000 - ov7670soc_sin_table[theta/SIN_STEP];
+ }
+ return sine*chs;
+}
+
+static int ov7670soc_cosine(int theta)
+{
+ theta = 90 - theta;
+ if (theta > 180)
+ theta -= 360;
+ else if (theta < -180)
+ theta += 360;
+ return ov7670soc_sine(theta);
+}
+
+static void ov7670soc_calc_cmatrix(struct ov7670soc_decoder *decoder, int matrix[CMATRIX_LEN], int sat, int hue)
+{
+ int i;
+ /*
+ * Apply the current saturation setting first.
+ */
+ for (i = 0; i < CMATRIX_LEN; i++)
+ matrix[i] = (decoder->fmt->cmatrix[i] * sat) >> 7;
+ /*
+ * Then, if need be, rotate the hue value.
+ */
+ if (hue != 0) {
+ int sinth, costh;
+ sinth = ov7670soc_sine(hue);
+ costh = ov7670soc_cosine(hue);
+
+ matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
+ matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
+ matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
+ matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
+ matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
+ matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
+ }
+}
+
+static int ov7670soc_store_cmatrix(struct v4l2_subdev *sd, int matrix[CMATRIX_LEN])
+{
+ int i, ret;
+ unsigned char signbits = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /*
+ * Weird crap seems to exist in the upper part of
+ * the sign bits register, so let's preserve it.
+ */
+/*#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* TODO: read forbidden on T-20
+ ret = ov7670soc_read_reg(client, REG_CMATRIX_SIGN, &signbits);
+ signbits &= 0xc0;
+#endif*/
+ for (i = 0; i < CMATRIX_LEN; i++) {
+ unsigned char raw;
+
+ if (matrix[i] < 0) {
+ signbits |= (1 << i);
+ if (matrix[i] < -255)
+ raw = 0xff;
+ else
+ raw = (-1 * matrix[i]) & 0xff;
+ }
+ else {
+ if (matrix[i] > 255)
+ raw = 0xff;
+ else
+ raw = matrix[i] & 0xff;
+ }
+ ret += ov7670soc_write_reg(client, REG_CMATRIX_BASE + i, raw);
+ }
+ ret += ov7670soc_write_reg(client, REG_CMATRIX_SIGN, signbits);
+ return ret;
+}
+
+static unsigned char ov7670soc_abs_to_sm(unsigned char v)
+{
+ if (v > 127)
+ return v & 0x7f;
+ return (128 - v) | 0x80;
+}
+
+static int ov7670soc_s_brightness(struct v4l2_subdev *sd, int value)
+{
+ unsigned char v;//, com8 = 0;
+ int ret;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* TODO: read forbidden on T-20 */
+/*#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned char com8 = 0;
+ ret = ov7670soc_read_reg(client, REG_COM8, &com8);
+ com8 &= ~COM8_AEC;
+#endif*/
+
+ ov7670soc_write_reg(client, REG_COM8, 0x8e); //defaul val is 0x8f, 0x8e->disable AEC
+ v = ov7670soc_abs_to_sm(value);
+ ret = ov7670soc_write_reg(client, REG_BRIGHTNESS, v);
+ return ret;
+}
+
+static int ov7670soc_s_contrast(struct v4l2_subdev *sd, int value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return ov7670soc_write_reg(client, REG_CONTRAST, (unsigned char) value);
+}
+
+static int ov7670soc_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue)
+{
+ struct ov7670soc_decoder *decoder = to_decoder(sd);
+ int matrix[CMATRIX_LEN];
+ int ret;
+
+ ov7670soc_calc_cmatrix(decoder, matrix, sat, hue);
+ ret = ov7670soc_store_cmatrix(sd, matrix);
+ return ret;
+}
+
+static int ov7670soc_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ov7670soc_decoder *decoder = to_decoder(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return ov7670soc_s_brightness(sd, ctrl->value);
+ case V4L2_CID_CONTRAST:
+ return ov7670soc_s_contrast(sd, ctrl->value);
+ case V4L2_CID_SATURATION:
+ return ov7670soc_s_sat_hue(sd, ctrl->value, decoder->hue);
+ case V4L2_CID_HUE:
+ return ov7670soc_s_sat_hue(sd, decoder->saturation, ctrl->value);
+ }
+ return -EINVAL;
+}
+
+static int ov7670soc_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ov7670soc_decoder *decoder = to_decoder(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = decoder->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = decoder->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = decoder->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = decoder->hue;
+ break;
+ }
+ return 0;
+}
+
+/* Functions required by soc_camera_ops ***************************************/
+/* Alter bus settings on camera side */
+static int ov7670soc_set_bus_param(struct soc_camera_device *icd, unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long ov7670soc_query_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+/******************************************************************************/
+
+/* Functions required by v4l2_subdev_video_ops ********************************/
+static int ov7670soc_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ /*
+ The OV7670 camera dose not have ability to start/stop streaming
+ */
+ return 0;
+}
+
+static int ov7670soc_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ /*
+ * Set the image format. Currently we support only one format with
+ * fixed resolution, so we can set the format as it is on camera startup.
+ */
+ ov7670soc_setup(sd, 0);
+
+ return 0;
+}
+
+static int ov7670soc_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct ov7670soc_decoder *decoder = to_decoder(sd);
+ int index;
+
+ /* Check if we support queried image format. */
+ for (index = 0; index < N_OV7670_FMTS; index++)
+ if (ov7670soc_formats[index].mbus_code == mf->code)
+ break;
+ /* If not, set the only one which we support */
+ if (index >= N_OV7670_FMTS) {
+ /* default to first format */
+ index = 0;
+ mf->code = ov7670soc_formats[0].mbus_code;
+ }
+
+ /* Store the current format */
+ decoder->fmt = &ov7670soc_formats[index];
+
+ /* Fixed value, move to ov7670_formats */
+ mf->field = V4L2_FIELD_NONE;
+ /* TODO: support for other resolutions (CIF/QCIF etc).*/
+ mf->width = VGA_WIDTH;
+ mf->height = VGA_HEIGHT;
+ mf->colorspace = decoder->fmt->colorspace;
+
+ return 0;
+}
+
+static int ov7670soc_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov7670soc_formats))
+ return -EINVAL;
+
+ *code = ov7670soc_formats[index].mbus_code;
+
+ return 0;
+}
+/******************************************************************************/
+
+static struct soc_camera_ops ov7670soc_soc_camera_ops = {
+ .set_bus_param = ov7670soc_set_bus_param,
+ .query_bus_param = ov7670soc_query_bus_param,
+ .controls = ov7670soc_controls,
+ .num_controls = ARRAY_SIZE(ov7670soc_controls),
+};
+
+static const struct v4l2_subdev_core_ops ov7670soc_core_ops = {
+ .g_ctrl = ov7670soc_g_ctrl,
+ .s_ctrl = ov7670soc_s_ctrl,
+};
+
+static const struct v4l2_subdev_video_ops ov7670soc_video_ops = {
+ .s_stream = ov7670soc_s_stream,
+ .s_mbus_fmt = ov7670soc_s_mbus_fmt,
+ .try_mbus_fmt = ov7670soc_try_mbus_fmt,
+ .enum_mbus_fmt = ov7670soc_enum_mbus_fmt,
+};
+
+static const struct v4l2_subdev_ops ov7670soc_ops = {
+ .core = &ov7670soc_core_ops,
+ .video = &ov7670soc_video_ops,
+};
+
+static int ov7670soc_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct ov7670soc_decoder *decoder;
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ icl = to_soc_camera_link(icd);
+ if (!icl) {
+ dev_err(&client->dev, "No platform data!!\n");
+ return -ENODEV;
+ }
+
+ decoder = kzalloc(sizeof(struct ov7670soc_decoder), GFP_KERNEL);
+ if (!decoder)
+ {
+ dev_err(&client->dev, "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+ sd = &decoder->sd;
+
+ /* Initialize the ov7670soc_decoder with default configuration */
+ decoder->fmt_list = ov7670soc_formats;
+ decoder->num_fmts = ARRAY_SIZE(ov7670soc_formats);
+ decoder->fmt = &ov7670soc_formats[0];
+ decoder->pdata = client->dev.platform_data;
+ decoder->brightness = 128;
+ decoder->contrast = 64;
+ decoder->saturation = 128;
+ decoder->hue = 0;
+ /* Register with V4L2 layer as slave device */
+ v4l2_i2c_subdev_init(sd, client, &ov7670soc_ops);
+
+ icd->ops = &ov7670soc_soc_camera_ops;
+
+ return 0;
+}
+
+static int ov7670soc_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov7670soc_decoder *decoder = to_decoder(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(decoder);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov7670soc_id[] =
+{
+ {OV7670SOC_MODULE_NAME, 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov7670soc_id);
+
+static struct i2c_driver ov7670soc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = OV7670SOC_MODULE_NAME,
+ },
+ .probe = ov7670soc_probe,
+ .remove = ov7670soc_remove,
+ .id_table = ov7670soc_id,
+};
+
+static int __init ov7670soc_init(void)
+{
+ return i2c_add_driver(&ov7670soc_driver);
+}
+
+static void __exit ov7670soc_exit(void)
+{
+ i2c_del_driver(&ov7670soc_driver);
+}
+
+module_init(ov7670soc_init);
+module_exit(ov7670soc_exit);
+
+MODULE_AUTHOR("Antmicro Ltd.");
+MODULE_DESCRIPTION("OV7670 linux decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/video/tvp5150_reg.h
index 4240043c0b2a..c4cdbadeead9 100644
--- a/drivers/media/video/tvp5150_reg.h
+++ b/drivers/media/video/tvp5150_reg.h
@@ -6,6 +6,9 @@
*/
#define TVP5150_VD_IN_SRC_SEL_1 0x00 /* Video input source selection #1 */
+#define TVP5150_VIDEO_INPUT_SELECT_AIP1A 0x00
+#define TVP5150_VIDEO_INPUT_SELECT_AIP1B 0x02
+#define TVP5150_VIDEO_INPUT_SELECT_SVIDEO 0x01
#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */
#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */
#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */
diff --git a/drivers/media/video/tvp5150soc.c b/drivers/media/video/tvp5150soc.c
new file mode 100644
index 000000000000..3cb7cd6b4dc8
--- /dev/null
+++ b/drivers/media/video/tvp5150soc.c
@@ -0,0 +1,538 @@
+/*
+ * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver
+ *
+ * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+
+#include <media/tvp5150.h>
+#include "tvp5150_reg.h"
+
+#define I2C_RETRY_COUNT 3
+#define LINE_PIXELS 576
+#define FRAME_LINES 520
+
+#define MODULE_NAME "tvp5150soc"
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level");
+
+struct i2c_reg_value {
+ unsigned char reg;
+ unsigned char value;
+};
+
+static struct tvp5150soc_format_struct {
+ enum v4l2_mbus_pixelcode mbus_code;
+ enum v4l2_colorspace colorspace;
+} tvp5150soc_formats[] = {
+ {
+ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+#define N_TVP5150_FMTS ARRAY_SIZE(tvp5150soc_formats)
+
+struct tvp5150soc_decoder {
+ struct v4l2_subdev sd;
+ const struct tvp5150soc_format_struct *fmt_list;
+ struct tvp5150soc_format_struct *fmt;
+ int num_fmts;
+ int active_input;
+};
+
+static const struct v4l2_queryctrl tvp5150soc_controls[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ },
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "contrast",
+ .minimum = 0,
+ .maximum = 207,
+ .step = 1,
+ .default_value = 128,
+ },
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ },
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "hue",
+ .minimum = -127, /*TODO: TVP5150 supports hue in range -180..180, which is equal to -127..127 reg value*/
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
+static inline struct tvp5150soc_decoder *to_decoder(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tvp5150soc_decoder, sd);
+}
+
+static int tvp5150soc_read_reg(struct v4l2_subdev *sd, unsigned char addr, unsigned char *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int err, retry = I2C_RETRY_COUNT;
+
+ int ccc = client->addr;
+ int aaa = addr;
+ printk(KERN_ERR "tvp5150soc_read_reg: %04X %04X\n", ccc, aaa);
+
+ while(retry)
+ {
+ err = i2c_smbus_read_byte_data(client, addr);
+ if(err > 0)
+ break;
+ retry--;
+ msleep_interruptible(10);
+ }
+
+ if(err < 0)
+ v4l2_dbg(0, debug, sd, "i2c i/o error: %d\n", err);
+ else
+ {
+ *val = (unsigned char)err;
+ v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, *val);
+ }
+
+ printk(KERN_ERR "tvp5150soc_read_reg result: %d\n", err);
+
+ return err;
+}
+
+static int tvp5150soc_write_reg(struct v4l2_subdev *sd, unsigned char addr, unsigned char val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int err, retry = I2C_RETRY_COUNT;
+
+ while(retry)
+ {
+ err = i2c_smbus_write_byte_data(client, addr, val);
+ if(err > 0)
+ break;
+ retry--;
+ msleep_interruptible(10);
+ }
+
+ if(err < 0)
+ v4l2_dbg(0, debug, sd, "i2c i/o error: %d\n", err);
+ else
+ v4l2_dbg(2, debug, sd, "tvp5150: write 0x%02x = 0x%02x\n", addr, val);
+
+ return err;
+}
+
+/* Default values as sugested at TVP5150AM1 datasheet */
+static const struct i2c_reg_value tvp5150soc_init_default[] = {
+ {TVP5150_VD_IN_SRC_SEL_1,0x00}, /* 0x00 */
+ {TVP5150_ANAL_CHL_CTL,0x15}, /* 0x01 */
+ {TVP5150_OP_MODE_CTL,0x00}, /* 0x02 */
+ {TVP5150_MISC_CTL,0x01}, /* 0x03 */
+ {TVP5150_COLOR_KIL_THSH_CTL,0x10}, /* 0x06 */
+ {TVP5150_LUMA_PROC_CTL_1,0x60}, /* 0x07 */
+ {TVP5150_LUMA_PROC_CTL_2,0x00}, /* 0x08 */
+ {TVP5150_BRIGHT_CTL,0x80}, /* 0x09 */
+ {TVP5150_SATURATION_CTL,0x80}, /* 0x0a */
+ {TVP5150_HUE_CTL,0x00}, /* 0x0b */
+ {TVP5150_CONTRAST_CTL,0x80}, /* 0x0c */
+ {TVP5150_DATA_RATE_SEL,0x47}, /* 0x0d */
+ {TVP5150_LUMA_PROC_CTL_3,0x00}, /* 0x0e */
+ {TVP5150_CONF_SHARED_PIN,0x08}, /* 0x0f */
+ {TVP5150_ACT_VD_CROP_ST_MSB,0x00}, /* 0x11 */
+ {TVP5150_ACT_VD_CROP_ST_LSB,0x00}, /* 0x12 */
+ {TVP5150_ACT_VD_CROP_STP_MSB,0x00}, /* 0x13 */
+ {TVP5150_ACT_VD_CROP_STP_LSB,0x00}, /* 0x14 */
+ {TVP5150_GENLOCK,0x01}, /* 0x15 */
+ {TVP5150_HORIZ_SYNC_START,0x80}, /* 0x16 */
+ {TVP5150_VERT_BLANKING_START,0x00}, /* 0x18 */
+ {TVP5150_VERT_BLANKING_STOP,0x00}, /* 0x19 */
+ {TVP5150_CHROMA_PROC_CTL_1,0x0c}, /* 0x1a */
+ {TVP5150_CHROMA_PROC_CTL_2,0x14}, /* 0x1b */
+ {TVP5150_INT_RESET_REG_B,0x00}, /* 0x1c */
+ {TVP5150_INT_ENABLE_REG_B,0x00}, /* 0x1d */
+ {TVP5150_INTT_CONFIG_REG_B,0x00}, /* 0x1e */
+ {TVP5150_VIDEO_STD,0x00}, /* 0x28 */
+ {TVP5150_MACROVISION_ON_CTR,0x0f}, /* 0x2e */
+ {TVP5150_MACROVISION_OFF_CTR,0x01}, /* 0x2f */
+ {TVP5150_TELETEXT_FIL_ENA,0x00}, /* 0xbb */
+ {TVP5150_INT_STATUS_REG_A,0x00}, /* 0xc0 */
+ {TVP5150_INT_ENABLE_REG_A,0x00}, /* 0xc1 */
+ {TVP5150_INT_CONF,0x04}, /* 0xc2 */
+ {TVP5150_FIFO_INT_THRESHOLD,0x80}, /* 0xc8 */
+ {TVP5150_FIFO_RESET,0x00}, /* 0xc9 */
+ {TVP5150_LINE_NUMBER_INT,0x00}, /* 0xca */
+ {TVP5150_PIX_ALIGN_REG_LOW,0x4e}, /* 0xcb */
+ {TVP5150_PIX_ALIGN_REG_HIGH,0x00}, /* 0xcc */
+ {TVP5150_FIFO_OUT_CTRL,0x01}, /* 0xcd */
+ {TVP5150_FULL_FIELD_ENA,0x00}, /* 0xcf */
+ {TVP5150_LINE_MODE_INI,0x00}, /* 0xd0 */
+ {TVP5150_FULL_FIELD_MODE_REG,0x7f}, /* 0xfc */
+ { /* end of data */0xff,0xff}
+};
+
+/* Default values as sugested at TVP5150AM1 datasheet */
+static const struct i2c_reg_value tvp5150soc_init_enable[] = {
+ {TVP5150_VD_IN_SRC_SEL_1, 0x02},
+ {TVP5150_CONF_SHARED_PIN, 0x02},
+ {TVP5150_ANAL_CHL_CTL, 0x15}, /* Automatic offset and AGC enabled */
+ {TVP5150_MISC_CTL, 0x6f}, /* Activate YCrCb output 0x9 or 0xd ? */
+ {TVP5150_AUTOSW_MSK, 0x0}, /* Activates video std autodetection for all standards */
+ {TVP5150_DATA_RATE_SEL, 0x47},/* Default format: 0x47. For 4:2:2: 0x40 */
+ {TVP5150_CHROMA_PROC_CTL_1, 0x0c},
+ {TVP5150_CHROMA_PROC_CTL_2, 0x54},
+ {0x27, 0x20},/* Non documented, but initialized on WinTV USB2 */
+ {0xff, 0xff}
+};
+
+static int tvp5150soc_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs)
+{
+ while (regs->reg != 0xff) {
+ tvp5150soc_write_reg(sd, regs->reg, regs->value);
+ regs++;
+ }
+ return 0;
+}
+
+static int tvp5150soc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct tvp5150soc_decoder *decoder = to_decoder(sd);
+ unsigned char val;
+
+ if (i < 3)
+ {
+ decoder->active_input = i;
+ switch (decoder->active_input) {
+ case 0:
+ val = TVP5150_VIDEO_INPUT_SELECT_AIP1A;
+ break;
+ case 1:
+ val = TVP5150_VIDEO_INPUT_SELECT_AIP1B;
+ break;
+ case 2:
+ val = TVP5150_VIDEO_INPUT_SELECT_SVIDEO;
+ break;
+ default:
+ val = TVP5150_VIDEO_INPUT_SELECT_AIP1A;
+ }
+ return tvp5150soc_write_reg(sd, TVP5150_VD_IN_SRC_SEL_1, val);
+ }
+
+ return -EINVAL;
+}
+
+static int tvp5150soc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct tvp5150soc_decoder *decoder = to_decoder(sd);
+
+ *i = decoder->active_input;
+
+ return 0;
+}
+
+/********************************************************************************************************************************/
+
+static int tvp5150soc_set_bus_param(struct soc_camera_device *icd, unsigned long flags)
+{
+ return 0;
+}
+
+static unsigned long tvp5150soc_query_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+ SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+/********************************************************************************************************************************/
+
+static int tvp5150soc_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ tvp5150soc_read_reg(sd, TVP5150_BRIGHT_CTL, (unsigned char*)&ctrl->value);
+ return 0;
+ case V4L2_CID_CONTRAST:
+ tvp5150soc_read_reg(sd, TVP5150_CONTRAST_CTL, (unsigned char*)&ctrl->value);
+ return 0;
+ case V4L2_CID_SATURATION:
+ tvp5150soc_read_reg(sd, TVP5150_SATURATION_CTL, (unsigned char*)&ctrl->value);
+ return 0;
+ case V4L2_CID_HUE:
+ tvp5150soc_read_reg(sd, TVP5150_HUE_CTL, (unsigned char*)&ctrl->value);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tvp5150soc_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ tvp5150soc_write_reg(sd, TVP5150_BRIGHT_CTL, ctrl->value);
+ return 0;
+ case V4L2_CID_CONTRAST:
+ tvp5150soc_write_reg(sd, TVP5150_CONTRAST_CTL, ctrl->value);
+ return 0;
+ case V4L2_CID_SATURATION:
+ tvp5150soc_write_reg(sd, TVP5150_SATURATION_CTL, ctrl->value);
+ return 0;
+ case V4L2_CID_HUE:
+ tvp5150soc_write_reg(sd, TVP5150_HUE_CTL, ctrl->value);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tvp5150soc_reset(struct v4l2_subdev *sd, u32 val)
+{
+ /* Initializes TVP5150 to its default values */
+ /* TVP5150 has no ability to software reset */
+ tvp5150soc_write_inittab(sd, tvp5150soc_init_default);
+
+ /* Initializes TVP5150 to stream enabled values */
+ tvp5150soc_write_inittab(sd, tvp5150soc_init_enable);
+
+ return 0;
+}
+
+/********************************************************************************************************************************/
+
+static int tvp5150soc_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ /*
+ * Set the image format. Currently we support only one format with
+ * fixed resolution, so we can set the format as it is on camera startup.
+ */
+ tvp5150soc_reset(sd, 0);
+
+ return 0;
+}
+
+static int tvp5150soc_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct tvp5150soc_decoder *decoder = to_decoder(sd);
+ int index;
+
+ printk(KERN_ERR "tvp5150soc_probe try mbus format\n");
+
+ /* Check if we support queried image format. */
+ for (index = 0; index < N_TVP5150_FMTS; index++)
+ if (tvp5150soc_formats[index].mbus_code == mf->code)
+ {
+ printk(KERN_ERR "tvp5150soc_probe try mbus format found format\n");
+ break;
+ }
+ /* If not, set the only one which we support */
+ if (index >= N_TVP5150_FMTS) {
+ /* default to first format */
+ index = 0;
+ printk(KERN_ERR "tvp5150soc_probe try mbus format default format\n");
+ mf->code = tvp5150soc_formats[0].mbus_code;
+ }
+
+ /* Store the current format */
+ decoder->fmt = &tvp5150soc_formats[index];
+
+ /* Fixed value, move to tvp5150soc_formats */
+ mf->field = V4L2_FIELD_INTERLACED_TB;
+ mf->width = LINE_PIXELS;
+ mf->height = FRAME_LINES;
+ mf->colorspace = tvp5150soc_formats[index].colorspace;
+
+ return 0;
+}
+
+static int tvp5150soc_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(tvp5150soc_formats))
+ return -EINVAL;
+
+ *code = tvp5150soc_formats[index].mbus_code;
+
+ return 0;
+}
+/********************************************************************************************************************************/
+
+static struct soc_camera_ops tvp5150soc_soc_camera_ops = {
+ .set_bus_param = tvp5150soc_set_bus_param,
+ .query_bus_param = tvp5150soc_query_bus_param,
+ .controls = tvp5150soc_controls,
+ .num_controls = ARRAY_SIZE(tvp5150soc_controls),
+};
+
+static const struct v4l2_subdev_core_ops tvp5150soc_core_ops = {
+ .g_ctrl = tvp5150soc_g_ctrl,
+ .s_ctrl = tvp5150soc_s_ctrl,
+ .reset = tvp5150soc_reset,
+};
+
+static const struct v4l2_subdev_video_ops tvp5150soc_video_ops = {
+ .s_mbus_fmt = tvp5150soc_s_mbus_fmt,
+ .try_mbus_fmt = tvp5150soc_try_mbus_fmt,
+ .enum_mbus_fmt = tvp5150soc_enum_mbus_fmt,
+};
+
+static const struct v4l2_subdev_ops tvp5150soc_ops = {
+ .core = &tvp5150soc_core_ops,
+ .video = &tvp5150soc_video_ops,
+};
+
+
+/****************************************************************************
+ I2C Client & Driver
+ ****************************************************************************/
+static int tvp5150soc_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct soc_camera_link *icl;
+ struct tvp5150soc_decoder *decoder;
+ struct v4l2_subdev *sd;
+ struct v4l2_ioctl_ops *ops;
+ unsigned char msb_id, lsb_id, msb_rom, lsb_rom;
+
+ printk(KERN_ERR "tvp5150soc_probe start\n");
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ {
+ printk(KERN_ERR "tvp5150soc_probe error 1\n");
+ return -EIO;
+ }
+
+ icl = to_soc_camera_link(icd);
+ if (!icl)
+ {
+ dev_err(&client->dev, "No platform data!!\n");
+ printk(KERN_ERR "tvp5150soc_probe error 2\n");
+ return -ENODEV;
+ }
+
+ decoder = kzalloc(sizeof(struct tvp5150soc_decoder), GFP_KERNEL);
+ if (!decoder)
+ {
+ dev_err(&client->dev, "Failed to allocate memory for private data!\n");
+ printk(KERN_ERR "tvp5150soc_probe error 3\n");
+ return -ENOMEM;
+ }
+
+ /* TODO: init def settings of tvp5150soc_decoder */
+
+ sd = &decoder->sd;
+
+ /* Register with V4L2 layer as slave device */
+ v4l2_i2c_subdev_init(sd, client, &tvp5150soc_ops);
+
+ tvp5150soc_read_reg(sd, TVP5150_MSB_DEV_ID, &msb_id);
+ tvp5150soc_read_reg(sd, TVP5150_LSB_DEV_ID, &lsb_id);
+ tvp5150soc_read_reg(sd, TVP5150_ROM_MAJOR_VER, &msb_rom);
+ tvp5150soc_read_reg(sd, TVP5150_ROM_MINOR_VER, &lsb_rom);
+
+ if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id);
+ /* ITU-T BT.656.4 timing */
+ tvp5150soc_write_reg(sd, TVP5150_REV_SELECT, 0);
+ } else {
+ if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */
+ v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id);
+ } else {
+ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", msb_id, lsb_id);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom);
+ }
+ }
+
+ icd->ops = &tvp5150soc_soc_camera_ops;
+
+ /*
+ * This is the only way to support more than one input as soc_camera
+ * assumes in its own vidioc_s(g)_input implementation that only one
+ * input is present we have to override that with our own handlers.
+ */
+ ops = (struct v4l2_ioctl_ops*)icd->vdev->ioctl_ops;
+ ops->vidioc_s_input = &tvp5150soc_s_input;
+ ops->vidioc_g_input = &tvp5150soc_g_input;
+
+ printk(KERN_ERR "tvp5150soc_probe return 0\n");
+ return 0;
+}
+
+static int tvp5150soc_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct tvp5150soc_decoder *decoder = to_decoder(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(decoder);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id tvp5150soc_id[] = {
+ { MODULE_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver tvp5150soc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MODULE_NAME,
+ },
+ .probe = tvp5150soc_probe,
+ .remove = tvp5150soc_remove,
+ .id_table = tvp5150soc_id,
+};
+
+static __init int init_tvp5150soc(void)
+{
+ return i2c_add_driver(&tvp5150soc_driver);
+}
+
+static __exit void exit_tvp5150soc(void)
+{
+ i2c_del_driver(&tvp5150soc_driver);
+}
+
+module_init(init_tvp5150soc);
+module_exit(exit_tvp5150soc);
+
+MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver for soc_camera interface");
+MODULE_AUTHOR("Antmicro Ltd.");
+MODULE_LICENSE("GPL");