All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4 v2][FOR 3.1] v4l2: add vb2_get_unmapped_area in vb2 core
@ 2011-09-19 20:59 Scott Jiang
  2011-09-19 20:59 ` [PATCH 2/4 v2][FOR 3.1] v4l2: add adv7183 decoder driver Scott Jiang
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Scott Jiang @ 2011-09-19 20:59 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Laurent Pinchart, Hans Verkuil,
	Guennadi Liakhovetski
  Cc: linux-media, uclinux-dist-devel, Scott Jiang

no mmu system needs get_unmapped_area file operations to do mmap

Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>
---
 drivers/media/video/videobuf2-core.c |   31 +++++++++++++++++++++++++++++++
 include/media/videobuf2-core.h       |    7 +++++++
 2 files changed, 38 insertions(+), 0 deletions(-)

diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 3015e60..02a0ec6 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -1344,6 +1344,37 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
 }
 EXPORT_SYMBOL_GPL(vb2_mmap);
 
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+				    unsigned long addr,
+				    unsigned long len,
+				    unsigned long pgoff,
+				    unsigned long flags)
+{
+	unsigned long off = pgoff << PAGE_SHIFT;
+	struct vb2_buffer *vb;
+	unsigned int buffer, plane;
+	int ret;
+
+	if (q->memory != V4L2_MEMORY_MMAP) {
+		dprintk(1, "Queue is not currently set up for mmap\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Find the plane corresponding to the offset passed by userspace.
+	 */
+	ret = __find_plane_by_offset(q, off, &buffer, &plane);
+	if (ret)
+		return ret;
+
+	vb = q->bufs[buffer];
+
+	return (unsigned long)vb2_plane_vaddr(vb, plane);
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
 static int __vb2_init_fileio(struct vb2_queue *q, int read);
 static int __vb2_cleanup_fileio(struct vb2_queue *q);
 
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index f87472a..5c7b5b4 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -302,6 +302,13 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type);
 int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
 
 int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+				    unsigned long addr,
+				    unsigned long len,
+				    unsigned long pgoff,
+				    unsigned long flags);
+#endif
 unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
 size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
 		loff_t *ppos, int nonblock);
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/4 v2][FOR 3.1] v4l2: add adv7183 decoder driver
  2011-09-19 20:59 [PATCH 1/4 v2][FOR 3.1] v4l2: add vb2_get_unmapped_area in vb2 core Scott Jiang
@ 2011-09-19 20:59 ` Scott Jiang
  2011-09-26 13:37   ` Hans Verkuil
  2011-09-19 20:59 ` [PATCH 3/4 v2][FOR 3.1] v4l2: add vs6624 sensor driver Scott Jiang
  2011-09-19 20:59 ` [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver Scott Jiang
  2 siblings, 1 reply; 12+ messages in thread
From: Scott Jiang @ 2011-09-19 20:59 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Laurent Pinchart, Hans Verkuil,
	Guennadi Liakhovetski
  Cc: linux-media, uclinux-dist-devel, Scott Jiang

this driver is a v4l2 subdevice driver to support
Analog Devices ADV7183 SDTV video decoder

Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>
---
 drivers/media/video/Kconfig        |   10 +
 drivers/media/video/Makefile       |    1 +
 drivers/media/video/adv7183.c      |  686 ++++++++++++++++++++++++++++++++++++
 drivers/media/video/adv7183_regs.h |  107 ++++++
 include/media/adv7183.h            |   47 +++
 include/media/v4l2-chip-ident.h    |    3 +
 6 files changed, 854 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/adv7183.c
 create mode 100644 drivers/media/video/adv7183_regs.h
 create mode 100644 include/media/adv7183.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index f574dc0..af8ed6b 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -273,6 +273,16 @@ config VIDEO_ADV7180
 	  To compile this driver as a module, choose M here: the
 	  module will be called adv7180.
 
+config VIDEO_ADV7183
+	tristate "Analog Devices ADV7183 decoder"
+	depends on VIDEO_V4L2 && I2C
+	---help---
+	  V4l2 subdevice driver for the Analog Devices
+	  ADV7183 video decoder.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adv7183.
+
 config VIDEO_BT819
 	tristate "BT819A VideoStream decoder"
 	depends on VIDEO_V4L2 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 2723900..b0329ae 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
 obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
 obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
 obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
+obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
 obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
 obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
 obj-$(CONFIG_VIDEO_BT819) += bt819.o
diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c
new file mode 100644
index 0000000..65d682c
--- /dev/null
+++ b/drivers/media/video/adv7183.c
@@ -0,0 +1,686 @@
+/*
+ * adv7183.c Analog Devices ADV7183 video decoder driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/adv7183.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#include "adv7183_regs.h"
+
+struct adv7183 {
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+
+	v4l2_std_id std; /* Current set standard */
+	u32 input;
+	u32 output;
+	unsigned reset_pin;
+	unsigned oe_pin;
+	struct v4l2_mbus_framefmt fmt;
+};
+
+/* EXAMPLES USING 27 MHz CLOCK
+ * Mode 1 CVBS Input (Composite Video on AIN5)
+ * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
+ */
+static const unsigned char adv7183_init_regs[] = {
+	ADV7183_IN_CTRL, 0x04,           /* CVBS input on AIN5 */
+	ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
+	ADV7183_SHAP_FILT_CTRL, 0x41,    /* Set CSFM to SH1 */
+	ADV7183_ADC_CTRL, 0x16,          /* Power down ADC 1 and ADC 2 */
+	ADV7183_CTI_DNR_CTRL_4, 0x04,    /* Set DNR threshold to 4 for flat response */
+	/* ADI recommended programming sequence */
+	ADV7183_ADI_CTRL, 0x80,
+	ADV7183_CTI_DNR_CTRL_4, 0x20,
+	0x52, 0x18,
+	0x58, 0xED,
+	0x77, 0xC5,
+	0x7C, 0x93,
+	0x7D, 0x00,
+	0xD0, 0x48,
+	0xD5, 0xA0,
+	0xD7, 0xEA,
+	ADV7183_SD_SATURATION_CR, 0x3E,
+	ADV7183_PAL_V_END, 0x3E,
+	ADV7183_PAL_F_TOGGLE, 0x0F,
+	ADV7183_ADI_CTRL, 0x00,
+};
+
+static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct adv7183, sd);
+}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
+}
+
+static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
+				unsigned char value)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int adv7183_writeregs(struct v4l2_subdev *sd,
+		const unsigned char *regs, unsigned int num)
+{
+	unsigned char reg, data;
+	unsigned int cnt = 0;
+
+	if (num & 0x1) {
+		v4l2_err(sd, "invalid regs array\n");
+		return -1;
+	}
+
+	while (cnt < num) {
+		reg = *regs++;
+		data = *regs++;
+		cnt += 2;
+
+		adv7183_write(sd, reg, data);
+	}
+	return 0;
+}
+
+static int adv7183_log_status(struct v4l2_subdev *sd)
+{
+	struct adv7183 *decoder = to_adv7183(sd);
+
+	v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_IN_CTRL));
+	v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
+			adv7183_read(sd, ADV7183_VD_SEL));
+	v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_OUT_CTRL));
+	v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
+	v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
+			adv7183_read(sd, ADV7183_AUTO_DET_EN));
+	v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
+			adv7183_read(sd, ADV7183_CONTRAST));
+	v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
+			adv7183_read(sd, ADV7183_BRIGHTNESS));
+	v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
+			adv7183_read(sd, ADV7183_HUE));
+	v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
+			adv7183_read(sd, ADV7183_DEF_Y));
+	v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
+			adv7183_read(sd, ADV7183_DEF_C));
+	v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_ADI_CTRL));
+	v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
+			adv7183_read(sd, ADV7183_POW_MANAGE));
+	v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_STATUS_1),
+			adv7183_read(sd, ADV7183_STATUS_2),
+			adv7183_read(sd, ADV7183_STATUS_3));
+	v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
+			adv7183_read(sd, ADV7183_IDENT));
+	v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
+	v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
+			adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
+	v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
+			adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
+	v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
+	v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
+			adv7183_read(sd, ADV7183_ADI_CTRL_2));
+	v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
+	v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
+	v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
+	v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
+			adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
+	v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
+			adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
+	v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
+			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
+			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
+	v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
+			adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
+			adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
+	v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
+			adv7183_read(sd, ADV7183_POLARITY));
+	v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
+			adv7183_read(sd, ADV7183_ADC_CTRL));
+	v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_SD_OFFSET_CB),
+			adv7183_read(sd, ADV7183_SD_OFFSET_CR));
+	v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
+			adv7183_read(sd, ADV7183_SD_SATURATION_CB),
+			adv7183_read(sd, ADV7183_SD_SATURATION_CR));
+	v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
+			adv7183_read(sd, ADV7183_DRIVE_STR));
+	v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
+	return 0;
+}
+
+static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+	struct adv7183 *decoder = to_adv7183(sd);
+	int reg;
+
+	if (std == decoder->std)
+		return 0;
+	reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
+	if (std == V4L2_STD_ALL)
+		adv7183_write(sd, ADV7183_IN_CTRL, reg);
+	else {
+		if (std == V4L2_STD_PAL_60)
+			reg |= 0x60;
+		else if (std == V4L2_STD_NTSC_443)
+			reg |= 0x70;
+		else if (std == V4L2_STD_PAL_N)
+			reg |= 0x90;
+		else if (std == V4L2_STD_PAL_M)
+			reg |= 0xA0;
+		else if (std == V4L2_STD_PAL_Nc)
+			reg |= 0xC0;
+		else if (std & V4L2_STD_PAL)
+			reg |= 0x80;
+		else if (std & V4L2_STD_NTSC)
+			reg |= 0x50;
+		else if (std & V4L2_STD_SECAM)
+			reg |= 0xE0;
+		else
+			return -EINVAL;
+		adv7183_write(sd, ADV7183_IN_CTRL, reg);
+	}
+
+	decoder->std = std;
+
+	return 0;
+}
+
+static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
+{
+	int reg;
+
+	reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
+	adv7183_write(sd, ADV7183_POW_MANAGE, reg);
+	/* wait 5ms before any further i2c writes are performed */
+	usleep_range(5000, 10000);
+	return 0;
+}
+
+static int adv7183_s_routing(struct v4l2_subdev *sd,
+				u32 input, u32 output, u32 config)
+{
+	struct adv7183 *decoder = to_adv7183(sd);
+	int reg;
+
+	if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
+		return -EINVAL;
+
+	if (input != decoder->input) {
+		decoder->input = input;
+		reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
+		switch (input) {
+		case ADV7183_COMPOSITE1:
+			reg |= 0x1;
+			break;
+		case ADV7183_COMPOSITE2:
+			reg |= 0x2;
+			break;
+		case ADV7183_COMPOSITE3:
+			reg |= 0x3;
+			break;
+		case ADV7183_COMPOSITE4:
+			reg |= 0x4;
+			break;
+		case ADV7183_COMPOSITE5:
+			reg |= 0x5;
+			break;
+		case ADV7183_COMPOSITE6:
+			reg |= 0xB;
+			break;
+		case ADV7183_COMPOSITE7:
+			reg |= 0xC;
+			break;
+		case ADV7183_COMPOSITE8:
+			reg |= 0xD;
+			break;
+		case ADV7183_COMPOSITE9:
+			reg |= 0xE;
+			break;
+		case ADV7183_COMPOSITE10:
+			reg |= 0xF;
+			break;
+		case ADV7183_SVIDEO0:
+			reg |= 0x6;
+			break;
+		case ADV7183_SVIDEO1:
+			reg |= 0x7;
+			break;
+		case ADV7183_SVIDEO2:
+			reg |= 0x8;
+			break;
+		case ADV7183_COMPONENT0:
+			reg |= 0x9;
+			break;
+		case ADV7183_COMPONENT1:
+			reg |= 0xA;
+			break;
+		default:
+			break;
+		}
+		adv7183_write(sd, ADV7183_IN_CTRL, reg);
+	}
+
+	if (output != decoder->output) {
+		decoder->output = output;
+		reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
+		switch (output) {
+		case ADV7183_16BIT_OUT:
+			reg |= 0x9;
+			break;
+		default:
+			reg |= 0xC;
+			break;
+		}
+		adv7183_write(sd, ADV7183_OUT_CTRL, reg);
+	}
+
+	return 0;
+}
+
+static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	int val = ctrl->val;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		if (val < 0)
+			val = 127 - val;
+		adv7183_write(sd, ADV7183_BRIGHTNESS, val);
+		break;
+	case V4L2_CID_CONTRAST:
+		adv7183_write(sd, ADV7183_CONTRAST, val);
+		break;
+	case V4L2_CID_SATURATION:
+		adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
+		adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
+		break;
+	case V4L2_CID_HUE:
+		adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
+		adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	struct adv7183 *decoder = to_adv7183(sd);
+	int reg;
+
+	if (decoder->std == V4L2_STD_ALL) {
+		reg = adv7183_read(sd, ADV7183_STATUS_1);
+		switch ((reg >> 0x4) & 0x7) {
+		case 0:
+			*std = V4L2_STD_NTSC;
+			break;
+		case 1:
+			*std = V4L2_STD_NTSC_443;
+			break;
+		case 2:
+			*std = V4L2_STD_PAL_M;
+			break;
+		case 3:
+			*std = V4L2_STD_PAL_60;
+			break;
+		case 4:
+			*std = V4L2_STD_PAL;
+			break;
+		case 5:
+			*std = V4L2_STD_SECAM;
+			break;
+		case 6:
+			*std = V4L2_STD_PAL_Nc;
+			break;
+		case 7:
+			*std = V4L2_STD_SECAM;
+			break;
+		default:
+			*std = V4L2_STD_UNKNOWN;
+			break;
+		}
+	} else
+		*std = decoder->std;
+	return 0;
+}
+
+static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+	int reg;
+
+	*status = V4L2_IN_ST_NO_SIGNAL;
+	reg = adv7183_read(sd, ADV7183_STATUS_1);
+	if (reg < 0)
+		return reg;
+	else if (reg & 0x1)
+		*status = 0;
+	return 0;
+}
+
+static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+				enum v4l2_mbus_pixelcode *code)
+{
+	if (index > 0)
+		return -EINVAL;
+
+	*code = V4L2_MBUS_FMT_UYVY8_2X8;
+	return 0;
+}
+
+static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
+				struct v4l2_mbus_framefmt *fmt)
+{
+	v4l2_std_id std;
+
+	fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	adv7183_querystd(sd, &std);
+	if (std & V4L2_STD_525_60) {
+		fmt->field = V4L2_FIELD_SEQ_TB;
+		fmt->width = 720;
+		fmt->height = 480;
+	} else {
+		fmt->field = V4L2_FIELD_SEQ_BT;
+		fmt->width = 720;
+		fmt->height = 576;
+	}
+	return 0;
+}
+
+static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
+				struct v4l2_mbus_framefmt *fmt)
+{
+	struct adv7183 *decoder = to_adv7183(sd);
+
+	adv7183_try_mbus_fmt(sd, fmt);
+	decoder->fmt = *fmt;
+	return 0;
+}
+
+static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
+				struct v4l2_mbus_framefmt *fmt)
+{
+	struct adv7183 *decoder = to_adv7183(sd);
+
+	*fmt = decoder->fmt;
+	return 0;
+}
+
+static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct adv7183 *decoder = to_adv7183(sd);
+
+	if (enable)
+		gpio_direction_output(decoder->oe_pin, 0);
+	else
+		gpio_direction_output(decoder->oe_pin, 1);
+	udelay(1);
+	return 0;
+}
+
+static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
+		struct v4l2_dbg_chip_ident *chip)
+{
+	int rev;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	/* 0x11 for adv7183, 0x13 for adv7183b */
+	rev = adv7183_read(sd, ADV7183_IDENT);
+
+	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (!v4l2_chip_match_i2c_client(client, &reg->match))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	reg->val = adv7183_read(sd, reg->reg & 0xff);
+	reg->size = 1;
+	return 0;
+}
+
+static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (!v4l2_chip_match_i2c_client(client, &reg->match))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
+	return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
+	.s_ctrl = adv7183_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7183_core_ops = {
+	.log_status = adv7183_log_status,
+	.s_std = adv7183_s_std,
+	.reset = adv7183_reset,
+	.g_chip_ident = adv7183_g_chip_ident,
+	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+	.g_ctrl = v4l2_subdev_g_ctrl,
+	.s_ctrl = v4l2_subdev_s_ctrl,
+	.queryctrl = v4l2_subdev_queryctrl,
+	.querymenu = v4l2_subdev_querymenu,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = adv7183_g_register,
+	.s_register = adv7183_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops adv7183_video_ops = {
+	.s_routing = adv7183_s_routing,
+	.querystd = adv7183_querystd,
+	.g_input_status = adv7183_g_input_status,
+	.enum_mbus_fmt = adv7183_enum_mbus_fmt,
+	.try_mbus_fmt = adv7183_try_mbus_fmt,
+	.s_mbus_fmt = adv7183_s_mbus_fmt,
+	.g_mbus_fmt = adv7183_g_mbus_fmt,
+	.s_stream = adv7183_s_stream,
+};
+
+static const struct v4l2_subdev_ops adv7183_ops = {
+	.core = &adv7183_core_ops,
+	.video = &adv7183_video_ops,
+};
+
+static int adv7183_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct adv7183 *decoder;
+	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *hdl;
+	int ret;
+	const unsigned *pin_array;
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EIO;
+
+	v4l_info(client, "chip found @ 0x%02x (%s)\n",
+			client->addr << 1, client->adapter->name);
+
+	pin_array = client->dev.platform_data;
+	if (pin_array == NULL)
+		return -EINVAL;
+
+	decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
+	if (decoder == NULL)
+		return -ENOMEM;
+
+	decoder->reset_pin = pin_array[0];
+	decoder->oe_pin = pin_array[1];
+
+	if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
+		v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
+		ret = -EBUSY;
+		goto err_free_decoder;
+	}
+
+	if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
+		v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
+		ret = -EBUSY;
+		goto err_free_reset;
+	}
+
+	sd = &decoder->sd;
+	v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
+
+	hdl = &decoder->hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
+	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
+	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+			V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
+	/* hook the control handler into the driver */
+	sd->ctrl_handler = hdl;
+	if (hdl->error) {
+		ret = hdl->error;
+
+		v4l2_ctrl_handler_free(hdl);
+		goto err_free_oe;
+	}
+
+	decoder->std = V4L2_STD_ALL; /* Default is autodetect */
+	decoder->input = ADV7183_COMPOSITE4;
+	decoder->output = ADV7183_8BIT_OUT;
+
+	gpio_direction_output(decoder->oe_pin, 1);
+	/* reset chip */
+	gpio_direction_output(decoder->reset_pin, 0);
+	/* reset pulse width at least 5ms */
+	mdelay(10);
+	gpio_direction_output(decoder->reset_pin, 1);
+	/* wait 5ms before any further i2c writes are performed */
+	mdelay(5);
+
+	adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
+
+	/* initialize the hardware to the default control values */
+	v4l2_ctrl_handler_setup(hdl);
+
+	return 0;
+err_free_oe:
+	gpio_free(decoder->oe_pin);
+err_free_reset:
+	gpio_free(decoder->reset_pin);
+err_free_decoder:
+	kfree(decoder);
+	return ret;
+}
+
+static int adv7183_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct adv7183 *decoder = to_adv7183(sd);
+
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	gpio_free(decoder->oe_pin);
+	gpio_free(decoder->reset_pin);
+	kfree(decoder);
+	return 0;
+}
+
+static const struct i2c_device_id adv7183_id[] = {
+	{"adv7183", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7183_id);
+
+static struct i2c_driver adv7183_driver = {
+	.driver = {
+		.owner  = THIS_MODULE,
+		.name   = "adv7183",
+	},
+	.probe          = adv7183_probe,
+	.remove         = __devexit_p(adv7183_remove),
+	.id_table       = adv7183_id,
+};
+
+static __init int adv7183_init(void)
+{
+	return i2c_add_driver(&adv7183_driver);
+}
+
+static __exit void adv7183_exit(void)
+{
+	i2c_del_driver(&adv7183_driver);
+}
+
+module_init(adv7183_init);
+module_exit(adv7183_exit);
+
+MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h
new file mode 100644
index 0000000..4a5b7d2
--- /dev/null
+++ b/drivers/media/video/adv7183_regs.h
@@ -0,0 +1,107 @@
+/*
+ * adv7183 - Analog Devices ADV7183 video decoder registers
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _ADV7183_REGS_H_
+#define _ADV7183_REGS_H_
+
+#define ADV7183_IN_CTRL            0x00 /* Input control */
+#define ADV7183_VD_SEL             0x01 /* Video selection */
+#define ADV7183_OUT_CTRL           0x03 /* Output control */
+#define ADV7183_EXT_OUT_CTRL       0x04 /* Extended output control */
+#define ADV7183_AUTO_DET_EN        0x07 /* Autodetect enable */
+#define ADV7183_CONTRAST           0x08 /* Contrast */
+#define ADV7183_BRIGHTNESS         0x0A /* Brightness */
+#define ADV7183_HUE                0x0B /* Hue */
+#define ADV7183_DEF_Y              0x0C /* Default value Y */
+#define ADV7183_DEF_C              0x0D /* Default value C */
+#define ADV7183_ADI_CTRL           0x0E /* ADI control */
+#define ADV7183_POW_MANAGE         0x0F /* Power Management */
+#define ADV7183_STATUS_1           0x10 /* Status 1 */
+#define ADV7183_IDENT              0x11 /* Ident */
+#define ADV7183_STATUS_2           0x12 /* Status 2 */
+#define ADV7183_STATUS_3           0x13 /* Status 3 */
+#define ADV7183_ANAL_CLAMP_CTRL    0x14 /* Analog clamp control */
+#define ADV7183_DIGI_CLAMP_CTRL_1  0x15 /* Digital clamp control 1 */
+#define ADV7183_SHAP_FILT_CTRL     0x17 /* Shaping filter control */
+#define ADV7183_SHAP_FILT_CTRL_2   0x18 /* Shaping filter control 2 */
+#define ADV7183_COMB_FILT_CTRL     0x19 /* Comb filter control */
+#define ADV7183_ADI_CTRL_2         0x1D /* ADI control 2 */
+#define ADV7183_PIX_DELAY_CTRL     0x27 /* Pixel delay control */
+#define ADV7183_MISC_GAIN_CTRL     0x2B /* Misc gain control */
+#define ADV7183_AGC_MODE_CTRL      0x2C /* AGC mode control */
+#define ADV7183_CHRO_GAIN_CTRL_1   0x2D /* Chroma gain control 1 */
+#define ADV7183_CHRO_GAIN_CTRL_2   0x2E /* Chroma gain control 2 */
+#define ADV7183_LUMA_GAIN_CTRL_1   0x2F /* Luma gain control 1 */
+#define ADV7183_LUMA_GAIN_CTRL_2   0x30 /* Luma gain control 2 */
+#define ADV7183_VS_FIELD_CTRL_1    0x31 /* Vsync field control 1 */
+#define ADV7183_VS_FIELD_CTRL_2    0x32 /* Vsync field control 2 */
+#define ADV7183_VS_FIELD_CTRL_3    0x33 /* Vsync field control 3 */
+#define ADV7183_HS_POS_CTRL_1      0x34 /* Hsync positon control 1 */
+#define ADV7183_HS_POS_CTRL_2      0x35 /* Hsync positon control 2 */
+#define ADV7183_HS_POS_CTRL_3      0x36 /* Hsync positon control 3 */
+#define ADV7183_POLARITY           0x37 /* Polarity */
+#define ADV7183_NTSC_COMB_CTRL     0x38 /* NTSC comb control */
+#define ADV7183_PAL_COMB_CTRL      0x39 /* PAL comb control */
+#define ADV7183_ADC_CTRL           0x3A /* ADC control */
+#define ADV7183_MAN_WIN_CTRL       0x3D /* Manual window control */
+#define ADV7183_RESAMPLE_CTRL      0x41 /* Resample control */
+#define ADV7183_GEMSTAR_CTRL_1     0x48 /* Gemstar ctrl 1 */
+#define ADV7183_GEMSTAR_CTRL_2     0x49 /* Gemstar ctrl 2 */
+#define ADV7183_GEMSTAR_CTRL_3     0x4A /* Gemstar ctrl 3 */
+#define ADV7183_GEMSTAR_CTRL_4     0x4B /* Gemstar ctrl 4 */
+#define ADV7183_GEMSTAR_CTRL_5     0x4C /* Gemstar ctrl 5 */
+#define ADV7183_CTI_DNR_CTRL_1     0x4D /* CTI DNR ctrl 1 */
+#define ADV7183_CTI_DNR_CTRL_2     0x4E /* CTI DNR ctrl 2 */
+#define ADV7183_CTI_DNR_CTRL_4     0x50 /* CTI DNR ctrl 4 */
+#define ADV7183_LOCK_CNT           0x51 /* Lock count */
+#define ADV7183_FREE_LINE_LEN      0x8F /* Free-Run line length 1 */
+#define ADV7183_VBI_INFO           0x90 /* VBI info */
+#define ADV7183_WSS_1              0x91 /* WSS 1 */
+#define ADV7183_WSS_2              0x92 /* WSS 2 */
+#define ADV7183_EDTV_1             0x93 /* EDTV 1 */
+#define ADV7183_EDTV_2             0x94 /* EDTV 2 */
+#define ADV7183_EDTV_3             0x95 /* EDTV 3 */
+#define ADV7183_CGMS_1             0x96 /* CGMS 1 */
+#define ADV7183_CGMS_2             0x97 /* CGMS 2 */
+#define ADV7183_CGMS_3             0x98 /* CGMS 3 */
+#define ADV7183_CCAP_1             0x99 /* CCAP 1 */
+#define ADV7183_CCAP_2             0x9A /* CCAP 2 */
+#define ADV7183_LETTERBOX_1        0x9B /* Letterbox 1 */
+#define ADV7183_LETTERBOX_2        0x9C /* Letterbox 2 */
+#define ADV7183_LETTERBOX_3        0x9D /* Letterbox 3 */
+#define ADV7183_CRC_EN             0xB2 /* CRC enable */
+#define ADV7183_ADC_SWITCH_1       0xC3 /* ADC switch 1 */
+#define ADV7183_ADC_SWITCH_2       0xC4 /* ADC swithc 2 */
+#define ADV7183_LETTERBOX_CTRL_1   0xDC /* Letterbox control 1 */
+#define ADV7183_LETTERBOX_CTRL_2   0xDD /* Letterbox control 2 */
+#define ADV7183_SD_OFFSET_CB       0xE1 /* SD offset Cb */
+#define ADV7183_SD_OFFSET_CR       0xE2 /* SD offset Cr */
+#define ADV7183_SD_SATURATION_CB   0xE3 /* SD saturation Cb */
+#define ADV7183_SD_SATURATION_CR   0xE4 /* SD saturation Cr */
+#define ADV7183_NTSC_V_BEGIN       0xE5 /* NTSC V bit begin */
+#define ADV7183_NTSC_V_END         0xE6 /* NTSC V bit end */
+#define ADV7183_NTSC_F_TOGGLE      0xE7 /* NTSC F bit toggle */
+#define ADV7183_PAL_V_BEGIN        0xE8 /* PAL V bit begin */
+#define ADV7183_PAL_V_END          0xE9 /* PAL V bit end */
+#define ADV7183_PAL_F_TOGGLE       0xEA /* PAL F bit toggle */
+#define ADV7183_DRIVE_STR          0xF4 /* Drive strength */
+#define ADV7183_IF_COMP_CTRL       0xF8 /* IF comp control */
+#define ADV7183_VS_MODE_CTRL       0xF9 /* VS mode control */
+
+#endif
diff --git a/include/media/adv7183.h b/include/media/adv7183.h
new file mode 100644
index 0000000..c5c2d37
--- /dev/null
+++ b/include/media/adv7183.h
@@ -0,0 +1,47 @@
+/*
+ * adv7183.h - definition for adv7183 inputs and outputs
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _ADV7183_H_
+#define _ADV7183_H_
+
+/* ADV7183 HW inputs */
+#define ADV7183_COMPOSITE0  0  /* CVBS in on AIN1 */
+#define ADV7183_COMPOSITE1  1  /* CVBS in on AIN2 */
+#define ADV7183_COMPOSITE2  2  /* CVBS in on AIN3 */
+#define ADV7183_COMPOSITE3  3  /* CVBS in on AIN4 */
+#define ADV7183_COMPOSITE4  4  /* CVBS in on AIN5 */
+#define ADV7183_COMPOSITE5  5  /* CVBS in on AIN6 */
+#define ADV7183_COMPOSITE6  6  /* CVBS in on AIN7 */
+#define ADV7183_COMPOSITE7  7  /* CVBS in on AIN8 */
+#define ADV7183_COMPOSITE8  8  /* CVBS in on AIN9 */
+#define ADV7183_COMPOSITE9  9  /* CVBS in on AIN10 */
+#define ADV7183_COMPOSITE10 10 /* CVBS in on AIN11 */
+
+#define ADV7183_SVIDEO0     11 /* Y on AIN1, C on AIN4 */
+#define ADV7183_SVIDEO1     12 /* Y on AIN2, C on AIN5 */
+#define ADV7183_SVIDEO2     13 /* Y on AIN3, C on AIN6 */
+
+#define ADV7183_COMPONENT0  14 /* Y on AIN1, Pr on AIN4, Pb on AIN5 */
+#define ADV7183_COMPONENT1  15 /* Y on AIN2, Pr on AIN3, Pb on AIN6 */
+
+/* ADV7183 HW outputs */
+#define ADV7183_8BIT_OUT    0
+#define ADV7183_16BIT_OUT   1
+
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 63fd9d3..20fd16d 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -162,6 +162,9 @@ enum {
 	/* module adv7180: just ident 7180 */
 	V4L2_IDENT_ADV7180 = 7180,
 
+	/* module adv7183: just ident 7183 */
+	V4L2_IDENT_ADV7183 = 7183,
+
 	/* module saa7185: just ident 7185 */
 	V4L2_IDENT_SAA7185 = 7185,
 
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/4 v2][FOR 3.1] v4l2: add vs6624 sensor driver
  2011-09-19 20:59 [PATCH 1/4 v2][FOR 3.1] v4l2: add vb2_get_unmapped_area in vb2 core Scott Jiang
  2011-09-19 20:59 ` [PATCH 2/4 v2][FOR 3.1] v4l2: add adv7183 decoder driver Scott Jiang
@ 2011-09-19 20:59 ` Scott Jiang
  2011-09-26 13:39   ` Hans Verkuil
  2011-09-19 20:59 ` [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver Scott Jiang
  2 siblings, 1 reply; 12+ messages in thread
From: Scott Jiang @ 2011-09-19 20:59 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Laurent Pinchart, Hans Verkuil,
	Guennadi Liakhovetski
  Cc: linux-media, uclinux-dist-devel, Scott Jiang

this is a v4l2 sensor-level driver for the ST VS6624 camera

Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>
---
 drivers/media/video/Kconfig       |   10 +
 drivers/media/video/Makefile      |    1 +
 drivers/media/video/vs6624.c      |  930 +++++++++++++++++++++++++++++++++++++
 drivers/media/video/vs6624_regs.h |  337 ++++++++++++++
 include/media/v4l2-chip-ident.h   |    3 +
 5 files changed, 1281 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/vs6624.c
 create mode 100644 drivers/media/video/vs6624_regs.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index af8ed6b..1c03abf 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -477,6 +477,16 @@ config VIDEO_OV7670
 	  OV7670 VGA camera.  It currently only works with the M88ALP01
 	  controller.
 
+config VIDEO_VS6624
+	tristate "ST VS6624 sensor support"
+	depends on VIDEO_V4L2 && I2C
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the ST VS6624
+	  camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called vs6624.
+
 config VIDEO_MT9V011
 	tristate "Micron mt9v011 sensor support"
 	depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index b0329ae..03b55ed 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
 obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
 obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
 obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
+obj-$(CONFIG_VIDEO_VS6624)  += vs6624.o
 obj-$(CONFIG_VIDEO_BT819) += bt819.o
 obj-$(CONFIG_VIDEO_BT856) += bt856.o
 obj-$(CONFIG_VIDEO_BT866) += bt866.o
diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c
new file mode 100644
index 0000000..50c2aa5
--- /dev/null
+++ b/drivers/media/video/vs6624.c
@@ -0,0 +1,930 @@
+/*
+ * vs6624.c ST VS6624 CMOS image sensor driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+
+#include "vs6624_regs.h"
+
+#define VGA_WIDTH       640
+#define VGA_HEIGHT      480
+#define QVGA_WIDTH      320
+#define QVGA_HEIGHT     240
+#define QQVGA_WIDTH     160
+#define QQVGA_HEIGHT    120
+#define CIF_WIDTH       352
+#define CIF_HEIGHT      288
+#define QCIF_WIDTH      176
+#define QCIF_HEIGHT     144
+#define QQCIF_WIDTH     88
+#define QQCIF_HEIGHT    72
+
+#define MAX_FRAME_RATE  30
+
+struct vs6624 {
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_fract frame_rate;
+	struct v4l2_mbus_framefmt fmt;
+	unsigned ce_pin;
+};
+
+static const struct vs6624_format {
+	enum v4l2_mbus_pixelcode mbus_code;
+	enum v4l2_colorspace colorspace;
+} vs6624_formats[] = {
+	{
+		.mbus_code      = V4L2_MBUS_FMT_UYVY8_2X8,
+		.colorspace     = V4L2_COLORSPACE_JPEG,
+	},
+	{
+		.mbus_code      = V4L2_MBUS_FMT_YUYV8_2X8,
+		.colorspace     = V4L2_COLORSPACE_JPEG,
+	},
+	{
+		.mbus_code      = V4L2_MBUS_FMT_RGB565_2X8_LE,
+		.colorspace     = V4L2_COLORSPACE_SRGB,
+	},
+};
+
+static struct v4l2_mbus_framefmt vs6624_default_fmt = {
+	.width = VGA_WIDTH,
+	.height = VGA_HEIGHT,
+	.code = V4L2_MBUS_FMT_UYVY8_2X8,
+	.field = V4L2_FIELD_NONE,
+	.colorspace = V4L2_COLORSPACE_JPEG,
+};
+
+static const u16 vs6624_p1[] = {
+	0x8104, 0x03,
+	0x8105, 0x01,
+	0xc900, 0x03,
+	0xc904, 0x47,
+	0xc905, 0x10,
+	0xc906, 0x80,
+	0xc907, 0x3a,
+	0x903a, 0x02,
+	0x903b, 0x47,
+	0x903c, 0x15,
+	0xc908, 0x31,
+	0xc909, 0xdc,
+	0xc90a, 0x80,
+	0xc90b, 0x44,
+	0x9044, 0x02,
+	0x9045, 0x31,
+	0x9046, 0xe2,
+	0xc90c, 0x07,
+	0xc90d, 0xe0,
+	0xc90e, 0x80,
+	0xc90f, 0x47,
+	0x9047, 0x90,
+	0x9048, 0x83,
+	0x9049, 0x81,
+	0x904a, 0xe0,
+	0x904b, 0x60,
+	0x904c, 0x08,
+	0x904d, 0x90,
+	0x904e, 0xc0,
+	0x904f, 0x43,
+	0x9050, 0x74,
+	0x9051, 0x01,
+	0x9052, 0xf0,
+	0x9053, 0x80,
+	0x9054, 0x05,
+	0x9055, 0xE4,
+	0x9056, 0x90,
+	0x9057, 0xc0,
+	0x9058, 0x43,
+	0x9059, 0xf0,
+	0x905a, 0x02,
+	0x905b, 0x07,
+	0x905c, 0xec,
+	0xc910, 0x5d,
+	0xc911, 0xca,
+	0xc912, 0x80,
+	0xc913, 0x5d,
+	0x905d, 0xa3,
+	0x905e, 0x04,
+	0x905f, 0xf0,
+	0x9060, 0xa3,
+	0x9061, 0x04,
+	0x9062, 0xf0,
+	0x9063, 0x22,
+	0xc914, 0x72,
+	0xc915, 0x92,
+	0xc916, 0x80,
+	0xc917, 0x64,
+	0x9064, 0x74,
+	0x9065, 0x01,
+	0x9066, 0x02,
+	0x9067, 0x72,
+	0x9068, 0x95,
+	0xc918, 0x47,
+	0xc919, 0xf2,
+	0xc91a, 0x81,
+	0xc91b, 0x69,
+	0x9169, 0x74,
+	0x916a, 0x02,
+	0x916b, 0xf0,
+	0x916c, 0xec,
+	0x916d, 0xb4,
+	0x916e, 0x10,
+	0x916f, 0x0a,
+	0x9170, 0x90,
+	0x9171, 0x80,
+	0x9172, 0x16,
+	0x9173, 0xe0,
+	0x9174, 0x70,
+	0x9175, 0x04,
+	0x9176, 0x90,
+	0x9177, 0xd3,
+	0x9178, 0xc4,
+	0x9179, 0xf0,
+	0x917a, 0x22,
+	0xc91c, 0x0a,
+	0xc91d, 0xbe,
+	0xc91e, 0x80,
+	0xc91f, 0x73,
+	0x9073, 0xfc,
+	0x9074, 0xa3,
+	0x9075, 0xe0,
+	0x9076, 0xf5,
+	0x9077, 0x82,
+	0x9078, 0x8c,
+	0x9079, 0x83,
+	0x907a, 0xa3,
+	0x907b, 0xa3,
+	0x907c, 0xe0,
+	0x907d, 0xfc,
+	0x907e, 0xa3,
+	0x907f, 0xe0,
+	0x9080, 0xc3,
+	0x9081, 0x9f,
+	0x9082, 0xff,
+	0x9083, 0xec,
+	0x9084, 0x9e,
+	0x9085, 0xfe,
+	0x9086, 0x02,
+	0x9087, 0x0a,
+	0x9088, 0xea,
+	0xc920, 0x47,
+	0xc921, 0x38,
+	0xc922, 0x80,
+	0xc923, 0x89,
+	0x9089, 0xec,
+	0x908a, 0xd3,
+	0x908b, 0x94,
+	0x908c, 0x20,
+	0x908d, 0x40,
+	0x908e, 0x01,
+	0x908f, 0x1c,
+	0x9090, 0x90,
+	0x9091, 0xd3,
+	0x9092, 0xd4,
+	0x9093, 0xec,
+	0x9094, 0xf0,
+	0x9095, 0x02,
+	0x9096, 0x47,
+	0x9097, 0x3d,
+	0xc924, 0x45,
+	0xc925, 0xca,
+	0xc926, 0x80,
+	0xc927, 0x98,
+	0x9098, 0x12,
+	0x9099, 0x77,
+	0x909a, 0xd6,
+	0x909b, 0x02,
+	0x909c, 0x45,
+	0x909d, 0xcd,
+	0xc928, 0x20,
+	0xc929, 0xd5,
+	0xc92a, 0x80,
+	0xc92b, 0x9e,
+	0x909e, 0x90,
+	0x909f, 0x82,
+	0x90a0, 0x18,
+	0x90a1, 0xe0,
+	0x90a2, 0xb4,
+	0x90a3, 0x03,
+	0x90a4, 0x0e,
+	0x90a5, 0x90,
+	0x90a6, 0x83,
+	0x90a7, 0xbf,
+	0x90a8, 0xe0,
+	0x90a9, 0x60,
+	0x90aa, 0x08,
+	0x90ab, 0x90,
+	0x90ac, 0x81,
+	0x90ad, 0xfc,
+	0x90ae, 0xe0,
+	0x90af, 0xff,
+	0x90b0, 0xc3,
+	0x90b1, 0x13,
+	0x90b2, 0xf0,
+	0x90b3, 0x90,
+	0x90b4, 0x81,
+	0x90b5, 0xfc,
+	0x90b6, 0xe0,
+	0x90b7, 0xff,
+	0x90b8, 0x02,
+	0x90b9, 0x20,
+	0x90ba, 0xda,
+	0xc92c, 0x70,
+	0xc92d, 0xbc,
+	0xc92e, 0x80,
+	0xc92f, 0xbb,
+	0x90bb, 0x90,
+	0x90bc, 0x82,
+	0x90bd, 0x18,
+	0x90be, 0xe0,
+	0x90bf, 0xb4,
+	0x90c0, 0x03,
+	0x90c1, 0x06,
+	0x90c2, 0x90,
+	0x90c3, 0xc1,
+	0x90c4, 0x06,
+	0x90c5, 0x74,
+	0x90c6, 0x05,
+	0x90c7, 0xf0,
+	0x90c8, 0x90,
+	0x90c9, 0xd3,
+	0x90ca, 0xa0,
+	0x90cb, 0x02,
+	0x90cc, 0x70,
+	0x90cd, 0xbf,
+	0xc930, 0x72,
+	0xc931, 0x21,
+	0xc932, 0x81,
+	0xc933, 0x3b,
+	0x913b, 0x7d,
+	0x913c, 0x02,
+	0x913d, 0x7f,
+	0x913e, 0x7b,
+	0x913f, 0x02,
+	0x9140, 0x72,
+	0x9141, 0x25,
+	0xc934, 0x28,
+	0xc935, 0xae,
+	0xc936, 0x80,
+	0xc937, 0xd2,
+	0x90d2, 0xf0,
+	0x90d3, 0x90,
+	0x90d4, 0xd2,
+	0x90d5, 0x0a,
+	0x90d6, 0x02,
+	0x90d7, 0x28,
+	0x90d8, 0xb4,
+	0xc938, 0x28,
+	0xc939, 0xb1,
+	0xc93a, 0x80,
+	0xc93b, 0xd9,
+	0x90d9, 0x90,
+	0x90da, 0x83,
+	0x90db, 0xba,
+	0x90dc, 0xe0,
+	0x90dd, 0xff,
+	0x90de, 0x90,
+	0x90df, 0xd2,
+	0x90e0, 0x08,
+	0x90e1, 0xe0,
+	0x90e2, 0xe4,
+	0x90e3, 0xef,
+	0x90e4, 0xf0,
+	0x90e5, 0xa3,
+	0x90e6, 0xe0,
+	0x90e7, 0x74,
+	0x90e8, 0xff,
+	0x90e9, 0xf0,
+	0x90ea, 0x90,
+	0x90eb, 0xd2,
+	0x90ec, 0x0a,
+	0x90ed, 0x02,
+	0x90ee, 0x28,
+	0x90ef, 0xb4,
+	0xc93c, 0x29,
+	0xc93d, 0x79,
+	0xc93e, 0x80,
+	0xc93f, 0xf0,
+	0x90f0, 0xf0,
+	0x90f1, 0x90,
+	0x90f2, 0xd2,
+	0x90f3, 0x0e,
+	0x90f4, 0x02,
+	0x90f5, 0x29,
+	0x90f6, 0x7f,
+	0xc940, 0x29,
+	0xc941, 0x7c,
+	0xc942, 0x80,
+	0xc943, 0xf7,
+	0x90f7, 0x90,
+	0x90f8, 0x83,
+	0x90f9, 0xba,
+	0x90fa, 0xe0,
+	0x90fb, 0xff,
+	0x90fc, 0x90,
+	0x90fd, 0xd2,
+	0x90fe, 0x0c,
+	0x90ff, 0xe0,
+	0x9100, 0xe4,
+	0x9101, 0xef,
+	0x9102, 0xf0,
+	0x9103, 0xa3,
+	0x9104, 0xe0,
+	0x9105, 0x74,
+	0x9106, 0xff,
+	0x9107, 0xf0,
+	0x9108, 0x90,
+	0x9109, 0xd2,
+	0x910a, 0x0e,
+	0x910b, 0x02,
+	0x910c, 0x29,
+	0x910d, 0x7f,
+	0xc944, 0x2a,
+	0xc945, 0x42,
+	0xc946, 0x81,
+	0xc947, 0x0e,
+	0x910e, 0xf0,
+	0x910f, 0x90,
+	0x9110, 0xd2,
+	0x9111, 0x12,
+	0x9112, 0x02,
+	0x9113, 0x2a,
+	0x9114, 0x48,
+	0xc948, 0x2a,
+	0xc949, 0x45,
+	0xc94a, 0x81,
+	0xc94b, 0x15,
+	0x9115, 0x90,
+	0x9116, 0x83,
+	0x9117, 0xba,
+	0x9118, 0xe0,
+	0x9119, 0xff,
+	0x911a, 0x90,
+	0x911b, 0xd2,
+	0x911c, 0x10,
+	0x911d, 0xe0,
+	0x911e, 0xe4,
+	0x911f, 0xef,
+	0x9120, 0xf0,
+	0x9121, 0xa3,
+	0x9122, 0xe0,
+	0x9123, 0x74,
+	0x9124, 0xff,
+	0x9125, 0xf0,
+	0x9126, 0x90,
+	0x9127, 0xd2,
+	0x9128, 0x12,
+	0x9129, 0x02,
+	0x912a, 0x2a,
+	0x912b, 0x48,
+	0xc900, 0x01,
+	0x0000, 0x00,
+};
+
+static const u16 vs6624_p2[] = {
+	0x806f, 0x01,
+	0x058c, 0x01,
+	0x0000, 0x00,
+};
+
+static const u16 vs6624_run_setup[] = {
+	0x1d18, 0x00,				/* Enableconstrainedwhitebalance */
+	VS6624_PEAK_MIN_OUT_G_MSB, 0x3c,	/* Damper PeakGain Output MSB */
+	VS6624_PEAK_MIN_OUT_G_LSB, 0x66,	/* Damper PeakGain Output LSB */
+	VS6624_CM_LOW_THR_MSB, 0x65,		/* Damper Low MSB */
+	VS6624_CM_LOW_THR_LSB, 0xd1,		/* Damper Low LSB */
+	VS6624_CM_HIGH_THR_MSB, 0x66,		/* Damper High MSB */
+	VS6624_CM_HIGH_THR_LSB, 0x62,		/* Damper High LSB */
+	VS6624_CM_MIN_OUT_MSB, 0x00,		/* Damper Min output MSB */
+	VS6624_CM_MIN_OUT_LSB, 0x00,		/* Damper Min output LSB */
+	VS6624_NORA_DISABLE, 0x00,		/* Nora fDisable */
+	VS6624_NORA_USAGE, 0x04,		/* Nora usage */
+	VS6624_NORA_LOW_THR_MSB, 0x63,		/* Damper Low MSB Changed 0x63 to 0x65 */
+	VS6624_NORA_LOW_THR_LSB, 0xd1,		/* Damper Low LSB */
+	VS6624_NORA_HIGH_THR_MSB, 0x68,		/* Damper High MSB */
+	VS6624_NORA_HIGH_THR_LSB, 0xdd,		/* Damper High LSB */
+	VS6624_NORA_MIN_OUT_MSB, 0x3a,		/* Damper Min output MSB */
+	VS6624_NORA_MIN_OUT_LSB, 0x00,		/* Damper Min output LSB */
+	VS6624_F2B_DISABLE, 0x00,		/* Disable */
+	0x1d8a, 0x30,				/* MAXWeightHigh */
+	0x1d91, 0x62,				/* fpDamperLowThresholdHigh MSB */
+	0x1d92, 0x4a,				/* fpDamperLowThresholdHigh LSB */
+	0x1d95, 0x65,				/* fpDamperHighThresholdHigh MSB */
+	0x1d96, 0x0e,				/* fpDamperHighThresholdHigh LSB */
+	0x1da1, 0x3a,				/* fpMinimumDamperOutputLow MSB */
+	0x1da2, 0xb8,				/* fpMinimumDamperOutputLow LSB */
+	0x1e08, 0x06,				/* MAXWeightLow */
+	0x1e0a, 0x0a,				/* MAXWeightHigh */
+	0x1601, 0x3a,				/* Red A MSB */
+	0x1602, 0x14,				/* Red A LSB */
+	0x1605, 0x3b,				/* Blue A MSB */
+	0x1606, 0x85,				/* BLue A LSB */
+	0x1609, 0x3b,				/* RED B MSB */
+	0x160a, 0x85,				/* RED B LSB */
+	0x160d, 0x3a,				/* Blue B MSB */
+	0x160e, 0x14,				/* Blue B LSB */
+	0x1611, 0x30,				/* Max Distance from Locus MSB */
+	0x1612, 0x8f,				/* Max Distance from Locus MSB */
+	0x1614, 0x01,				/* Enable constrainer */
+	0x0000, 0x00,
+};
+
+static const u16 vs6624_default[] = {
+	VS6624_CONTRAST0, 0x84,
+	VS6624_SATURATION0, 0x75,
+	VS6624_GAMMA0, 0x11,
+	VS6624_CONTRAST1, 0x84,
+	VS6624_SATURATION1, 0x75,
+	VS6624_GAMMA1, 0x11,
+	VS6624_MAN_RG, 0x80,
+	VS6624_MAN_GG, 0x80,
+	VS6624_MAN_BG, 0x80,
+	VS6624_WB_MODE, 0x1,
+	VS6624_EXPO_COMPENSATION, 0xfe,
+	VS6624_EXPO_METER, 0x0,
+	VS6624_LIGHT_FREQ, 0x64,
+	VS6624_PEAK_GAIN, 0xe,
+	VS6624_PEAK_LOW_THR, 0x28,
+	VS6624_HMIRROR0, 0x0,
+	VS6624_VFLIP0, 0x0,
+	VS6624_ZOOM_HSTEP0_MSB, 0x0,
+	VS6624_ZOOM_HSTEP0_LSB, 0x1,
+	VS6624_ZOOM_VSTEP0_MSB, 0x0,
+	VS6624_ZOOM_VSTEP0_LSB, 0x1,
+	VS6624_PAN_HSTEP0_MSB, 0x0,
+	VS6624_PAN_HSTEP0_LSB, 0xf,
+	VS6624_PAN_VSTEP0_MSB, 0x0,
+	VS6624_PAN_VSTEP0_LSB, 0xf,
+	VS6624_SENSOR_MODE, 0x1,
+	VS6624_SYNC_CODE_SETUP, 0x21,
+	VS6624_DISABLE_FR_DAMPER, 0x0,
+	VS6624_FR_DEN, 0x1,
+	VS6624_FR_NUM_LSB, 0xf,
+	VS6624_INIT_PIPE_SETUP, 0x0,
+	VS6624_IMG_FMT0, 0x0,
+	VS6624_YUV_SETUP, 0x1,
+	VS6624_IMAGE_SIZE0, 0x2,
+	0x0000, 0x00,
+};
+
+static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct vs6624, sd);
+}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct vs6624, hdl)->sd;
+}
+
+static int vs6624_read(struct v4l2_subdev *sd, u16 index)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u8 buf[2];
+
+	buf[0] = index >> 8;
+	buf[1] = index;
+	i2c_master_send(client, buf, 2);
+	i2c_master_recv(client, buf, 1);
+
+	return buf[0];
+}
+
+static int vs6624_write(struct v4l2_subdev *sd, u16 index,
+				u8 value)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u8 buf[3];
+
+	buf[0] = index >> 8;
+	buf[1] = index;
+	buf[2] = value;
+
+	return i2c_master_send(client, buf, 3);
+}
+
+static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs)
+{
+	u16 reg;
+	u8 data;
+
+	while (*regs != 0x00) {
+		reg = *regs++;
+		data = *regs++;
+
+		vs6624_write(sd, reg, data);
+	}
+	return 0;
+}
+
+static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+
+	switch (ctrl->id) {
+	case V4L2_CID_CONTRAST:
+		vs6624_write(sd, VS6624_CONTRAST0, ctrl->val);
+		break;
+	case V4L2_CID_SATURATION:
+		vs6624_write(sd, VS6624_SATURATION0, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		vs6624_write(sd, VS6624_HMIRROR0, ctrl->val);
+		break;
+	case V4L2_CID_VFLIP:
+		vs6624_write(sd, VS6624_VFLIP0, ctrl->val);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+				enum v4l2_mbus_pixelcode *code)
+{
+	if (index >= ARRAY_SIZE(vs6624_formats))
+		return -EINVAL;
+
+	*code = vs6624_formats[index].mbus_code;
+	return 0;
+}
+
+static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd,
+				struct v4l2_mbus_framefmt *fmt)
+{
+	int index;
+
+	for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++)
+		if (vs6624_formats[index].mbus_code == fmt->code)
+			break;
+	if (index >= ARRAY_SIZE(vs6624_formats)) {
+		/* default to first format */
+		index = 0;
+		fmt->code = vs6624_formats[0].mbus_code;
+	}
+
+	/* sensor mode is VGA */
+	if (fmt->width > VGA_WIDTH)
+		fmt->width = VGA_WIDTH;
+	if (fmt->height > VGA_HEIGHT)
+		fmt->height = VGA_HEIGHT;
+	fmt->width = fmt->width & (~3);
+	fmt->height = fmt->height & (~3);
+	fmt->field = V4L2_FIELD_NONE;
+	fmt->colorspace = vs6624_formats[index].colorspace;
+	return 0;
+}
+
+static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd,
+				struct v4l2_mbus_framefmt *fmt)
+{
+	struct vs6624 *sensor = to_vs6624(sd);
+	int ret;
+
+	ret = vs6624_try_mbus_fmt(sd, fmt);
+	if (ret)
+		return ret;
+
+	/* set image format */
+	switch (fmt->code) {
+	case V4L2_MBUS_FMT_UYVY8_2X8:
+		vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
+		vs6624_write(sd, VS6624_YUV_SETUP, 0x1);
+		break;
+	case V4L2_MBUS_FMT_YUYV8_2X8:
+		vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
+		vs6624_write(sd, VS6624_YUV_SETUP, 0x3);
+		break;
+	case V4L2_MBUS_FMT_RGB565_2X8_LE:
+		vs6624_write(sd, VS6624_IMG_FMT0, 0x4);
+		vs6624_write(sd, VS6624_RGB_SETUP, 0x0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set image size */
+	if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT))
+		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2);
+	else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT))
+		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4);
+	else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT))
+		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6);
+	else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT))
+		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3);
+	else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT))
+		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5);
+	else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT))
+		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7);
+	else {
+		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8);
+		vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8);
+		vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF);
+		vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8);
+		vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF);
+		vs6624_write(sd, VS6624_CROP_CTRL0, 0x1);
+	}
+
+	sensor->fmt = *fmt;
+
+	return 0;
+}
+
+static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd,
+				struct v4l2_mbus_framefmt *fmt)
+{
+	struct vs6624 *sensor = to_vs6624(sd);
+
+	*fmt = sensor->fmt;
+	return 0;
+}
+
+static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct vs6624 *sensor = to_vs6624(sd);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(cp, 0, sizeof(*cp));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = sensor->frame_rate.denominator;
+	cp->timeperframe.denominator = sensor->frame_rate.numerator;
+	return 0;
+}
+
+static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct vs6624 *sensor = to_vs6624(sd);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+
+	if (tpf->numerator == 0 || tpf->denominator == 0
+		|| (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) {
+		/* reset to max frame rate */
+		tpf->numerator = 1;
+		tpf->denominator = MAX_FRAME_RATE;
+	}
+	sensor->frame_rate.numerator = tpf->denominator;
+	sensor->frame_rate.denominator = tpf->numerator;
+	vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
+	vs6624_write(sd, VS6624_FR_NUM_MSB,
+			sensor->frame_rate.numerator >> 8);
+	vs6624_write(sd, VS6624_FR_NUM_LSB,
+			sensor->frame_rate.numerator & 0xFF);
+	vs6624_write(sd, VS6624_FR_DEN,
+			sensor->frame_rate.denominator & 0xFF);
+	return 0;
+}
+
+static int vs6624_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	if (enable)
+		vs6624_write(sd, VS6624_USER_CMD, 0x2);
+	else
+		vs6624_write(sd, VS6624_USER_CMD, 0x4);
+	udelay(100);
+	return 0;
+}
+
+static int vs6624_g_chip_ident(struct v4l2_subdev *sd,
+		struct v4l2_dbg_chip_ident *chip)
+{
+	int rev;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8)
+		| vs6624_read(sd, VS6624_FW_VSN_MINOR);
+
+	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (!v4l2_chip_match_i2c_client(client, &reg->match))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	reg->val = vs6624_read(sd, reg->reg & 0xffff);
+	reg->size = 1;
+	return 0;
+}
+
+static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	if (!v4l2_chip_match_i2c_client(client, &reg->match))
+		return -EINVAL;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff);
+	return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops vs6624_ctrl_ops = {
+	.s_ctrl = vs6624_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops vs6624_core_ops = {
+	.g_chip_ident = vs6624_g_chip_ident,
+	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+	.g_ctrl = v4l2_subdev_g_ctrl,
+	.s_ctrl = v4l2_subdev_s_ctrl,
+	.queryctrl = v4l2_subdev_queryctrl,
+	.querymenu = v4l2_subdev_querymenu,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register = vs6624_g_register,
+	.s_register = vs6624_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops vs6624_video_ops = {
+	.enum_mbus_fmt = vs6624_enum_mbus_fmt,
+	.try_mbus_fmt = vs6624_try_mbus_fmt,
+	.s_mbus_fmt = vs6624_s_mbus_fmt,
+	.g_mbus_fmt = vs6624_g_mbus_fmt,
+	.s_parm = vs6624_s_parm,
+	.g_parm = vs6624_g_parm,
+	.s_stream = vs6624_s_stream,
+};
+
+static const struct v4l2_subdev_ops vs6624_ops = {
+	.core = &vs6624_core_ops,
+	.video = &vs6624_video_ops,
+};
+
+static int __devinit vs6624_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct vs6624 *sensor;
+	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *hdl;
+	const unsigned *ce;
+	int ret;
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -EIO;
+
+	ce = client->dev.platform_data;
+	if (ce == NULL)
+		return -EINVAL;
+
+	ret = gpio_request(*ce, "VS6624 Chip Enable");
+	if (ret) {
+		v4l_err(client, "failed to request GPIO %d\n", *ce);
+		return ret;
+	}
+	gpio_direction_output(*ce, 1);
+	/* wait 100ms before any further i2c writes are performed */
+	mdelay(100);
+
+	sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+	if (sensor == NULL) {
+		gpio_free(*ce);
+		return -ENOMEM;
+	}
+
+	sd = &sensor->sd;
+	v4l2_i2c_subdev_init(sd, client, &vs6624_ops);
+
+	vs6624_writeregs(sd, vs6624_p1);
+	vs6624_write(sd, VS6624_MICRO_EN, 0x2);
+	vs6624_write(sd, VS6624_DIO_EN, 0x1);
+	mdelay(10);
+	vs6624_writeregs(sd, vs6624_p2);
+
+	vs6624_writeregs(sd, vs6624_default);
+	vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF);
+	vs6624_writeregs(sd, vs6624_run_setup);
+
+	/* set frame rate */
+	sensor->frame_rate.numerator = MAX_FRAME_RATE;
+	sensor->frame_rate.denominator = 1;
+	vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
+	vs6624_write(sd, VS6624_FR_NUM_MSB,
+			sensor->frame_rate.numerator >> 8);
+	vs6624_write(sd, VS6624_FR_NUM_LSB,
+			sensor->frame_rate.numerator & 0xFF);
+	vs6624_write(sd, VS6624_FR_DEN,
+			sensor->frame_rate.denominator & 0xFF);
+
+	sensor->fmt = vs6624_default_fmt;
+	sensor->ce_pin = *ce;
+
+	v4l_info(client, "chip found @ 0x%02x (%s)\n",
+			client->addr << 1, client->adapter->name);
+
+	hdl = &sensor->hdl;
+	v4l2_ctrl_handler_init(hdl, 4);
+	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+			V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87);
+	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+			V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78);
+	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+			V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+			V4L2_CID_VFLIP, 0, 1, 1, 0);
+	/* hook the control handler into the driver */
+	sd->ctrl_handler = hdl;
+	if (hdl->error) {
+		int err = hdl->error;
+
+		v4l2_ctrl_handler_free(hdl);
+		kfree(sensor);
+		gpio_free(*ce);
+		return err;
+	}
+
+	/* initialize the hardware to the default control values */
+	v4l2_ctrl_handler_setup(hdl);
+	return 0;
+}
+
+static int __devexit vs6624_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct vs6624 *sensor = to_vs6624(sd);
+
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	gpio_free(sensor->ce_pin);
+	kfree(sensor);
+	return 0;
+}
+
+static const struct i2c_device_id vs6624_id[] = {
+	{"vs6624", 0},
+	{},
+};
+
+MODULE_DEVICE_TABLE(i2c, vs6624_id);
+
+static struct i2c_driver vs6624_driver = {
+	.driver = {
+		.owner  = THIS_MODULE,
+		.name   = "vs6624",
+	},
+	.probe          = vs6624_probe,
+	.remove         = __devexit_p(vs6624_remove),
+	.id_table       = vs6624_id,
+};
+
+static __init int vs6624_init(void)
+{
+	return i2c_add_driver(&vs6624_driver);
+}
+
+static __exit void vs6624_exit(void)
+{
+	i2c_del_driver(&vs6624_driver);
+}
+
+module_init(vs6624_init);
+module_exit(vs6624_exit);
+
+MODULE_DESCRIPTION("VS6624 sensor driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h
new file mode 100644
index 0000000..6ba2ee2
--- /dev/null
+++ b/drivers/media/video/vs6624_regs.h
@@ -0,0 +1,337 @@
+/*
+ * vs6624 - ST VS6624 CMOS image sensor registers
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _VS6624_REGS_H_
+#define _VS6624_REGS_H_
+
+/* low level control registers */
+#define VS6624_MICRO_EN               0xC003 /* power enable for all MCU clock */
+#define VS6624_DIO_EN                 0xC044 /* enable digital I/O */
+/* device parameters */
+#define VS6624_DEV_ID_MSB             0x0001 /* device id MSB */
+#define VS6624_DEV_ID_LSB             0x0002 /* device id LSB */
+#define VS6624_FW_VSN_MAJOR           0x0004 /* firmware version major */
+#define VS6624_FW_VSN_MINOR           0x0006 /* firmware version minor */
+#define VS6624_PATCH_VSN_MAJOR        0x0008 /* patch version major */
+#define VS6624_PATCH_VSN_MINOR        0x000A /* patch version minor */
+/* host interface manager control */
+#define VS6624_USER_CMD               0x0180 /* user level control of operating states */
+/* host interface manager status */
+#define VS6624_STATE                  0x0202 /* current state of the mode manager */
+/* run mode control */
+#define VS6624_METER_ON               0x0280 /* if false AE and AWB are disabled */
+/* mode setup */
+#define VS6624_ACTIVE_PIPE_SETUP      0x0302 /* select the active bank for non view live mode */
+#define VS6624_SENSOR_MODE            0x0308 /* select the different sensor mode */
+/* pipe setup bank0 */
+#define VS6624_IMAGE_SIZE0            0x0380 /* required output dimension */
+#define VS6624_MAN_HSIZE0_MSB         0x0383 /* input required manual H size MSB */
+#define VS6624_MAN_HSIZE0_LSB         0x0384 /* input required manual H size LSB */
+#define VS6624_MAN_VSIZE0_MSB         0x0387 /* input required manual V size MSB */
+#define VS6624_MAN_VSIZE0_LSB         0x0388 /* input required manual V size LSB */
+#define VS6624_ZOOM_HSTEP0_MSB        0x038B /* set the zoom H step MSB */
+#define VS6624_ZOOM_HSTEP0_LSB        0x038C /* set the zoom H step LSB */
+#define VS6624_ZOOM_VSTEP0_MSB        0x038F /* set the zoom V step MSB */
+#define VS6624_ZOOM_VSTEP0_LSB        0x0390 /* set the zoom V step LSB */
+#define VS6624_ZOOM_CTRL0             0x0392 /* control zoon in, out and stop */
+#define VS6624_PAN_HSTEP0_MSB         0x0395 /* set the pan H step MSB */
+#define VS6624_PAN_HSTEP0_LSB         0x0396 /* set the pan H step LSB */
+#define VS6624_PAN_VSTEP0_MSB         0x0399 /* set the pan V step MSB */
+#define VS6624_PAN_VSTEP0_LSB         0x039A /* set the pan V step LSB */
+#define VS6624_PAN_CTRL0              0x039C /* control pan operation */
+#define VS6624_CROP_CTRL0             0x039E /* select cropping mode */
+#define VS6624_CROP_HSTART0_MSB       0x03A1 /* set the cropping H start address MSB */
+#define VS6624_CROP_HSTART0_LSB       0x03A2 /* set the cropping H start address LSB */
+#define VS6624_CROP_HSIZE0_MSB        0x03A5 /* set the cropping H size MSB */
+#define VS6624_CROP_HSIZE0_LSB        0x03A6 /* set the cropping H size LSB */
+#define VS6624_CROP_VSTART0_MSB       0x03A9 /* set the cropping V start address MSB */
+#define VS6624_CROP_VSTART0_LSB       0x03AA /* set the cropping V start address LSB */
+#define VS6624_CROP_VSIZE0_MSB        0x03AD /* set the cropping V size MSB */
+#define VS6624_CROP_VSIZE0_LSB        0x03AE /* set the cropping V size LSB */
+#define VS6624_IMG_FMT0               0x03B0 /* select required output image format */
+#define VS6624_BAYER_OUT_ALIGN0       0x03B2 /* set bayer output alignment */
+#define VS6624_CONTRAST0              0x03B4 /* contrast control for output */
+#define VS6624_SATURATION0            0x03B6 /* saturation control for output */
+#define VS6624_GAMMA0                 0x03B8 /* gamma settings */
+#define VS6624_HMIRROR0               0x03BA /* horizontal image orientation flip */
+#define VS6624_VFLIP0                 0x03BC /* vertical image orientation flip */
+#define VS6624_CHANNEL_ID0            0x03BE /* logical DMA channel number */
+/* pipe setup bank1 */
+#define VS6624_IMAGE_SIZE1            0x0400 /* required output dimension */
+#define VS6624_MAN_HSIZE1_MSB         0x0403 /* input required manual H size MSB */
+#define VS6624_MAN_HSIZE1_LSB         0x0404 /* input required manual H size LSB */
+#define VS6624_MAN_VSIZE1_MSB         0x0407 /* input required manual V size MSB */
+#define VS6624_MAN_VSIZE1_LSB         0x0408 /* input required manual V size LSB */
+#define VS6624_ZOOM_HSTEP1_MSB        0x040B /* set the zoom H step MSB */
+#define VS6624_ZOOM_HSTEP1_LSB        0x040C /* set the zoom H step LSB */
+#define VS6624_ZOOM_VSTEP1_MSB        0x040F /* set the zoom V step MSB */
+#define VS6624_ZOOM_VSTEP1_LSB        0x0410 /* set the zoom V step LSB */
+#define VS6624_ZOOM_CTRL1             0x0412 /* control zoon in, out and stop */
+#define VS6624_PAN_HSTEP1_MSB         0x0415 /* set the pan H step MSB */
+#define VS6624_PAN_HSTEP1_LSB         0x0416 /* set the pan H step LSB */
+#define VS6624_PAN_VSTEP1_MSB         0x0419 /* set the pan V step MSB */
+#define VS6624_PAN_VSTEP1_LSB         0x041A /* set the pan V step LSB */
+#define VS6624_PAN_CTRL1              0x041C /* control pan operation */
+#define VS6624_CROP_CTRL1             0x041E /* select cropping mode */
+#define VS6624_CROP_HSTART1_MSB       0x0421 /* set the cropping H start address MSB */
+#define VS6624_CROP_HSTART1_LSB       0x0422 /* set the cropping H start address LSB */
+#define VS6624_CROP_HSIZE1_MSB        0x0425 /* set the cropping H size MSB */
+#define VS6624_CROP_HSIZE1_LSB        0x0426 /* set the cropping H size LSB */
+#define VS6624_CROP_VSTART1_MSB       0x0429 /* set the cropping V start address MSB */
+#define VS6624_CROP_VSTART1_LSB       0x042A /* set the cropping V start address LSB */
+#define VS6624_CROP_VSIZE1_MSB        0x042D /* set the cropping V size MSB */
+#define VS6624_CROP_VSIZE1_LSB        0x042E /* set the cropping V size LSB */
+#define VS6624_IMG_FMT1               0x0430 /* select required output image format */
+#define VS6624_BAYER_OUT_ALIGN1       0x0432 /* set bayer output alignment */
+#define VS6624_CONTRAST1              0x0434 /* contrast control for output */
+#define VS6624_SATURATION1            0x0436 /* saturation control for output */
+#define VS6624_GAMMA1                 0x0438 /* gamma settings */
+#define VS6624_HMIRROR1               0x043A /* horizontal image orientation flip */
+#define VS6624_VFLIP1                 0x043C /* vertical image orientation flip */
+#define VS6624_CHANNEL_ID1            0x043E /* logical DMA channel number */
+/* view live control */
+#define VS6624_VIEW_LIVE_EN           0x0480 /* enable view live mode */
+#define VS6624_INIT_PIPE_SETUP        0x0482 /* select initial pipe setup bank */
+/* view live status */
+#define VS6624_CUR_PIPE_SETUP         0x0500 /* indicates most recently applied setup bank */
+/* power management */
+#define VS6624_TIME_TO_POWER_DOWN     0x0580 /* automatically transition time to stop mode */
+/* video timing parameter host inputs */
+#define VS6624_EXT_CLK_FREQ_NUM_MSB   0x0605 /* external clock frequency numerator MSB */
+#define VS6624_EXT_CLK_FREQ_NUM_LSB   0x0606 /* external clock frequency numerator LSB */
+#define VS6624_EXT_CLK_FREQ_DEN       0x0608 /* external clock frequency denominator */
+/* video timing control */
+#define VS6624_SYS_CLK_MODE           0x0880 /* decides system clock frequency */
+/* frame dimension parameter host inputs */
+#define VS6624_LIGHT_FREQ             0x0C80 /* AC frequency used for flicker free time */
+#define VS6624_FLICKER_COMPAT         0x0C82 /* flicker compatible frame length */
+/* static frame rate control */
+#define VS6624_FR_NUM_MSB             0x0D81 /* desired frame rate numerator MSB */
+#define VS6624_FR_NUM_LSB             0x0D82 /* desired frame rate numerator LSB */
+#define VS6624_FR_DEN                 0x0D84 /* desired frame rate denominator */
+/* automatic frame rate control */
+#define VS6624_DISABLE_FR_DAMPER      0x0E80 /* defines frame rate mode */
+#define VS6624_MIN_DAMPER_OUT_MSB     0x0E8C /* minimum frame rate MSB */
+#define VS6624_MIN_DAMPER_OUT_LSB     0x0E8A /* minimum frame rate LSB */
+/* exposure controls */
+#define VS6624_EXPO_MODE              0x1180 /* exposure mode */
+#define VS6624_EXPO_METER             0x1182 /* weights to be associated with the zones */
+#define VS6624_EXPO_TIME_NUM          0x1184 /* exposure time numerator */
+#define VS6624_EXPO_TIME_DEN          0x1186 /* exposure time denominator */
+#define VS6624_EXPO_TIME_MSB          0x1189 /* exposure time for the Manual Mode MSB */
+#define VS6624_EXPO_TIME_LSB          0x118A /* exposure time for the Manual Mode LSB */
+#define VS6624_EXPO_COMPENSATION      0x1190 /* exposure compensation */
+#define VS6624_DIRECT_COARSE_MSB      0x1195 /* coarse integration lines for Direct Mode MSB */
+#define VS6624_DIRECT_COARSE_LSB      0x1196 /* coarse integration lines for Direct Mode LSB */
+#define VS6624_DIRECT_FINE_MSB        0x1199 /* fine integration pixels for Direct Mode MSB */
+#define VS6624_DIRECT_FINE_LSB        0x119A /* fine integration pixels for Direct Mode LSB */
+#define VS6624_DIRECT_ANAL_GAIN_MSB   0x119D /* analog gain for Direct Mode MSB */
+#define VS6624_DIRECT_ANAL_GAIN_LSB   0x119E /* analog gain for Direct Mode LSB */
+#define VS6624_DIRECT_DIGI_GAIN_MSB   0x11A1 /* digital gain for Direct Mode MSB */
+#define VS6624_DIRECT_DIGI_GAIN_LSB   0x11A2 /* digital gain for Direct Mode LSB */
+#define VS6624_FLASH_COARSE_MSB       0x11A5 /* coarse integration lines for Flash Gun Mode MSB */
+#define VS6624_FLASH_COARSE_LSB       0x11A6 /* coarse integration lines for Flash Gun Mode LSB */
+#define VS6624_FLASH_FINE_MSB         0x11A9 /* fine integration pixels for Flash Gun Mode MSB */
+#define VS6624_FLASH_FINE_LSB         0x11AA /* fine integration pixels for Flash Gun Mode LSB */
+#define VS6624_FLASH_ANAL_GAIN_MSB    0x11AD /* analog gain for Flash Gun Mode MSB */
+#define VS6624_FLASH_ANAL_GAIN_LSB    0x11AE /* analog gain for Flash Gun Mode LSB */
+#define VS6624_FLASH_DIGI_GAIN_MSB    0x11B1 /* digital gain for Flash Gun Mode MSB */
+#define VS6624_FLASH_DIGI_GAIN_LSB    0x11B2 /* digital gain for Flash Gun Mode LSB */
+#define VS6624_FREEZE_AE              0x11B4 /* freeze auto exposure */
+#define VS6624_MAX_INT_TIME_MSB       0x11B7 /* user maximum integration time MSB */
+#define VS6624_MAX_INT_TIME_LSB       0x11B8 /* user maximum integration time LSB */
+#define VS6624_FLASH_AG_THR_MSB       0x11BB /* recommend flash gun analog gain threshold MSB */
+#define VS6624_FLASH_AG_THR_LSB       0x11BC /* recommend flash gun analog gain threshold LSB */
+#define VS6624_ANTI_FLICKER_MODE      0x11C0 /* anti flicker mode */
+/* white balance control */
+#define VS6624_WB_MODE                0x1480 /* set white balance mode */
+#define VS6624_MAN_RG                 0x1482 /* user setting for red channel gain */
+#define VS6624_MAN_GG                 0x1484 /* user setting for green channel gain */
+#define VS6624_MAN_BG                 0x1486 /* user setting for blue channel gain */
+#define VS6624_FLASH_RG_MSB           0x148B /* red gain for Flash Gun MSB */
+#define VS6624_FLASH_RG_LSB           0x148C /* red gain for Flash Gun LSB */
+#define VS6624_FLASH_GG_MSB           0x148F /* green gain for Flash Gun MSB */
+#define VS6624_FLASH_GG_LSB           0x1490 /* green gain for Flash Gun LSB */
+#define VS6624_FLASH_BG_MSB           0x1493 /* blue gain for Flash Gun MSB */
+#define VS6624_FLASH_BG_LSB           0x1494 /* blue gain for Flash Gun LSB */
+/* sensor setup */
+#define VS6624_BC_OFFSET              0x1990 /* Black Correction Offset */
+/* image stability */
+#define VS6624_STABLE_WB              0x1900 /* white balance stable */
+#define VS6624_STABLE_EXPO            0x1902 /* exposure stable */
+#define VS6624_STABLE                 0x1906 /* system stable */
+/* flash control */
+#define VS6624_FLASH_MODE             0x1A80 /* flash mode */
+#define VS6624_FLASH_OFF_LINE_MSB     0x1A83 /* off line at flash pulse mode MSB */
+#define VS6624_FLASH_OFF_LINE_LSB     0x1A84 /* off line at flash pulse mode LSB */
+/* flash status */
+#define VS6624_FLASH_RECOM            0x1B00 /* flash gun is recommended */
+#define VS6624_FLASH_GRAB_COMPLETE    0x1B02 /* flash gun image has been grabbed */
+/* scythe filter controls */
+#define VS6624_SCYTHE_FILTER          0x1D80 /* disable scythe defect correction */
+/* jack filter controls */
+#define VS6624_JACK_FILTER            0x1E00 /* disable jack defect correction */
+/* demosaic control */
+#define VS6624_ANTI_ALIAS_FILTER      0x1E80 /* anti alias filter suppress */
+/* color matrix dampers */
+#define VS6624_CM_DISABLE             0x1F00 /* disable color matrix damper */
+#define VS6624_CM_LOW_THR_MSB         0x1F03 /* low threshold for exposure MSB */
+#define VS6624_CM_LOW_THR_LSB         0x1F04 /* low threshold for exposure LSB */
+#define VS6624_CM_HIGH_THR_MSB        0x1F07 /* high threshold for exposure MSB */
+#define VS6624_CM_HIGH_THR_LSB        0x1F08 /* high threshold for exposure LSB */
+#define VS6624_CM_MIN_OUT_MSB         0x1F0B /* minimum possible damper output MSB */
+#define VS6624_CM_MIN_OUT_LSB         0x1F0C /* minimum possible damper output LSB */
+/* peaking control */
+#define VS6624_PEAK_GAIN              0x2000 /* controls peaking gain */
+#define VS6624_PEAK_G_DISABLE         0x2002 /* disable peak gain damping */
+#define VS6624_PEAK_LOW_THR_G_MSB     0x2005 /* low threshold for exposure for gain MSB */
+#define VS6624_PEAK_LOW_THR_G_LSB     0x2006 /* low threshold for exposure for gain LSB */
+#define VS6624_PEAK_HIGH_THR_G_MSB    0x2009 /* high threshold for exposure for gain MSB */
+#define VS6624_PEAK_HIGH_THR_G_LSB    0x200A /* high threshold for exposure for gain LSB */
+#define VS6624_PEAK_MIN_OUT_G_MSB     0x200D /* minimum damper output for gain MSB */
+#define VS6624_PEAK_MIN_OUT_G_LSB     0x200E /* minimum damper output for gain LSB */
+#define VS6624_PEAK_LOW_THR           0x2010 /* adjust degree of coring */
+#define VS6624_PEAK_C_DISABLE         0x2012 /* disable coring damping */
+#define VS6624_PEAK_HIGH_THR          0x2014 /* adjust maximum gain */
+#define VS6624_PEAK_LOW_THR_C_MSB     0x2017 /* low threshold for exposure for coring MSB */
+#define VS6624_PEAK_LOW_THR_C_LSB     0x2018 /* low threshold for exposure for coring LSB */
+#define VS6624_PEAK_HIGH_THR_C_MSB    0x201B /* high threshold for exposure for coring MSB */
+#define VS6624_PEAK_HIGH_THR_C_LSB    0x201C /* high threshold for exposure for coring LSB */
+#define VS6624_PEAK_MIN_OUT_C_MSB     0x201F /* minimum damper output for coring MSB */
+#define VS6624_PEAK_MIN_OUT_C_LSB     0x2020 /* minimum damper output for coring LSB */
+/* pipe 0 RGB to YUV matrix manual control */
+#define VS6624_RYM0_MAN_CTRL          0x2180 /* enable manual RGB to YUV matrix */
+#define VS6624_RYM0_W00_MSB           0x2183 /* row 0 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W00_LSB           0x2184 /* row 0 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W01_MSB           0x2187 /* row 0 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W01_LSB           0x2188 /* row 0 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W02_MSB           0x218C /* row 0 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W02_LSB           0x218D /* row 0 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_W10_MSB           0x2190 /* row 1 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W10_LSB           0x218F /* row 1 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W11_MSB           0x2193 /* row 1 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W11_LSB           0x2194 /* row 1 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W12_MSB           0x2197 /* row 1 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W12_LSB           0x2198 /* row 1 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_W20_MSB           0x219B /* row 2 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W20_LSB           0x219C /* row 2 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W21_MSB           0x21A0 /* row 2 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W21_LSB           0x219F /* row 2 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W22_MSB           0x21A3 /* row 2 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W22_LSB           0x21A4 /* row 2 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_YINY_MSB          0x21A7 /* Y in Y MSB */
+#define VS6624_RYM0_YINY_LSB          0x21A8 /* Y in Y LSB */
+#define VS6624_RYM0_YINCB_MSB         0x21AB /* Y in Cb MSB */
+#define VS6624_RYM0_YINCB_LSB         0x21AC /* Y in Cb LSB */
+#define VS6624_RYM0_YINCR_MSB         0x21B0 /* Y in Cr MSB */
+#define VS6624_RYM0_YINCR_LSB         0x21AF /* Y in Cr LSB */
+/* pipe 1 RGB to YUV matrix manual control */
+#define VS6624_RYM1_MAN_CTRL          0x2200 /* enable manual RGB to YUV matrix */
+#define VS6624_RYM1_W00_MSB           0x2203 /* row 0 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W00_LSB           0x2204 /* row 0 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W01_MSB           0x2207 /* row 0 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W01_LSB           0x2208 /* row 0 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W02_MSB           0x220C /* row 0 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W02_LSB           0x220D /* row 0 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_W10_MSB           0x2210 /* row 1 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W10_LSB           0x220F /* row 1 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W11_MSB           0x2213 /* row 1 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W11_LSB           0x2214 /* row 1 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W12_MSB           0x2217 /* row 1 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W12_LSB           0x2218 /* row 1 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_W20_MSB           0x221B /* row 2 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W20_LSB           0x221C /* row 2 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W21_MSB           0x2220 /* row 2 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W21_LSB           0x221F /* row 2 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W22_MSB           0x2223 /* row 2 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W22_LSB           0x2224 /* row 2 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_YINY_MSB          0x2227 /* Y in Y MSB */
+#define VS6624_RYM1_YINY_LSB          0x2228 /* Y in Y LSB */
+#define VS6624_RYM1_YINCB_MSB         0x222B /* Y in Cb MSB */
+#define VS6624_RYM1_YINCB_LSB         0x222C /* Y in Cb LSB */
+#define VS6624_RYM1_YINCR_MSB         0x2220 /* Y in Cr MSB */
+#define VS6624_RYM1_YINCR_LSB         0x222F /* Y in Cr LSB */
+/* pipe 0 gamma manual control */
+#define VS6624_GAMMA_MAN_CTRL0        0x2280 /* enable manual gamma setup */
+#define VS6624_GAMMA_PEAK_R0          0x2282 /* peaked red channel gamma value */
+#define VS6624_GAMMA_PEAK_G0          0x2284 /* peaked green channel gamma value */
+#define VS6624_GAMMA_PEAK_B0          0x2286 /* peaked blue channel gamma value */
+#define VS6624_GAMMA_UNPEAK_R0        0x2288 /* unpeaked red channel gamma value */
+#define VS6624_GAMMA_UNPEAK_G0        0x228A /* unpeaked green channel gamma value */
+#define VS6624_GAMMA_UNPEAK_B0        0x228C /* unpeaked blue channel gamma value */
+/* pipe 1 gamma manual control */
+#define VS6624_GAMMA_MAN_CTRL1        0x2300 /* enable manual gamma setup */
+#define VS6624_GAMMA_PEAK_R1          0x2302 /* peaked red channel gamma value */
+#define VS6624_GAMMA_PEAK_G1          0x2304 /* peaked green channel gamma value */
+#define VS6624_GAMMA_PEAK_B1          0x2306 /* peaked blue channel gamma value */
+#define VS6624_GAMMA_UNPEAK_R1        0x2308 /* unpeaked red channel gamma value */
+#define VS6624_GAMMA_UNPEAK_G1        0x230A /* unpeaked green channel gamma value */
+#define VS6624_GAMMA_UNPEAK_B1        0x230C /* unpeaked blue channel gamma value */
+/* fade to black */
+#define VS6624_F2B_DISABLE            0x2480 /* disable fade to black */
+#define VS6624_F2B_BLACK_VAL_MSB      0x2483 /* black value MSB */
+#define VS6624_F2B_BLACK_VAL_LSB      0x2484 /* black value LSB */
+#define VS6624_F2B_LOW_THR_MSB        0x2487 /* low threshold for exposure MSB */
+#define VS6624_F2B_LOW_THR_LSB        0x2488 /* low threshold for exposure LSB */
+#define VS6624_F2B_HIGH_THR_MSB       0x248B /* high threshold for exposure MSB */
+#define VS6624_F2B_HIGH_THR_LSB       0x248C /* high threshold for exposure LSB */
+#define VS6624_F2B_MIN_OUT_MSB        0x248F /* minimum damper output MSB */
+#define VS6624_F2B_MIN_OUT_LSB        0x2490 /* minimum damper output LSB */
+/* output formatter control */
+#define VS6624_CODE_CK_EN             0x2580 /* code check enable */
+#define VS6624_BLANK_FMT              0x2582 /* blank format */
+#define VS6624_SYNC_CODE_SETUP        0x2584 /* sync code setup */
+#define VS6624_HSYNC_SETUP            0x2586 /* H sync setup */
+#define VS6624_VSYNC_SETUP            0x2588 /* V sync setup */
+#define VS6624_PCLK_SETUP             0x258A /* PCLK setup */
+#define VS6624_PCLK_EN                0x258C /* PCLK enable */
+#define VS6624_OPF_SP_SETUP           0x258E /* output formatter sp setup */
+#define VS6624_BLANK_DATA_MSB         0x2590 /* blank data MSB */
+#define VS6624_BLANK_DATA_LSB         0x2592 /* blank data LSB */
+#define VS6624_RGB_SETUP              0x2594 /* RGB setup */
+#define VS6624_YUV_SETUP              0x2596 /* YUV setup */
+#define VS6624_VSYNC_RIS_COARSE_H     0x2598 /* V sync rising coarse high */
+#define VS6624_VSYNC_RIS_COARSE_L     0x259A /* V sync rising coarse low */
+#define VS6624_VSYNC_RIS_FINE_H       0x259C /* V sync rising fine high */
+#define VS6624_VSYNC_RIS_FINE_L       0x259E /* V sync rising fine low */
+#define VS6624_VSYNC_FALL_COARSE_H    0x25A0 /* V sync falling coarse high */
+#define VS6624_VSYNC_FALL_COARSE_L    0x25A2 /* V sync falling coarse low */
+#define VS6624_VSYNC_FALL_FINE_H      0x25A4 /* V sync falling fine high */
+#define VS6624_VSYNC_FALL_FINE_L      0x25A6 /* V sync falling fine low */
+#define VS6624_HSYNC_RIS_H            0x25A8 /* H sync rising high */
+#define VS6624_HSYNC_RIS_L            0x25AA /* H sync rising low */
+#define VS6624_HSYNC_FALL_H           0x25AC /* H sync falling high */
+#define VS6624_HSYNC_FALL_L           0x25AE /* H sync falling low */
+#define VS6624_OUT_IF                 0x25B0 /* output interface */
+#define VS6624_CCP_EXT_DATA           0x25B2 /* CCP extra data */
+/* NoRA controls */
+#define VS6624_NORA_DISABLE           0x2600 /* NoRA control mode */
+#define VS6624_NORA_USAGE             0x2602 /* usage */
+#define VS6624_NORA_SPLIT_KN          0x2604 /* split kn */
+#define VS6624_NORA_SPLIT_NI          0x2606 /* split ni */
+#define VS6624_NORA_TIGHT_G           0x2608 /* tight green */
+#define VS6624_NORA_DISABLE_NP        0x260A /* disable noro promoting */
+#define VS6624_NORA_LOW_THR_MSB       0x260D /* low threshold for exposure MSB */
+#define VS6624_NORA_LOW_THR_LSB       0x260E /* low threshold for exposure LSB */
+#define VS6624_NORA_HIGH_THR_MSB      0x2611 /* high threshold for exposure MSB */
+#define VS6624_NORA_HIGH_THR_LSB      0x2612 /* high threshold for exposure LSB */
+#define VS6624_NORA_MIN_OUT_MSB       0x2615 /* minimum damper output MSB */
+#define VS6624_NORA_MIN_OUT_LSB       0x2616 /* minimum damper output LSB */
+
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 20fd16d..0133b3b 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -143,6 +143,9 @@ enum {
 	/* module saa6588: just ident 6588 */
 	V4L2_IDENT_SAA6588 = 6588,
 
+	/* module vs6624: just ident 6624 */
+	V4L2_IDENT_VS6624 = 6624,
+
 	/* module saa6752hs: reserved range 6750-6759 */
 	V4L2_IDENT_SAA6752HS = 6752,
 	V4L2_IDENT_SAA6752HS_AC3 = 6753,
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver
  2011-09-19 20:59 [PATCH 1/4 v2][FOR 3.1] v4l2: add vb2_get_unmapped_area in vb2 core Scott Jiang
  2011-09-19 20:59 ` [PATCH 2/4 v2][FOR 3.1] v4l2: add adv7183 decoder driver Scott Jiang
  2011-09-19 20:59 ` [PATCH 3/4 v2][FOR 3.1] v4l2: add vs6624 sensor driver Scott Jiang
@ 2011-09-19 20:59 ` Scott Jiang
  2011-09-26 14:09   ` Hans Verkuil
  2011-09-30 15:07   ` Guennadi Liakhovetski
  2 siblings, 2 replies; 12+ messages in thread
From: Scott Jiang @ 2011-09-19 20:59 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Laurent Pinchart, Hans Verkuil,
	Guennadi Liakhovetski
  Cc: linux-media, uclinux-dist-devel, Scott Jiang

this is a v4l2 bridge driver for Blackfin video capture device,
support ppi interface

Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>
---
 drivers/media/video/Kconfig                 |    2 +
 drivers/media/video/Makefile                |    2 +
 drivers/media/video/blackfin/Kconfig        |   10 +
 drivers/media/video/blackfin/Makefile       |    2 +
 drivers/media/video/blackfin/bfin_capture.c | 1114 +++++++++++++++++++++++++++
 drivers/media/video/blackfin/ppi.c          |  201 +++++
 include/media/blackfin/bfin_capture.h       |   33 +
 include/media/blackfin/ppi.h                |   63 ++
 8 files changed, 1427 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/blackfin/Kconfig
 create mode 100644 drivers/media/video/blackfin/Makefile
 create mode 100644 drivers/media/video/blackfin/bfin_capture.c
 create mode 100644 drivers/media/video/blackfin/ppi.c
 create mode 100644 include/media/blackfin/bfin_capture.h
 create mode 100644 include/media/blackfin/ppi.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 1c03abf..52cd491 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -602,6 +602,8 @@ source "drivers/media/video/davinci/Kconfig"
 
 source "drivers/media/video/omap/Kconfig"
 
+source "drivers/media/video/blackfin/Kconfig"
+
 source "drivers/media/video/bt8xx/Kconfig"
 
 config VIDEO_PMS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 03b55ed..227a700 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -177,6 +177,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
 obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
 
+obj-$(CONFIG_BLACKFIN)                  += blackfin/
+
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
 
 obj-$(CONFIG_VIDEO_SH_VOU)		+= sh_vou.o
diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig
new file mode 100644
index 0000000..ecd5323
--- /dev/null
+++ b/drivers/media/video/blackfin/Kconfig
@@ -0,0 +1,10 @@
+config VIDEO_BLACKFIN_CAPTURE
+	tristate "Blackfin Video Capture Driver"
+	depends on VIDEO_V4L2 && BLACKFIN && I2C
+	select VIDEOBUF2_DMA_CONTIG
+	help
+	  V4L2 bridge driver for Blackfin video capture device.
+	  Choose PPI or EPPI as its interface.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bfin_video_capture.
diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile
new file mode 100644
index 0000000..aa3a0a2
--- /dev/null
+++ b/drivers/media/video/blackfin/Makefile
@@ -0,0 +1,2 @@
+bfin_video_capture-objs := bfin_capture.o ppi.o
+obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
new file mode 100644
index 0000000..30ed8bb
--- /dev/null
+++ b/drivers/media/video/blackfin/bfin_capture.c
@@ -0,0 +1,1114 @@
+/*
+ * Analog Devices video capture driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <asm/dma.h>
+
+#include <media/blackfin/bfin_capture.h>
+
+#define CAPTURE_DRV_NAME        "bfin_capture"
+#define BCAP_MIN_NUM_BUF        2
+
+struct bcap_format {
+	u8 *desc;
+	u32 pixelformat;
+	enum v4l2_mbus_pixelcode mbus_code;
+	int bpp; /* bits per pixel */
+};
+
+struct bcap_buffer {
+	struct vb2_buffer vb;
+	struct list_head list;
+};
+
+struct bcap_device {
+	/* capture device instance */
+	struct v4l2_device v4l2_dev;
+	/* device node data */
+	struct video_device *video_dev;
+	/* sub device instance */
+	struct v4l2_subdev *sd;
+	/* capture config */
+	struct bfin_capture_config *cfg;
+	/* ppi interface */
+	struct ppi_if *ppi;
+	/* current input */
+	unsigned int cur_input;
+	/* current selected standard */
+	v4l2_std_id std;
+	/* used to store pixel format */
+	struct v4l2_pix_format fmt;
+	/* bits per pixel*/
+	int bpp;
+	/* pointing to current video buffer */
+	struct bcap_buffer *cur_frm;
+	/* pointing to next video buffer */
+	struct bcap_buffer *next_frm;
+	/* buffer queue used in videobuf2 */
+	struct vb2_queue buffer_queue;
+	/* allocator-specific contexts for each plane */
+	struct vb2_alloc_ctx *alloc_ctx;
+	/* queue of filled frames */
+	struct list_head dma_queue;
+	/* used in videobuf2 callback */
+	spinlock_t lock;
+	/* used to access capture device */
+	struct mutex mutex;
+	/* used to wait ppi to complete one transfer */
+	struct completion comp;
+	/* number of users performing IO */
+	unsigned int io_usrs;
+	/* number of open instances of the device */
+	unsigned int usrs;
+	/* indicate whether streaming has started */
+	bool started;
+};
+
+struct bcap_fh {
+	struct v4l2_fh fh;
+	struct bcap_device *bcap_dev;
+	/* indicates whether this file handle is doing IO */
+	bool io_allowed;
+};
+
+static const struct bcap_format bcap_formats[] = {
+	{
+		.desc        = "YCbCr 4:2:2 Interleaved UYVY",
+		.pixelformat = V4L2_PIX_FMT_UYVY,
+		.mbus_code   = V4L2_MBUS_FMT_UYVY8_2X8,
+		.bpp         = 16,
+	},
+	{
+		.desc        = "YCbCr 4:2:2 Interleaved YUYV",
+		.pixelformat = V4L2_PIX_FMT_YUYV,
+		.mbus_code   = V4L2_MBUS_FMT_YUYV8_2X8,
+		.bpp         = 16,
+	},
+	{
+		.desc        = "RGB 565",
+		.pixelformat = V4L2_PIX_FMT_RGB565,
+		.mbus_code   = V4L2_MBUS_FMT_RGB565_2X8_LE,
+		.bpp         = 16,
+	},
+	{
+		.desc        = "RGB 444",
+		.pixelformat = V4L2_PIX_FMT_RGB444,
+		.mbus_code   = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+		.bpp         = 16,
+	},
+
+};
+#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
+
+static irqreturn_t bcap_isr(int irq, void *dev_id);
+
+static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
+{
+	return container_of(vb, struct bcap_buffer, vb);
+}
+
+static int bcap_open(struct file *file)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct video_device *vfd = bcap_dev->video_dev;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	struct v4l2_pix_format *pixfmt = &bcap_dev->fmt;
+	const struct bcap_format *bcap_fmt;
+	struct bcap_fh *bcap_fh;
+	int ret, i;
+
+	if (!bcap_dev->sd) {
+		v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
+		return -ENODEV;
+	}
+
+	/* if first open, query format of subdevice as default format */
+	if (!bcap_dev->usrs) {
+		ret = v4l2_subdev_call(bcap_dev->sd, video,
+					g_mbus_fmt, &mbus_fmt);
+		if (ret < 0)
+			return ret;
+
+		for (i = 0; i < BCAP_MAX_FMTS; i++) {
+			if (mbus_fmt.code != bcap_formats[i].mbus_code)
+				continue;
+			bcap_fmt = &bcap_formats[i];
+			v4l2_fill_pix_format(pixfmt, &mbus_fmt);
+			pixfmt->pixelformat = bcap_fmt->pixelformat;
+			pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
+			pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+			break;
+		}
+		if (i == BCAP_MAX_FMTS) {
+			v4l2_err(&bcap_dev->v4l2_dev,
+					"subdev fmt is not supported by bcap\n");
+			return -EINVAL;
+		}
+	}
+
+	bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
+	if (!bcap_fh) {
+		v4l2_err(&bcap_dev->v4l2_dev,
+			 "unable to allocate memory for file handle object\n");
+		return -ENOMEM;
+	}
+
+	v4l2_fh_init(&bcap_fh->fh, vfd);
+
+	/* store pointer to v4l2_fh in private_data member of file */
+	file->private_data = &bcap_fh->fh;
+	v4l2_fh_add(&bcap_fh->fh);
+	bcap_fh->bcap_dev = bcap_dev;
+	bcap_dev->usrs++;
+	bcap_fh->io_allowed = false;
+	return 0;
+}
+
+static int bcap_release(struct file *file)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_fh *fh = file->private_data;
+	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+	/* if this instance is doing IO */
+	if (bcap_fh->io_allowed) {
+		vb2_queue_release(&bcap_dev->buffer_queue);
+		bcap_dev->io_usrs = 0;
+	}
+
+	bcap_dev->usrs--;
+	file->private_data = NULL;
+	v4l2_fh_del(&bcap_fh->fh);
+	v4l2_fh_exit(&bcap_fh->fh);
+	kfree(bcap_fh);
+	return 0;
+}
+
+static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return vb2_mmap(&bcap_dev->buffer_queue, vma);
+}
+
+#ifndef CONFIG_MMU
+static unsigned long bcap_get_unmapped_area(struct file *file,
+					    unsigned long addr,
+					    unsigned long len,
+					    unsigned long pgoff,
+					    unsigned long flags)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
+				     addr,
+				     len,
+				     pgoff,
+				     flags);
+}
+#endif
+
+static unsigned int bcap_poll(struct file *file, poll_table *wait)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	if (bcap_dev->started)
+		return vb2_poll(&bcap_dev->buffer_queue, file, wait);
+	return 0;
+}
+
+static int bcap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+				unsigned int *nplanes, unsigned long sizes[],
+				void *alloc_ctxs[])
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+
+	if (*nbuffers < BCAP_MIN_NUM_BUF)
+		*nbuffers = BCAP_MIN_NUM_BUF;
+
+	*nplanes = 1;
+	sizes[0] = bcap_dev->fmt.sizeimage;
+	alloc_ctxs[0] = bcap_dev->alloc_ctx;
+
+	return 0;
+}
+
+static int bcap_buffer_init(struct vb2_buffer *vb)
+{
+	struct bcap_buffer *buf = to_bcap_vb(vb);
+
+	INIT_LIST_HEAD(&buf->list);
+	return 0;
+}
+
+static int bcap_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct bcap_buffer *buf = to_bcap_vb(vb);
+	unsigned long size;
+
+	size = bcap_dev->fmt.sizeimage;
+	if (vb2_plane_size(vb, 0) < size) {
+		v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
+				vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+	vb2_set_plane_payload(&buf->vb, 0, size);
+
+	return 0;
+}
+
+static void bcap_buffer_queue(struct vb2_buffer *vb)
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct bcap_buffer *buf = to_bcap_vb(vb);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&bcap_dev->lock, flags);
+	list_add_tail(&buf->list, &bcap_dev->dma_queue);
+	spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_buffer_cleanup(struct vb2_buffer *vb)
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct bcap_buffer *buf = to_bcap_vb(vb);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&bcap_dev->lock, flags);
+	list_del_init(&buf->list);
+	spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_lock(struct vb2_queue *vq)
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+	mutex_lock(&bcap_dev->mutex);
+}
+
+static void bcap_unlock(struct vb2_queue *vq)
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+	mutex_unlock(&bcap_dev->mutex);
+}
+
+static int bcap_start_streaming(struct vb2_queue *vq)
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+	struct ppi_if *ppi = bcap_dev->ppi;
+	struct ppi_params params;
+	int ret;
+
+	/* enable streamon on the sub device */
+	ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
+	if (ret && (ret != -ENOIOCTLCMD)) {
+		v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
+		return ret;
+	}
+
+	/* attach ppi DMA irq handler */
+	ret = ppi->ops->attach_irq(ppi, bcap_isr);
+	if (ret < 0) {
+		v4l2_err(&bcap_dev->v4l2_dev,
+				"Error in attaching interrupt handler\n");
+		return -EFAULT;
+	}
+
+	/* set ppi params */
+	params.width = bcap_dev->fmt.width;
+	params.height = bcap_dev->fmt.height;
+	params.bpp = bcap_dev->bpp;
+	params.ppi_control = bcap_dev->cfg->ppi_control;
+	ret = ppi->ops->set_params(ppi, &params);
+	if (ret < 0) {
+		v4l2_err(&bcap_dev->v4l2_dev,
+				"Error in setting ppi params\n");
+		ppi->ops->detach_irq(ppi);
+		return -EINVAL;
+	}
+
+	INIT_COMPLETION(bcap_dev->comp);
+	return 0;
+}
+
+static int bcap_stop_streaming(struct vb2_queue *vq)
+{
+	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+	struct ppi_if *ppi = bcap_dev->ppi;
+	int ret;
+
+	if (!bcap_dev->started)
+		return 0;
+
+	bcap_dev->started = false;
+	wait_for_completion(&bcap_dev->comp);
+	ppi->ops->stop(ppi);
+	ppi->ops->detach_irq(ppi);
+	ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
+	if (ret && (ret != -ENOIOCTLCMD))
+		v4l2_err(&bcap_dev->v4l2_dev,
+				"stream off failed in subdev\n");
+
+	/* release all active buffers */
+	while (!list_empty(&bcap_dev->dma_queue)) {
+		bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+						struct bcap_buffer, list);
+		list_del(&bcap_dev->next_frm->list);
+		vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
+	}
+	return 0;
+}
+
+static struct vb2_ops bcap_video_qops = {
+	.queue_setup            = bcap_queue_setup,
+	.buf_init               = bcap_buffer_init,
+	.buf_prepare            = bcap_buffer_prepare,
+	.buf_cleanup            = bcap_buffer_cleanup,
+	.buf_queue              = bcap_buffer_queue,
+	.wait_prepare           = bcap_unlock,
+	.wait_finish            = bcap_lock,
+	.start_streaming        = bcap_start_streaming,
+	.stop_streaming         = bcap_stop_streaming,
+};
+
+static int bcap_reqbufs(struct file *file, void *priv,
+			struct v4l2_requestbuffers *req_buf)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_fh *fh = file->private_data;
+	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+	if (bcap_dev->io_usrs != 0) {
+		v4l2_err(&bcap_dev->v4l2_dev, "Only one IO user allowed\n");
+		return -EBUSY;
+	}
+
+	bcap_fh->io_allowed = true;
+	bcap_dev->io_usrs = 1;
+
+	return vb2_reqbufs(&bcap_dev->buffer_queue, req_buf);
+}
+
+static int bcap_querybuf(struct file *file, void *priv,
+				struct v4l2_buffer *buf)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return vb2_querybuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_qbuf(struct file *file, void *priv,
+			struct v4l2_buffer *buf)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_fh *fh = file->private_data;
+	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+	if (!bcap_fh->io_allowed)
+		return -EACCES;
+
+	return vb2_qbuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_dqbuf(struct file *file, void *priv,
+			struct v4l2_buffer *buf)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_fh *fh = file->private_data;
+	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+	if (!bcap_fh->io_allowed)
+		return -EACCES;
+
+	return vb2_dqbuf(&bcap_dev->buffer_queue,
+				buf, file->f_flags & O_NONBLOCK);
+}
+
+static irqreturn_t bcap_isr(int irq, void *dev_id)
+{
+	struct ppi_if *ppi = dev_id;
+	struct bcap_device *bcap_dev = ppi->priv;
+	struct timeval timevalue;
+	struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
+	dma_addr_t addr;
+
+	spin_lock(&bcap_dev->lock);
+
+	if (bcap_dev->cur_frm != bcap_dev->next_frm) {
+		do_gettimeofday(&timevalue);
+		vb->v4l2_buf.timestamp = timevalue;
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+		bcap_dev->cur_frm = bcap_dev->next_frm;
+	}
+
+	ppi->ops->stop(ppi);
+
+	if (!bcap_dev->started) {
+		complete(&bcap_dev->comp);
+	} else {
+		if (!list_empty(&bcap_dev->dma_queue)) {
+			bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+						struct bcap_buffer, list);
+			list_del(&bcap_dev->next_frm->list);
+			addr = vb2_dma_contig_plane_paddr(&bcap_dev->next_frm->vb, 0);
+			ppi->ops->update_addr(ppi, (unsigned long)addr);
+		}
+		ppi->ops->start(ppi);
+	}
+
+	spin_unlock(&bcap_dev->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int bcap_streamon(struct file *file, void *priv,
+				enum v4l2_buf_type buf_type)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct bcap_fh *fh = file->private_data;
+	struct ppi_if *ppi = bcap_dev->ppi;
+	dma_addr_t addr;
+	int ret;
+
+	if (!fh->io_allowed)
+		return -EACCES;
+
+	if (bcap_dev->started)
+		return -EBUSY;
+
+	/* call streamon to start streaming in videobuf */
+	ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
+	if (ret)
+		return ret;
+
+	/* if dma queue is empty, return error */
+	if (list_empty(&bcap_dev->dma_queue)) {
+		v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* get the next frame from the dma queue */
+	bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+					struct bcap_buffer, list);
+	bcap_dev->cur_frm = bcap_dev->next_frm;
+	/* remove buffer from the dma queue */
+	list_del(&bcap_dev->cur_frm->list);
+	addr = vb2_dma_contig_plane_paddr(&bcap_dev->cur_frm->vb, 0);
+	/* update DMA address */
+	ppi->ops->update_addr(ppi, (unsigned long)addr);
+	/* enable ppi */
+	ppi->ops->start(ppi);
+	bcap_dev->started = true;
+
+	return 0;
+err:
+	vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+	return ret;
+}
+
+static int bcap_streamoff(struct file *file, void *priv,
+				enum v4l2_buf_type buf_type)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct bcap_fh *fh = file->private_data;
+
+	if (!fh->io_allowed)
+		return -EACCES;
+
+	if (!bcap_dev->started)
+		return -EINVAL;
+
+	return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+}
+
+static int bcap_queryctrl(struct file *file, void *priv,
+				struct v4l2_queryctrl *qctrl)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return v4l2_subdev_call(bcap_dev->sd, core, queryctrl, qctrl);
+}
+
+static int bcap_g_ctrl(struct file *file, void *priv,
+			struct v4l2_control *ctrl)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return v4l2_subdev_call(bcap_dev->sd, core, g_ctrl, ctrl);
+}
+
+static int bcap_s_ctrl(struct file *file, void *priv,
+			struct v4l2_control *ctrl)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return v4l2_subdev_call(bcap_dev->sd, core, s_ctrl, ctrl);
+}
+
+static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	int ret;
+
+	ret = v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
+	return ret;
+}
+
+static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	if (bcap_dev->std) {
+		*std = bcap_dev->std;
+		return 0;
+	} else
+		return -EINVAL;
+}
+
+static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	int ret;
+
+	/* if streaming is started, return error */
+	if (bcap_dev->started) {
+		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");
+		return -EBUSY;
+	}
+
+	ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
+	if (ret < 0)
+		return ret;
+
+	bcap_dev->std = *std;
+	return 0;
+}
+
+static int bcap_enum_input(struct file *file, void *priv,
+				struct v4l2_input *input)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct bfin_capture_config *config = bcap_dev->cfg;
+	int ret;
+	u32 status;
+
+	if (input->index >= config->num_inputs)
+		return -EINVAL;
+
+	*input = config->inputs[input->index];
+	/* get input status */
+	ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
+	if (!ret)
+		input->status = status;
+	return 0;
+}
+
+static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	*index = bcap_dev->cur_input;
+	return 0;
+}
+
+static int bcap_s_input(struct file *file, void *priv, unsigned int index)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct bfin_capture_config *config = bcap_dev->cfg;
+	struct bcap_route *route;
+	int ret;
+
+	/* if streaming is started, return error */
+	if (bcap_dev->started) {
+		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");
+		return -EBUSY;
+	}
+
+	if (index >= config->num_inputs)
+		return -EINVAL;
+
+	route = &config->routes[index];
+	ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
+				route->input, route->output, 0);
+	if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
+		v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
+		return ret;
+	}
+	bcap_dev->cur_input = index;
+	return 0;
+}
+
+static int bcap_try_format(struct bcap_device *bcap,
+				struct v4l2_pix_format *pixfmt,
+				enum v4l2_mbus_pixelcode *mbus_code,
+				int *bpp)
+{
+	const struct bcap_format *fmt;
+	struct v4l2_mbus_framefmt mbus_fmt;
+	int ret, i;
+
+	for (i = 0; i < BCAP_MAX_FMTS; i++) {
+		if (pixfmt->pixelformat != bcap_formats[i].pixelformat)
+			continue;
+		fmt = &bcap_formats[i];
+		if (mbus_code)
+			*mbus_code = fmt->mbus_code;
+		if (bpp)
+			*bpp = fmt->bpp;
+		v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
+		ret = v4l2_subdev_call(bcap->sd, video,
+					try_mbus_fmt, &mbus_fmt);
+		if (ret < 0)
+			return ret;
+		v4l2_fill_pix_format(pixfmt, &mbus_fmt);
+		pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
+		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int bcap_enum_fmt_vid_cap(struct file *file, void  *priv,
+					struct v4l2_fmtdesc *fmt)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	enum v4l2_mbus_pixelcode mbus_code;
+	u32 index = fmt->index;
+	int ret, i;
+
+	ret = v4l2_subdev_call(bcap_dev->sd, video,
+				enum_mbus_fmt, index, &mbus_code);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < BCAP_MAX_FMTS; i++) {
+		if (mbus_code != bcap_formats[i].mbus_code)
+			continue;
+		fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		strlcpy(fmt->description,
+			bcap_formats[index].desc,
+			sizeof(fmt->description));
+		fmt->pixelformat = bcap_formats[index].pixelformat;
+		return 0;
+	}
+	v4l2_err(&bcap_dev->v4l2_dev,
+			"subdev fmt is not supported by bcap\n");
+	return -EINVAL;
+}
+
+static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
+					struct v4l2_format *fmt)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
+}
+
+static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *fmt)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_mbus_framefmt mbus_fmt;
+	const struct bcap_format *bcap_fmt;
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+	int ret, i;
+
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	ret = v4l2_subdev_call(bcap_dev->sd, video,
+				g_mbus_fmt, &mbus_fmt);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < BCAP_MAX_FMTS; i++) {
+		if (mbus_fmt.code != bcap_formats[i].mbus_code)
+			continue;
+		bcap_fmt = &bcap_formats[i];
+		v4l2_fill_pix_format(pixfmt, &mbus_fmt);
+		pixfmt->pixelformat = bcap_fmt->pixelformat;
+		pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
+		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+		return 0;
+	}
+	v4l2_err(&bcap_dev->v4l2_dev,
+			"subdev fmt is not supported by bcap\n");
+	return -EINVAL;
+}
+
+static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *fmt)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	struct v4l2_mbus_framefmt mbus_fmt;
+	enum v4l2_mbus_pixelcode mbus_code;
+	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+	int ret, bpp;
+
+	/* if streaming is started, return error */
+	if (bcap_dev->started) {
+		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");
+		return -EBUSY;
+	}
+
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	/* see if format works */
+	ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
+	if (ret < 0)
+		return ret;
+
+	v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
+	ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
+	if (ret < 0)
+		return ret;
+	bcap_dev->fmt = *pixfmt;
+	bcap_dev->bpp = bpp;
+	return 0;
+}
+
+static int bcap_querycap(struct file *file, void  *priv,
+				struct v4l2_capability *cap)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+	strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
+	strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
+	return 0;
+}
+
+static int bcap_cropcap(struct file *file, void *priv,
+			struct v4l2_cropcap *crop)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return v4l2_subdev_call(bcap_dev->sd, video, cropcap, crop);
+}
+
+static int bcap_g_parm(struct file *file, void *fh,
+				struct v4l2_streamparm *a)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
+}
+
+static int bcap_s_parm(struct file *file, void *fh,
+				struct v4l2_streamparm *a)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
+}
+
+static int bcap_g_chip_ident(struct file *file, void *priv,
+		struct v4l2_dbg_chip_ident *chip)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	chip->ident = V4L2_IDENT_NONE;
+	chip->revision = 0;
+	if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+			chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+		return -EINVAL;
+
+	return v4l2_subdev_call(bcap_dev->sd, core,
+			g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bcap_dbg_g_register(struct file *file, void *priv,
+		struct v4l2_dbg_register *reg)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return v4l2_subdev_call(bcap_dev->sd, core,
+			g_register, reg);
+}
+
+static int bcap_dbg_s_register(struct file *file, void *priv,
+		struct v4l2_dbg_register *reg)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+
+	return v4l2_subdev_call(bcap_dev->sd, core,
+			s_register, reg);
+}
+#endif
+
+static int bcap_log_status(struct file *file, void *priv)
+{
+	struct bcap_device *bcap_dev = video_drvdata(file);
+	/* status for sub devices */
+	v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
+	.vidioc_querycap         = bcap_querycap,
+	.vidioc_g_fmt_vid_cap    = bcap_g_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap    = bcap_s_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap  = bcap_try_fmt_vid_cap,
+	.vidioc_enum_input       = bcap_enum_input,
+	.vidioc_g_input          = bcap_g_input,
+	.vidioc_s_input          = bcap_s_input,
+	.vidioc_querystd         = bcap_querystd,
+	.vidioc_s_std            = bcap_s_std,
+	.vidioc_g_std            = bcap_g_std,
+	.vidioc_queryctrl        = bcap_queryctrl,
+	.vidioc_g_ctrl           = bcap_g_ctrl,
+	.vidioc_s_ctrl           = bcap_s_ctrl,
+	.vidioc_reqbufs          = bcap_reqbufs,
+	.vidioc_querybuf         = bcap_querybuf,
+	.vidioc_qbuf             = bcap_qbuf,
+	.vidioc_dqbuf            = bcap_dqbuf,
+	.vidioc_streamon         = bcap_streamon,
+	.vidioc_streamoff        = bcap_streamoff,
+	.vidioc_cropcap          = bcap_cropcap,
+	.vidioc_g_parm           = bcap_g_parm,
+	.vidioc_s_parm           = bcap_s_parm,
+	.vidioc_g_chip_ident     = bcap_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.vidioc_g_register       = bcap_dbg_g_register,
+	.vidioc_s_register       = bcap_dbg_s_register,
+#endif
+	.vidioc_log_status       = bcap_log_status,
+};
+
+static struct v4l2_file_operations bcap_fops = {
+	.owner = THIS_MODULE,
+	.open = bcap_open,
+	.release = bcap_release,
+	.unlocked_ioctl = video_ioctl2,
+	.mmap = bcap_mmap,
+#ifndef CONFIG_MMU
+	.get_unmapped_area = bcap_get_unmapped_area,
+#endif
+	.poll = bcap_poll
+};
+
+static int __devinit bcap_probe(struct platform_device *pdev)
+{
+	struct bcap_device *bcap_dev;
+	struct video_device *vfd;
+	struct i2c_adapter *i2c_adap;
+	struct bfin_capture_config *config;
+	struct vb2_queue *q;
+	int ret;
+
+	config = pdev->dev.platform_data;
+	if (!config) {
+		v4l2_err(pdev->dev.driver, "Unable to get board config\n");
+		return -ENODEV;
+	}
+
+	bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
+	if (!bcap_dev) {
+		v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
+		return -ENOMEM;
+	}
+
+	bcap_dev->cfg = config;
+
+	bcap_dev->ppi = ppi_create_instance(config->ppi_info);
+	if (!bcap_dev->ppi) {
+		v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
+		ret = -ENODEV;
+		goto err_free_dev;
+	}
+	bcap_dev->ppi->priv = bcap_dev;
+
+	bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+	if (IS_ERR(bcap_dev->alloc_ctx)) {
+		ret = PTR_ERR(bcap_dev->alloc_ctx);
+		goto err_free_ppi;
+	}
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		ret = -ENOMEM;
+		v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
+		goto err_cleanup_ctx;
+	}
+
+	/* initialize field of video device */
+	vfd->release            = video_device_release;
+	vfd->fops               = &bcap_fops;
+	vfd->ioctl_ops          = &bcap_ioctl_ops;
+	vfd->tvnorms            = 0;
+	vfd->v4l2_dev           = &bcap_dev->v4l2_dev;
+	set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+	strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
+	bcap_dev->video_dev     = vfd;
+
+	ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
+	if (ret) {
+		v4l2_err(pdev->dev.driver,
+				"Unable to register v4l2 device\n");
+		goto err_release_vdev;
+	}
+	v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
+
+	spin_lock_init(&bcap_dev->lock);
+	/* initialize queue */
+	q = &bcap_dev->buffer_queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	q->io_modes = VB2_MMAP;
+	q->drv_priv = bcap_dev;
+	q->buf_struct_size = sizeof(struct bcap_buffer);
+	q->ops = &bcap_video_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+
+	vb2_queue_init(q);
+
+	mutex_init(&bcap_dev->mutex);
+	init_completion(&bcap_dev->comp);
+
+	/* init video dma queues */
+	INIT_LIST_HEAD(&bcap_dev->dma_queue);
+
+	vfd->lock = &bcap_dev->mutex;
+
+	/* register video device */
+	ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
+	if (ret) {
+		v4l2_err(&bcap_dev->v4l2_dev,
+				"Unable to register video device\n");
+		goto err_unreg_v4l2;
+	}
+	video_set_drvdata(bcap_dev->video_dev, bcap_dev);
+	v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
+			video_device_node_name(vfd));
+
+	/* load up the subdevice */
+	i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
+	if (!i2c_adap) {
+		v4l2_err(&bcap_dev->v4l2_dev,
+				"Unable to find i2c adapter\n");
+		goto err_unreg_vdev;
+
+	}
+	bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
+						 i2c_adap,
+						 &config->board_info,
+						 NULL);
+	if (bcap_dev->sd) {
+		int i;
+		/* update tvnorms from the sub devices */
+		for (i = 0; i < config->num_inputs; i++)
+			vfd->tvnorms |= config->inputs[i].std;
+		/* set default std */
+		bcap_dev->std = vfd->tvnorms;
+	} else {
+		v4l2_err(&bcap_dev->v4l2_dev,
+				"Unable to register sub device\n");
+		goto err_unreg_vdev;
+	}
+	v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
+	return 0;
+err_unreg_vdev:
+	video_unregister_device(bcap_dev->video_dev);
+err_unreg_v4l2:
+	v4l2_device_unregister(&bcap_dev->v4l2_dev);
+err_release_vdev:
+	if (!video_is_registered(bcap_dev->video_dev))
+		video_device_release(bcap_dev->video_dev);
+err_cleanup_ctx:
+	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+err_free_ppi:
+	ppi_delete_instance(bcap_dev->ppi);
+err_free_dev:
+	kfree(bcap_dev);
+	return ret;
+}
+
+static int __devexit bcap_remove(struct platform_device *pdev)
+{
+	struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+	struct bcap_device *bcap_dev = container_of(v4l2_dev,
+						struct bcap_device, v4l2_dev);
+
+	video_unregister_device(bcap_dev->video_dev);
+	v4l2_device_unregister(v4l2_dev);
+	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+	ppi_delete_instance(bcap_dev->ppi);
+	kfree(bcap_dev);
+	return 0;
+}
+
+static struct platform_driver bcap_driver = {
+	.driver = {
+		.name   = CAPTURE_DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = bcap_probe,
+	.remove = __devexit_p(bcap_remove),
+};
+
+static __init int bcap_init(void)
+{
+	return platform_driver_register(&bcap_driver);
+}
+
+static __exit void bcap_exit(void)
+{
+	platform_driver_unregister(&bcap_driver);
+}
+
+module_init(bcap_init);
+module_exit(bcap_exit);
+
+MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/video/blackfin/ppi.c
new file mode 100644
index 0000000..3b5f43e
--- /dev/null
+++ b/drivers/media/video/blackfin/ppi.c
@@ -0,0 +1,201 @@
+/*
+ * ppi.c Analog Devices Parallel Peripheral Interface driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/slab.h>
+
+#include <asm/bfin_ppi.h>
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include <media/blackfin/ppi.h>
+
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
+static void ppi_detach_irq(struct ppi_if *ppi);
+static int ppi_start(struct ppi_if *ppi);
+static int ppi_stop(struct ppi_if *ppi);
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
+
+static const struct ppi_ops ppi_ops = {
+	.attach_irq = ppi_attach_irq,
+	.detach_irq = ppi_detach_irq,
+	.start = ppi_start,
+	.stop = ppi_stop,
+	.set_params = ppi_set_params,
+	.update_addr = ppi_update_addr,
+};
+
+static irqreturn_t ppi_irq_err(int irq, void *dev_id)
+{
+	struct ppi_if *ppi = dev_id;
+	const struct ppi_info *info = ppi->info;
+	unsigned short status;
+
+	if (!strcmp(info->name, "ppi")) {
+		struct bfin_ppi_regs __iomem *reg =
+			(struct bfin_ppi_regs __iomem *)info->base;
+		status = bfin_read16(reg->status);
+		if (printk_ratelimit())
+			pr_info("%s: status = 0x%x\n", __func__, status);
+		bfin_write16(&reg->status, 0xff);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
+{
+	const struct ppi_info *info = ppi->info;
+
+	if (request_dma(info->dma_ch, "PPI_DMA") < 0) {
+		pr_err("Unable to allocate DMA channel for PPI\n");
+		return -EBUSY;
+	}
+	set_dma_callback(info->dma_ch, handler, ppi);
+
+	if (request_irq(info->irq_err, ppi_irq_err, IRQF_DISABLED,
+				"PPI ERROR", ppi)) {
+		pr_err("Unable to allocate IRQ for PPI\n");
+		free_dma(info->dma_ch);
+		return -EBUSY;
+	}
+	return 0;
+}
+
+static void ppi_detach_irq(struct ppi_if *ppi)
+{
+	const struct ppi_info *info = ppi->info;
+
+	free_irq(info->irq_err, ppi);
+	free_dma(info->dma_ch);
+}
+
+static int ppi_start(struct ppi_if *ppi)
+{
+	const struct ppi_info *info = ppi->info;
+
+	/* enable DMA */
+	enable_dma(info->dma_ch);
+
+	/* enable PPI */
+	ppi->ppi_control |= PORT_EN;
+	if (!strcmp(info->name, "ppi")) {
+		struct bfin_ppi_regs __iomem *reg =
+			(struct bfin_ppi_regs __iomem *)info->base;
+		bfin_write16(&reg->control, ppi->ppi_control);
+	}
+
+	SSYNC();
+	return 0;
+}
+
+static int ppi_stop(struct ppi_if *ppi)
+{
+	const struct ppi_info *info = ppi->info;
+
+	/* disable PPI */
+	ppi->ppi_control &= ~PORT_EN;
+	if (!strcmp(info->name, "ppi")) {
+		struct bfin_ppi_regs __iomem *reg =
+			(struct bfin_ppi_regs __iomem *)info->base;
+		bfin_write16(&reg->control, ppi->ppi_control);
+	}
+
+	/* disable DMA */
+	clear_dma_irqstat(info->dma_ch);
+	disable_dma(info->dma_ch);
+
+	SSYNC();
+	return 0;
+}
+
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
+{
+	const struct ppi_info *info = ppi->info;
+
+	ppi->bytes_per_line = params->width * params->bpp / 8;
+	ppi->lines_per_frame = params->height;
+
+	/* config DMA */
+	ppi->dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN);
+	if (params->ppi_control & DMA32) {
+		ppi->dma_config |= WDSIZE_32;
+		set_dma_x_count(info->dma_ch, ppi->bytes_per_line >> 2);
+		set_dma_x_modify(info->dma_ch, 4);
+		set_dma_y_modify(info->dma_ch, 4);
+	} else {
+		ppi->dma_config |= WDSIZE_16;
+		set_dma_x_count(info->dma_ch, ppi->bytes_per_line >> 1);
+		set_dma_x_modify(info->dma_ch, 2);
+		set_dma_y_modify(info->dma_ch, 2);
+	}
+	set_dma_y_count(info->dma_ch, ppi->lines_per_frame);
+	set_dma_config(info->dma_ch, ppi->dma_config);
+
+	/* config PPI */
+	ppi->ppi_control = params->ppi_control & ~PORT_EN;
+	if (!strcmp(info->name, "ppi")) {
+		struct bfin_ppi_regs __iomem *reg =
+			(struct bfin_ppi_regs __iomem *)info->base;
+		bfin_write16(&reg->control, ppi->ppi_control);
+		bfin_write16(&reg->count, ppi->bytes_per_line - 1);
+		bfin_write16(&reg->frame, ppi->lines_per_frame);
+	}
+
+	SSYNC();
+	return 0;
+}
+
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
+{
+	set_dma_start_addr(ppi->info->dma_ch, addr);
+}
+
+struct ppi_if *ppi_create_instance(const struct ppi_info *info)
+{
+	struct ppi_if *ppi;
+
+	if (!info || !info->name || !info->pin_req)
+		return NULL;
+
+	if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
+		pr_err("request peripheral failed\n");
+		return NULL;
+	}
+
+	ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
+	if (!ppi) {
+		peripheral_free_list(info->pin_req);
+		pr_err("unable to allocate memory for ppi handle\n");
+		return NULL;
+	}
+	ppi->ops = &ppi_ops;
+	ppi->info = info;
+
+	pr_info("ppi probe success\n");
+	return ppi;
+}
+
+void ppi_delete_instance(struct ppi_if *ppi)
+{
+	peripheral_free_list(ppi->info->pin_req);
+	kfree(ppi);
+}
diff --git a/include/media/blackfin/bfin_capture.h b/include/media/blackfin/bfin_capture.h
new file mode 100644
index 0000000..0905837
--- /dev/null
+++ b/include/media/blackfin/bfin_capture.h
@@ -0,0 +1,33 @@
+#ifndef _BFIN_CAPTURE_H_
+#define _BFIN_CAPTURE_H_
+
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+
+#include <media/blackfin/ppi.h>
+
+struct bcap_route {
+	u32 input;
+	u32 output;
+};
+
+struct bfin_capture_config {
+	/* card name */
+	char *card_name;
+	/* inputs available at the sub device */
+	struct v4l2_input *inputs;
+	/* number of inputs supported */
+	int num_inputs;
+	/* routing information for each input */
+	struct bcap_route *routes;
+	/* i2c bus adapter no */
+	int i2c_adapter_id;
+	/* i2c subdevice board info */
+	struct i2c_board_info board_info;
+	/* ppi board info */
+	const struct ppi_info *ppi_info;
+	/* ppi control */
+	unsigned short ppi_control;
+};
+
+#endif
diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h
new file mode 100644
index 0000000..4e7711a
--- /dev/null
+++ b/include/media/blackfin/ppi.h
@@ -0,0 +1,63 @@
+/*
+ * Analog Devices PPI header file
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _PPI_H_
+#define _PPI_H_
+
+#include <linux/interrupt.h>
+
+struct ppi_if;
+
+struct ppi_params {
+	int width;
+	int height;
+	int bpp;
+	unsigned short ppi_control;
+};
+
+struct ppi_ops {
+	int (*attach_irq)(struct ppi_if *ppi, irq_handler_t handler);
+	void (*detach_irq)(struct ppi_if *ppi);
+	int (*start)(struct ppi_if *ppi);
+	int (*stop)(struct ppi_if *ppi);
+	int (*set_params)(struct ppi_if *ppi, struct ppi_params *params);
+	void (*update_addr)(struct ppi_if *ppi, unsigned long addr);
+};
+
+struct ppi_info {
+	const char *name; /* ppi or eppi */
+	int dma_ch;
+	int irq_err;
+	unsigned long base;
+	const unsigned short *pin_req;
+};
+
+struct ppi_if {
+	int dma_config;
+	int bytes_per_line;
+	int lines_per_frame;
+	unsigned short ppi_control;
+	const struct ppi_ops *ops;
+	const struct ppi_info *info;
+	void *priv;
+};
+
+struct ppi_if *ppi_create_instance(const struct ppi_info *info);
+void ppi_delete_instance(struct ppi_if *ppi);
+#endif
-- 
1.7.0.4



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 2/4 v2][FOR 3.1] v4l2: add adv7183 decoder driver
  2011-09-19 20:59 ` [PATCH 2/4 v2][FOR 3.1] v4l2: add adv7183 decoder driver Scott Jiang
@ 2011-09-26 13:37   ` Hans Verkuil
  0 siblings, 0 replies; 12+ messages in thread
From: Hans Verkuil @ 2011-09-26 13:37 UTC (permalink / raw)
  To: Scott Jiang
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Guennadi Liakhovetski,
	linux-media, uclinux-dist-devel

Hi Scott!

Sorry for the late review, but real-life interfered with my usual v4l review
duties in the past few weeks.

On Monday, September 19, 2011 22:59:39 Scott Jiang wrote:
> this driver is a v4l2 subdevice driver to support
> Analog Devices ADV7183 SDTV video decoder

Since we already have an adv7180.c driver I was wondering if there are many
differences between the two chips or if it might be possible to support both
through the same driver?

> Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>
> ---
>  drivers/media/video/Kconfig        |   10 +
>  drivers/media/video/Makefile       |    1 +
>  drivers/media/video/adv7183.c      |  686 ++++++++++++++++++++++++++++++++++++
>  drivers/media/video/adv7183_regs.h |  107 ++++++
>  include/media/adv7183.h            |   47 +++
>  include/media/v4l2-chip-ident.h    |    3 +
>  6 files changed, 854 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/adv7183.c
>  create mode 100644 drivers/media/video/adv7183_regs.h
>  create mode 100644 include/media/adv7183.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index f574dc0..af8ed6b 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -273,6 +273,16 @@ config VIDEO_ADV7180
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called adv7180.
>  
> +config VIDEO_ADV7183
> +	tristate "Analog Devices ADV7183 decoder"
> +	depends on VIDEO_V4L2 && I2C
> +	---help---
> +	  V4l2 subdevice driver for the Analog Devices
> +	  ADV7183 video decoder.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called adv7183.
> +
>  config VIDEO_BT819
>  	tristate "BT819A VideoStream decoder"
>  	depends on VIDEO_V4L2 && I2C
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 2723900..b0329ae 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
>  obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
>  obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
>  obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
> +obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
>  obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
>  obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
>  obj-$(CONFIG_VIDEO_BT819) += bt819.o
> diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c
> new file mode 100644
> index 0000000..65d682c
> --- /dev/null
> +++ b/drivers/media/video/adv7183.c
> @@ -0,0 +1,686 @@
> +/*
> + * adv7183.c Analog Devices ADV7183 video decoder driver
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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/delay.h>
> +#include <linux/errno.h>
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/adv7183.h>
> +#include <media/v4l2-chip-ident.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +
> +#include "adv7183_regs.h"
> +
> +struct adv7183 {
> +	struct v4l2_subdev sd;
> +	struct v4l2_ctrl_handler hdl;
> +
> +	v4l2_std_id std; /* Current set standard */
> +	u32 input;
> +	u32 output;
> +	unsigned reset_pin;
> +	unsigned oe_pin;
> +	struct v4l2_mbus_framefmt fmt;
> +};
> +
> +/* EXAMPLES USING 27 MHz CLOCK
> + * Mode 1 CVBS Input (Composite Video on AIN5)
> + * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
> + */
> +static const unsigned char adv7183_init_regs[] = {
> +	ADV7183_IN_CTRL, 0x04,           /* CVBS input on AIN5 */
> +	ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
> +	ADV7183_SHAP_FILT_CTRL, 0x41,    /* Set CSFM to SH1 */
> +	ADV7183_ADC_CTRL, 0x16,          /* Power down ADC 1 and ADC 2 */
> +	ADV7183_CTI_DNR_CTRL_4, 0x04,    /* Set DNR threshold to 4 for flat response */
> +	/* ADI recommended programming sequence */
> +	ADV7183_ADI_CTRL, 0x80,
> +	ADV7183_CTI_DNR_CTRL_4, 0x20,
> +	0x52, 0x18,
> +	0x58, 0xED,
> +	0x77, 0xC5,
> +	0x7C, 0x93,
> +	0x7D, 0x00,
> +	0xD0, 0x48,
> +	0xD5, 0xA0,
> +	0xD7, 0xEA,
> +	ADV7183_SD_SATURATION_CR, 0x3E,
> +	ADV7183_PAL_V_END, 0x3E,
> +	ADV7183_PAL_F_TOGGLE, 0x0F,
> +	ADV7183_ADI_CTRL, 0x00,
> +};
> +
> +static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct adv7183, sd);
> +}
> +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
> +{
> +	return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
> +}
> +
> +static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	return i2c_smbus_read_byte_data(client, reg);
> +}
> +
> +static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
> +				unsigned char value)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	return i2c_smbus_write_byte_data(client, reg, value);
> +}
> +
> +static int adv7183_writeregs(struct v4l2_subdev *sd,
> +		const unsigned char *regs, unsigned int num)
> +{
> +	unsigned char reg, data;
> +	unsigned int cnt = 0;
> +
> +	if (num & 0x1) {
> +		v4l2_err(sd, "invalid regs array\n");
> +		return -1;
> +	}
> +
> +	while (cnt < num) {
> +		reg = *regs++;
> +		data = *regs++;
> +		cnt += 2;
> +
> +		adv7183_write(sd, reg, data);
> +	}
> +	return 0;
> +}
> +
> +static int adv7183_log_status(struct v4l2_subdev *sd)
> +{
> +	struct adv7183 *decoder = to_adv7183(sd);
> +
> +	v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_IN_CTRL));
> +	v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_VD_SEL));
> +	v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_OUT_CTRL));
> +	v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
> +	v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_AUTO_DET_EN));
> +	v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_CONTRAST));
> +	v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_BRIGHTNESS));
> +	v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_HUE));
> +	v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_DEF_Y));
> +	v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_DEF_C));
> +	v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_ADI_CTRL));
> +	v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_POW_MANAGE));
> +	v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_STATUS_1),
> +			adv7183_read(sd, ADV7183_STATUS_2),
> +			adv7183_read(sd, ADV7183_STATUS_3));
> +	v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_IDENT));
> +	v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
> +	v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
> +	v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
> +			adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
> +	v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
> +	v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_ADI_CTRL_2));
> +	v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
> +	v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
> +	v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
> +	v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
> +			adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
> +	v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
> +			adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
> +	v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
> +			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
> +			adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
> +	v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
> +			adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
> +			adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
> +	v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_POLARITY));
> +	v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_ADC_CTRL));
> +	v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_SD_OFFSET_CB),
> +			adv7183_read(sd, ADV7183_SD_OFFSET_CR));
> +	v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
> +			adv7183_read(sd, ADV7183_SD_SATURATION_CB),
> +			adv7183_read(sd, ADV7183_SD_SATURATION_CR));
> +	v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
> +			adv7183_read(sd, ADV7183_DRIVE_STR));
> +	v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
> +	return 0;
> +}
> +
> +static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
> +{
> +	struct adv7183 *decoder = to_adv7183(sd);
> +	int reg;
> +
> +	if (std == decoder->std)
> +		return 0;
> +	reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
> +	if (std == V4L2_STD_ALL)
> +		adv7183_write(sd, ADV7183_IN_CTRL, reg);
> +	else {
> +		if (std == V4L2_STD_PAL_60)
> +			reg |= 0x60;
> +		else if (std == V4L2_STD_NTSC_443)
> +			reg |= 0x70;
> +		else if (std == V4L2_STD_PAL_N)
> +			reg |= 0x90;
> +		else if (std == V4L2_STD_PAL_M)
> +			reg |= 0xA0;
> +		else if (std == V4L2_STD_PAL_Nc)
> +			reg |= 0xC0;
> +		else if (std & V4L2_STD_PAL)
> +			reg |= 0x80;
> +		else if (std & V4L2_STD_NTSC)
> +			reg |= 0x50;
> +		else if (std & V4L2_STD_SECAM)
> +			reg |= 0xE0;
> +		else
> +			return -EINVAL;
> +		adv7183_write(sd, ADV7183_IN_CTRL, reg);
> +	}
> +
> +	decoder->std = std;
> +
> +	return 0;
> +}
> +
> +static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
> +{
> +	int reg;
> +
> +	reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
> +	adv7183_write(sd, ADV7183_POW_MANAGE, reg);
> +	/* wait 5ms before any further i2c writes are performed */
> +	usleep_range(5000, 10000);
> +	return 0;
> +}
> +
> +static int adv7183_s_routing(struct v4l2_subdev *sd,
> +				u32 input, u32 output, u32 config)
> +{
> +	struct adv7183 *decoder = to_adv7183(sd);
> +	int reg;
> +
> +	if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
> +		return -EINVAL;
> +
> +	if (input != decoder->input) {
> +		decoder->input = input;
> +		reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
> +		switch (input) {
> +		case ADV7183_COMPOSITE1:
> +			reg |= 0x1;
> +			break;
> +		case ADV7183_COMPOSITE2:
> +			reg |= 0x2;
> +			break;
> +		case ADV7183_COMPOSITE3:
> +			reg |= 0x3;
> +			break;
> +		case ADV7183_COMPOSITE4:
> +			reg |= 0x4;
> +			break;
> +		case ADV7183_COMPOSITE5:
> +			reg |= 0x5;
> +			break;
> +		case ADV7183_COMPOSITE6:
> +			reg |= 0xB;
> +			break;
> +		case ADV7183_COMPOSITE7:
> +			reg |= 0xC;
> +			break;
> +		case ADV7183_COMPOSITE8:
> +			reg |= 0xD;
> +			break;
> +		case ADV7183_COMPOSITE9:
> +			reg |= 0xE;
> +			break;
> +		case ADV7183_COMPOSITE10:
> +			reg |= 0xF;
> +			break;
> +		case ADV7183_SVIDEO0:
> +			reg |= 0x6;
> +			break;
> +		case ADV7183_SVIDEO1:
> +			reg |= 0x7;
> +			break;
> +		case ADV7183_SVIDEO2:
> +			reg |= 0x8;
> +			break;
> +		case ADV7183_COMPONENT0:
> +			reg |= 0x9;
> +			break;
> +		case ADV7183_COMPONENT1:
> +			reg |= 0xA;
> +			break;
> +		default:
> +			break;
> +		}
> +		adv7183_write(sd, ADV7183_IN_CTRL, reg);
> +	}
> +
> +	if (output != decoder->output) {
> +		decoder->output = output;
> +		reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
> +		switch (output) {
> +		case ADV7183_16BIT_OUT:
> +			reg |= 0x9;
> +			break;
> +		default:
> +			reg |= 0xC;
> +			break;
> +		}
> +		adv7183_write(sd, ADV7183_OUT_CTRL, reg);
> +	}
> +
> +	return 0;
> +}
> +
> +static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = to_sd(ctrl);
> +	int val = ctrl->val;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_BRIGHTNESS:
> +		if (val < 0)
> +			val = 127 - val;
> +		adv7183_write(sd, ADV7183_BRIGHTNESS, val);
> +		break;
> +	case V4L2_CID_CONTRAST:
> +		adv7183_write(sd, ADV7183_CONTRAST, val);
> +		break;
> +	case V4L2_CID_SATURATION:
> +		adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
> +		adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
> +		break;
> +	case V4L2_CID_HUE:
> +		adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
> +		adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
> +{
> +	struct adv7183 *decoder = to_adv7183(sd);
> +	int reg;
> +
> +	if (decoder->std == V4L2_STD_ALL) {

This is wrong. The querystd should always query the current standard,
regardless of whether decoder->std is set or not.

The user may have set the standard to e.g. PAL, but that doesn't mean it
is really PAL.

> +		reg = adv7183_read(sd, ADV7183_STATUS_1);
> +		switch ((reg >> 0x4) & 0x7) {
> +		case 0:
> +			*std = V4L2_STD_NTSC;
> +			break;
> +		case 1:
> +			*std = V4L2_STD_NTSC_443;
> +			break;
> +		case 2:
> +			*std = V4L2_STD_PAL_M;
> +			break;
> +		case 3:
> +			*std = V4L2_STD_PAL_60;
> +			break;
> +		case 4:
> +			*std = V4L2_STD_PAL;
> +			break;
> +		case 5:
> +			*std = V4L2_STD_SECAM;
> +			break;
> +		case 6:
> +			*std = V4L2_STD_PAL_Nc;
> +			break;
> +		case 7:
> +			*std = V4L2_STD_SECAM;
> +			break;
> +		default:
> +			*std = V4L2_STD_UNKNOWN;
> +			break;
> +		}
> +	} else
> +		*std = decoder->std;
> +	return 0;
> +}
> +
> +static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
> +{
> +	int reg;
> +
> +	*status = V4L2_IN_ST_NO_SIGNAL;
> +	reg = adv7183_read(sd, ADV7183_STATUS_1);
> +	if (reg < 0)
> +		return reg;
> +	else if (reg & 0x1)

No need for the 'else' keyword here.

> +		*status = 0;
> +	return 0;
> +}
> +
> +static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
> +				enum v4l2_mbus_pixelcode *code)
> +{
> +	if (index > 0)
> +		return -EINVAL;
> +
> +	*code = V4L2_MBUS_FMT_UYVY8_2X8;
> +	return 0;
> +}
> +
> +static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
> +				struct v4l2_mbus_framefmt *fmt)
> +{
> +	v4l2_std_id std;
> +
> +	fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
> +	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +	adv7183_querystd(sd, &std);

No, use decoder->std here. That's what the user specified, so that's what
should be used here.

> +	if (std & V4L2_STD_525_60) {
> +		fmt->field = V4L2_FIELD_SEQ_TB;
> +		fmt->width = 720;
> +		fmt->height = 480;
> +	} else {
> +		fmt->field = V4L2_FIELD_SEQ_BT;
> +		fmt->width = 720;
> +		fmt->height = 576;
> +	}
> +	return 0;
> +}
> +
> +static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
> +				struct v4l2_mbus_framefmt *fmt)
> +{
> +	struct adv7183 *decoder = to_adv7183(sd);
> +
> +	adv7183_try_mbus_fmt(sd, fmt);
> +	decoder->fmt = *fmt;
> +	return 0;
> +}
> +
> +static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
> +				struct v4l2_mbus_framefmt *fmt)
> +{
> +	struct adv7183 *decoder = to_adv7183(sd);
> +
> +	*fmt = decoder->fmt;
> +	return 0;
> +}
> +
> +static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct adv7183 *decoder = to_adv7183(sd);
> +
> +	if (enable)
> +		gpio_direction_output(decoder->oe_pin, 0);
> +	else
> +		gpio_direction_output(decoder->oe_pin, 1);
> +	udelay(1);
> +	return 0;
> +}
> +
> +static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
> +		struct v4l2_dbg_chip_ident *chip)
> +{
> +	int rev;
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	/* 0x11 for adv7183, 0x13 for adv7183b */
> +	rev = adv7183_read(sd, ADV7183_IDENT);
> +
> +	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	if (!v4l2_chip_match_i2c_client(client, &reg->match))
> +		return -EINVAL;
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +	reg->val = adv7183_read(sd, reg->reg & 0xff);
> +	reg->size = 1;
> +	return 0;
> +}
> +
> +static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	if (!v4l2_chip_match_i2c_client(client, &reg->match))
> +		return -EINVAL;
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +	adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
> +	return 0;
> +}
> +#endif
> +
> +static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
> +	.s_ctrl = adv7183_s_ctrl,
> +};
> +
> +static const struct v4l2_subdev_core_ops adv7183_core_ops = {
> +	.log_status = adv7183_log_status,
> +	.s_std = adv7183_s_std,
> +	.reset = adv7183_reset,
> +	.g_chip_ident = adv7183_g_chip_ident,
> +	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
> +	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
> +	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
> +	.g_ctrl = v4l2_subdev_g_ctrl,
> +	.s_ctrl = v4l2_subdev_s_ctrl,
> +	.queryctrl = v4l2_subdev_queryctrl,
> +	.querymenu = v4l2_subdev_querymenu,

Don't add these control ops here: these are only needed if this driver is ever
used by a bridge driver that does not yet support the control framework.

New subdevs shouldn't set these ops.

Eventually these control ops will disappear.

> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.g_register = adv7183_g_register,
> +	.s_register = adv7183_s_register,
> +#endif
> +};
> +
> +static const struct v4l2_subdev_video_ops adv7183_video_ops = {
> +	.s_routing = adv7183_s_routing,
> +	.querystd = adv7183_querystd,
> +	.g_input_status = adv7183_g_input_status,
> +	.enum_mbus_fmt = adv7183_enum_mbus_fmt,
> +	.try_mbus_fmt = adv7183_try_mbus_fmt,
> +	.s_mbus_fmt = adv7183_s_mbus_fmt,
> +	.g_mbus_fmt = adv7183_g_mbus_fmt,
> +	.s_stream = adv7183_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops adv7183_ops = {
> +	.core = &adv7183_core_ops,
> +	.video = &adv7183_video_ops,
> +};
> +
> +static int adv7183_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct adv7183 *decoder;
> +	struct v4l2_subdev *sd;
> +	struct v4l2_ctrl_handler *hdl;
> +	int ret;
> +	const unsigned *pin_array;
> +
> +	/* Check if the adapter supports the needed features */
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> +		return -EIO;
> +
> +	v4l_info(client, "chip found @ 0x%02x (%s)\n",
> +			client->addr << 1, client->adapter->name);
> +
> +	pin_array = client->dev.platform_data;
> +	if (pin_array == NULL)
> +		return -EINVAL;
> +
> +	decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
> +	if (decoder == NULL)
> +		return -ENOMEM;
> +
> +	decoder->reset_pin = pin_array[0];
> +	decoder->oe_pin = pin_array[1];
> +
> +	if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
> +		v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
> +		ret = -EBUSY;
> +		goto err_free_decoder;
> +	}
> +
> +	if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
> +		v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
> +		ret = -EBUSY;
> +		goto err_free_reset;
> +	}
> +
> +	sd = &decoder->sd;
> +	v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
> +
> +	hdl = &decoder->hdl;
> +	v4l2_ctrl_handler_init(hdl, 4);
> +	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
> +			V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
> +	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
> +			V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
> +	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
> +			V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
> +	v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
> +			V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
> +	/* hook the control handler into the driver */
> +	sd->ctrl_handler = hdl;
> +	if (hdl->error) {
> +		ret = hdl->error;
> +
> +		v4l2_ctrl_handler_free(hdl);
> +		goto err_free_oe;
> +	}
> +
> +	decoder->std = V4L2_STD_ALL; /* Default is autodetect */

No, while there are some really old drivers that abuse this, this shouldn't
be done for newer drivers. Just pick PAL or NTSC as the default and set up the
chip accordingly.

V4L2 doesn't support a autodetect standard. Instead, applications are expected
to query the standard using QUERYSTD.

> +	decoder->input = ADV7183_COMPOSITE4;
> +	decoder->output = ADV7183_8BIT_OUT;
> +
> +	gpio_direction_output(decoder->oe_pin, 1);
> +	/* reset chip */
> +	gpio_direction_output(decoder->reset_pin, 0);
> +	/* reset pulse width at least 5ms */
> +	mdelay(10);
> +	gpio_direction_output(decoder->reset_pin, 1);
> +	/* wait 5ms before any further i2c writes are performed */
> +	mdelay(5);
> +
> +	adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
> +
> +	/* initialize the hardware to the default control values */
> +	v4l2_ctrl_handler_setup(hdl);

I believe this was mentioned already, but you should check the return code from
v4l2_ctrl_handler_setup().

> +
> +	return 0;
> +err_free_oe:
> +	gpio_free(decoder->oe_pin);
> +err_free_reset:
> +	gpio_free(decoder->reset_pin);
> +err_free_decoder:
> +	kfree(decoder);
> +	return ret;
> +}
> +
> +static int adv7183_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct adv7183 *decoder = to_adv7183(sd);
> +
> +	v4l2_device_unregister_subdev(sd);
> +	v4l2_ctrl_handler_free(sd->ctrl_handler);
> +	gpio_free(decoder->oe_pin);
> +	gpio_free(decoder->reset_pin);
> +	kfree(decoder);
> +	return 0;
> +}
> +
> +static const struct i2c_device_id adv7183_id[] = {
> +	{"adv7183", 0},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, adv7183_id);
> +
> +static struct i2c_driver adv7183_driver = {
> +	.driver = {
> +		.owner  = THIS_MODULE,
> +		.name   = "adv7183",
> +	},
> +	.probe          = adv7183_probe,
> +	.remove         = __devexit_p(adv7183_remove),
> +	.id_table       = adv7183_id,
> +};
> +
> +static __init int adv7183_init(void)
> +{
> +	return i2c_add_driver(&adv7183_driver);
> +}
> +
> +static __exit void adv7183_exit(void)
> +{
> +	i2c_del_driver(&adv7183_driver);
> +}
> +
> +module_init(adv7183_init);
> +module_exit(adv7183_exit);
> +
> +MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
> +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h
> new file mode 100644
> index 0000000..4a5b7d2
> --- /dev/null
> +++ b/drivers/media/video/adv7183_regs.h
> @@ -0,0 +1,107 @@
> +/*
> + * adv7183 - Analog Devices ADV7183 video decoder registers
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#ifndef _ADV7183_REGS_H_
> +#define _ADV7183_REGS_H_
> +
> +#define ADV7183_IN_CTRL            0x00 /* Input control */
> +#define ADV7183_VD_SEL             0x01 /* Video selection */
> +#define ADV7183_OUT_CTRL           0x03 /* Output control */
> +#define ADV7183_EXT_OUT_CTRL       0x04 /* Extended output control */
> +#define ADV7183_AUTO_DET_EN        0x07 /* Autodetect enable */
> +#define ADV7183_CONTRAST           0x08 /* Contrast */
> +#define ADV7183_BRIGHTNESS         0x0A /* Brightness */
> +#define ADV7183_HUE                0x0B /* Hue */
> +#define ADV7183_DEF_Y              0x0C /* Default value Y */
> +#define ADV7183_DEF_C              0x0D /* Default value C */
> +#define ADV7183_ADI_CTRL           0x0E /* ADI control */
> +#define ADV7183_POW_MANAGE         0x0F /* Power Management */
> +#define ADV7183_STATUS_1           0x10 /* Status 1 */
> +#define ADV7183_IDENT              0x11 /* Ident */
> +#define ADV7183_STATUS_2           0x12 /* Status 2 */
> +#define ADV7183_STATUS_3           0x13 /* Status 3 */
> +#define ADV7183_ANAL_CLAMP_CTRL    0x14 /* Analog clamp control */
> +#define ADV7183_DIGI_CLAMP_CTRL_1  0x15 /* Digital clamp control 1 */
> +#define ADV7183_SHAP_FILT_CTRL     0x17 /* Shaping filter control */
> +#define ADV7183_SHAP_FILT_CTRL_2   0x18 /* Shaping filter control 2 */
> +#define ADV7183_COMB_FILT_CTRL     0x19 /* Comb filter control */
> +#define ADV7183_ADI_CTRL_2         0x1D /* ADI control 2 */
> +#define ADV7183_PIX_DELAY_CTRL     0x27 /* Pixel delay control */
> +#define ADV7183_MISC_GAIN_CTRL     0x2B /* Misc gain control */
> +#define ADV7183_AGC_MODE_CTRL      0x2C /* AGC mode control */
> +#define ADV7183_CHRO_GAIN_CTRL_1   0x2D /* Chroma gain control 1 */
> +#define ADV7183_CHRO_GAIN_CTRL_2   0x2E /* Chroma gain control 2 */
> +#define ADV7183_LUMA_GAIN_CTRL_1   0x2F /* Luma gain control 1 */
> +#define ADV7183_LUMA_GAIN_CTRL_2   0x30 /* Luma gain control 2 */
> +#define ADV7183_VS_FIELD_CTRL_1    0x31 /* Vsync field control 1 */
> +#define ADV7183_VS_FIELD_CTRL_2    0x32 /* Vsync field control 2 */
> +#define ADV7183_VS_FIELD_CTRL_3    0x33 /* Vsync field control 3 */
> +#define ADV7183_HS_POS_CTRL_1      0x34 /* Hsync positon control 1 */
> +#define ADV7183_HS_POS_CTRL_2      0x35 /* Hsync positon control 2 */
> +#define ADV7183_HS_POS_CTRL_3      0x36 /* Hsync positon control 3 */
> +#define ADV7183_POLARITY           0x37 /* Polarity */
> +#define ADV7183_NTSC_COMB_CTRL     0x38 /* NTSC comb control */
> +#define ADV7183_PAL_COMB_CTRL      0x39 /* PAL comb control */
> +#define ADV7183_ADC_CTRL           0x3A /* ADC control */
> +#define ADV7183_MAN_WIN_CTRL       0x3D /* Manual window control */
> +#define ADV7183_RESAMPLE_CTRL      0x41 /* Resample control */
> +#define ADV7183_GEMSTAR_CTRL_1     0x48 /* Gemstar ctrl 1 */
> +#define ADV7183_GEMSTAR_CTRL_2     0x49 /* Gemstar ctrl 2 */
> +#define ADV7183_GEMSTAR_CTRL_3     0x4A /* Gemstar ctrl 3 */
> +#define ADV7183_GEMSTAR_CTRL_4     0x4B /* Gemstar ctrl 4 */
> +#define ADV7183_GEMSTAR_CTRL_5     0x4C /* Gemstar ctrl 5 */
> +#define ADV7183_CTI_DNR_CTRL_1     0x4D /* CTI DNR ctrl 1 */
> +#define ADV7183_CTI_DNR_CTRL_2     0x4E /* CTI DNR ctrl 2 */
> +#define ADV7183_CTI_DNR_CTRL_4     0x50 /* CTI DNR ctrl 4 */
> +#define ADV7183_LOCK_CNT           0x51 /* Lock count */
> +#define ADV7183_FREE_LINE_LEN      0x8F /* Free-Run line length 1 */
> +#define ADV7183_VBI_INFO           0x90 /* VBI info */
> +#define ADV7183_WSS_1              0x91 /* WSS 1 */
> +#define ADV7183_WSS_2              0x92 /* WSS 2 */
> +#define ADV7183_EDTV_1             0x93 /* EDTV 1 */
> +#define ADV7183_EDTV_2             0x94 /* EDTV 2 */
> +#define ADV7183_EDTV_3             0x95 /* EDTV 3 */
> +#define ADV7183_CGMS_1             0x96 /* CGMS 1 */
> +#define ADV7183_CGMS_2             0x97 /* CGMS 2 */
> +#define ADV7183_CGMS_3             0x98 /* CGMS 3 */
> +#define ADV7183_CCAP_1             0x99 /* CCAP 1 */
> +#define ADV7183_CCAP_2             0x9A /* CCAP 2 */
> +#define ADV7183_LETTERBOX_1        0x9B /* Letterbox 1 */
> +#define ADV7183_LETTERBOX_2        0x9C /* Letterbox 2 */
> +#define ADV7183_LETTERBOX_3        0x9D /* Letterbox 3 */
> +#define ADV7183_CRC_EN             0xB2 /* CRC enable */
> +#define ADV7183_ADC_SWITCH_1       0xC3 /* ADC switch 1 */
> +#define ADV7183_ADC_SWITCH_2       0xC4 /* ADC swithc 2 */
> +#define ADV7183_LETTERBOX_CTRL_1   0xDC /* Letterbox control 1 */
> +#define ADV7183_LETTERBOX_CTRL_2   0xDD /* Letterbox control 2 */
> +#define ADV7183_SD_OFFSET_CB       0xE1 /* SD offset Cb */
> +#define ADV7183_SD_OFFSET_CR       0xE2 /* SD offset Cr */
> +#define ADV7183_SD_SATURATION_CB   0xE3 /* SD saturation Cb */
> +#define ADV7183_SD_SATURATION_CR   0xE4 /* SD saturation Cr */
> +#define ADV7183_NTSC_V_BEGIN       0xE5 /* NTSC V bit begin */
> +#define ADV7183_NTSC_V_END         0xE6 /* NTSC V bit end */
> +#define ADV7183_NTSC_F_TOGGLE      0xE7 /* NTSC F bit toggle */
> +#define ADV7183_PAL_V_BEGIN        0xE8 /* PAL V bit begin */
> +#define ADV7183_PAL_V_END          0xE9 /* PAL V bit end */
> +#define ADV7183_PAL_F_TOGGLE       0xEA /* PAL F bit toggle */
> +#define ADV7183_DRIVE_STR          0xF4 /* Drive strength */
> +#define ADV7183_IF_COMP_CTRL       0xF8 /* IF comp control */
> +#define ADV7183_VS_MODE_CTRL       0xF9 /* VS mode control */
> +
> +#endif
> diff --git a/include/media/adv7183.h b/include/media/adv7183.h
> new file mode 100644
> index 0000000..c5c2d37
> --- /dev/null
> +++ b/include/media/adv7183.h
> @@ -0,0 +1,47 @@
> +/*
> + * adv7183.h - definition for adv7183 inputs and outputs
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#ifndef _ADV7183_H_
> +#define _ADV7183_H_
> +
> +/* ADV7183 HW inputs */
> +#define ADV7183_COMPOSITE0  0  /* CVBS in on AIN1 */
> +#define ADV7183_COMPOSITE1  1  /* CVBS in on AIN2 */
> +#define ADV7183_COMPOSITE2  2  /* CVBS in on AIN3 */
> +#define ADV7183_COMPOSITE3  3  /* CVBS in on AIN4 */
> +#define ADV7183_COMPOSITE4  4  /* CVBS in on AIN5 */
> +#define ADV7183_COMPOSITE5  5  /* CVBS in on AIN6 */
> +#define ADV7183_COMPOSITE6  6  /* CVBS in on AIN7 */
> +#define ADV7183_COMPOSITE7  7  /* CVBS in on AIN8 */
> +#define ADV7183_COMPOSITE8  8  /* CVBS in on AIN9 */
> +#define ADV7183_COMPOSITE9  9  /* CVBS in on AIN10 */
> +#define ADV7183_COMPOSITE10 10 /* CVBS in on AIN11 */
> +
> +#define ADV7183_SVIDEO0     11 /* Y on AIN1, C on AIN4 */
> +#define ADV7183_SVIDEO1     12 /* Y on AIN2, C on AIN5 */
> +#define ADV7183_SVIDEO2     13 /* Y on AIN3, C on AIN6 */
> +
> +#define ADV7183_COMPONENT0  14 /* Y on AIN1, Pr on AIN4, Pb on AIN5 */
> +#define ADV7183_COMPONENT1  15 /* Y on AIN2, Pr on AIN3, Pb on AIN6 */
> +
> +/* ADV7183 HW outputs */
> +#define ADV7183_8BIT_OUT    0
> +#define ADV7183_16BIT_OUT   1
> +
> +#endif
> diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
> index 63fd9d3..20fd16d 100644
> --- a/include/media/v4l2-chip-ident.h
> +++ b/include/media/v4l2-chip-ident.h
> @@ -162,6 +162,9 @@ enum {
>  	/* module adv7180: just ident 7180 */
>  	V4L2_IDENT_ADV7180 = 7180,
>  
> +	/* module adv7183: just ident 7183 */
> +	V4L2_IDENT_ADV7183 = 7183,
> +
>  	/* module saa7185: just ident 7185 */
>  	V4L2_IDENT_SAA7185 = 7185,
>  
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/4 v2][FOR 3.1] v4l2: add vs6624 sensor driver
  2011-09-19 20:59 ` [PATCH 3/4 v2][FOR 3.1] v4l2: add vs6624 sensor driver Scott Jiang
@ 2011-09-26 13:39   ` Hans Verkuil
  0 siblings, 0 replies; 12+ messages in thread
From: Hans Verkuil @ 2011-09-26 13:39 UTC (permalink / raw)
  To: Scott Jiang
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Guennadi Liakhovetski,
	linux-media, uclinux-dist-devel

On Monday, September 19, 2011 22:59:40 Scott Jiang wrote:
> this is a v4l2 sensor-level driver for the ST VS6624 camera
> 
> Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>
> ---
>  drivers/media/video/Kconfig       |   10 +
>  drivers/media/video/Makefile      |    1 +
>  drivers/media/video/vs6624.c      |  930 +++++++++++++++++++++++++++++++++++++
>  drivers/media/video/vs6624_regs.h |  337 ++++++++++++++
>  include/media/v4l2-chip-ident.h   |    3 +
>  5 files changed, 1281 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/vs6624.c
>  create mode 100644 drivers/media/video/vs6624_regs.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index af8ed6b..1c03abf 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -477,6 +477,16 @@ config VIDEO_OV7670
>  	  OV7670 VGA camera.  It currently only works with the M88ALP01
>  	  controller.
>  
> +config VIDEO_VS6624
> +	tristate "ST VS6624 sensor support"
> +	depends on VIDEO_V4L2 && I2C
> +	---help---
> +	  This is a Video4Linux2 sensor-level driver for the ST VS6624
> +	  camera.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called vs6624.
> +
>  config VIDEO_MT9V011
>  	tristate "Micron mt9v011 sensor support"
>  	depends on I2C && VIDEO_V4L2
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index b0329ae..03b55ed 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
>  obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
>  obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
>  obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
> +obj-$(CONFIG_VIDEO_VS6624)  += vs6624.o
>  obj-$(CONFIG_VIDEO_BT819) += bt819.o
>  obj-$(CONFIG_VIDEO_BT856) += bt856.o
>  obj-$(CONFIG_VIDEO_BT866) += bt866.o
> diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c
> new file mode 100644
> index 0000000..50c2aa5
> --- /dev/null
> +++ b/drivers/media/video/vs6624.c
> @@ -0,0 +1,930 @@
> +/*
> + * vs6624.c ST VS6624 CMOS image sensor driver
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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/delay.h>
> +#include <linux/errno.h>
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-chip-ident.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-mediabus.h>
> +
> +#include "vs6624_regs.h"
> +
> +#define VGA_WIDTH       640
> +#define VGA_HEIGHT      480
> +#define QVGA_WIDTH      320
> +#define QVGA_HEIGHT     240
> +#define QQVGA_WIDTH     160
> +#define QQVGA_HEIGHT    120
> +#define CIF_WIDTH       352
> +#define CIF_HEIGHT      288
> +#define QCIF_WIDTH      176
> +#define QCIF_HEIGHT     144
> +#define QQCIF_WIDTH     88
> +#define QQCIF_HEIGHT    72
> +
> +#define MAX_FRAME_RATE  30
> +
> +struct vs6624 {
> +	struct v4l2_subdev sd;
> +	struct v4l2_ctrl_handler hdl;
> +	struct v4l2_fract frame_rate;
> +	struct v4l2_mbus_framefmt fmt;
> +	unsigned ce_pin;
> +};
> +
> +static const struct vs6624_format {
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	enum v4l2_colorspace colorspace;
> +} vs6624_formats[] = {
> +	{
> +		.mbus_code      = V4L2_MBUS_FMT_UYVY8_2X8,
> +		.colorspace     = V4L2_COLORSPACE_JPEG,
> +	},
> +	{
> +		.mbus_code      = V4L2_MBUS_FMT_YUYV8_2X8,
> +		.colorspace     = V4L2_COLORSPACE_JPEG,
> +	},
> +	{
> +		.mbus_code      = V4L2_MBUS_FMT_RGB565_2X8_LE,
> +		.colorspace     = V4L2_COLORSPACE_SRGB,
> +	},
> +};
> +
> +static struct v4l2_mbus_framefmt vs6624_default_fmt = {
> +	.width = VGA_WIDTH,
> +	.height = VGA_HEIGHT,
> +	.code = V4L2_MBUS_FMT_UYVY8_2X8,
> +	.field = V4L2_FIELD_NONE,
> +	.colorspace = V4L2_COLORSPACE_JPEG,
> +};
> +
> +static const u16 vs6624_p1[] = {
> +	0x8104, 0x03,
> +	0x8105, 0x01,
> +	0xc900, 0x03,
> +	0xc904, 0x47,
> +	0xc905, 0x10,
> +	0xc906, 0x80,
> +	0xc907, 0x3a,
> +	0x903a, 0x02,
> +	0x903b, 0x47,
> +	0x903c, 0x15,
> +	0xc908, 0x31,
> +	0xc909, 0xdc,
> +	0xc90a, 0x80,
> +	0xc90b, 0x44,
> +	0x9044, 0x02,
> +	0x9045, 0x31,
> +	0x9046, 0xe2,
> +	0xc90c, 0x07,
> +	0xc90d, 0xe0,
> +	0xc90e, 0x80,
> +	0xc90f, 0x47,
> +	0x9047, 0x90,
> +	0x9048, 0x83,
> +	0x9049, 0x81,
> +	0x904a, 0xe0,
> +	0x904b, 0x60,
> +	0x904c, 0x08,
> +	0x904d, 0x90,
> +	0x904e, 0xc0,
> +	0x904f, 0x43,
> +	0x9050, 0x74,
> +	0x9051, 0x01,
> +	0x9052, 0xf0,
> +	0x9053, 0x80,
> +	0x9054, 0x05,
> +	0x9055, 0xE4,
> +	0x9056, 0x90,
> +	0x9057, 0xc0,
> +	0x9058, 0x43,
> +	0x9059, 0xf0,
> +	0x905a, 0x02,
> +	0x905b, 0x07,
> +	0x905c, 0xec,
> +	0xc910, 0x5d,
> +	0xc911, 0xca,
> +	0xc912, 0x80,
> +	0xc913, 0x5d,
> +	0x905d, 0xa3,
> +	0x905e, 0x04,
> +	0x905f, 0xf0,
> +	0x9060, 0xa3,
> +	0x9061, 0x04,
> +	0x9062, 0xf0,
> +	0x9063, 0x22,
> +	0xc914, 0x72,
> +	0xc915, 0x92,
> +	0xc916, 0x80,
> +	0xc917, 0x64,
> +	0x9064, 0x74,
> +	0x9065, 0x01,
> +	0x9066, 0x02,
> +	0x9067, 0x72,
> +	0x9068, 0x95,
> +	0xc918, 0x47,
> +	0xc919, 0xf2,
> +	0xc91a, 0x81,
> +	0xc91b, 0x69,
> +	0x9169, 0x74,
> +	0x916a, 0x02,
> +	0x916b, 0xf0,
> +	0x916c, 0xec,
> +	0x916d, 0xb4,
> +	0x916e, 0x10,
> +	0x916f, 0x0a,
> +	0x9170, 0x90,
> +	0x9171, 0x80,
> +	0x9172, 0x16,
> +	0x9173, 0xe0,
> +	0x9174, 0x70,
> +	0x9175, 0x04,
> +	0x9176, 0x90,
> +	0x9177, 0xd3,
> +	0x9178, 0xc4,
> +	0x9179, 0xf0,
> +	0x917a, 0x22,
> +	0xc91c, 0x0a,
> +	0xc91d, 0xbe,
> +	0xc91e, 0x80,
> +	0xc91f, 0x73,
> +	0x9073, 0xfc,
> +	0x9074, 0xa3,
> +	0x9075, 0xe0,
> +	0x9076, 0xf5,
> +	0x9077, 0x82,
> +	0x9078, 0x8c,
> +	0x9079, 0x83,
> +	0x907a, 0xa3,
> +	0x907b, 0xa3,
> +	0x907c, 0xe0,
> +	0x907d, 0xfc,
> +	0x907e, 0xa3,
> +	0x907f, 0xe0,
> +	0x9080, 0xc3,
> +	0x9081, 0x9f,
> +	0x9082, 0xff,
> +	0x9083, 0xec,
> +	0x9084, 0x9e,
> +	0x9085, 0xfe,
> +	0x9086, 0x02,
> +	0x9087, 0x0a,
> +	0x9088, 0xea,
> +	0xc920, 0x47,
> +	0xc921, 0x38,
> +	0xc922, 0x80,
> +	0xc923, 0x89,
> +	0x9089, 0xec,
> +	0x908a, 0xd3,
> +	0x908b, 0x94,
> +	0x908c, 0x20,
> +	0x908d, 0x40,
> +	0x908e, 0x01,
> +	0x908f, 0x1c,
> +	0x9090, 0x90,
> +	0x9091, 0xd3,
> +	0x9092, 0xd4,
> +	0x9093, 0xec,
> +	0x9094, 0xf0,
> +	0x9095, 0x02,
> +	0x9096, 0x47,
> +	0x9097, 0x3d,
> +	0xc924, 0x45,
> +	0xc925, 0xca,
> +	0xc926, 0x80,
> +	0xc927, 0x98,
> +	0x9098, 0x12,
> +	0x9099, 0x77,
> +	0x909a, 0xd6,
> +	0x909b, 0x02,
> +	0x909c, 0x45,
> +	0x909d, 0xcd,
> +	0xc928, 0x20,
> +	0xc929, 0xd5,
> +	0xc92a, 0x80,
> +	0xc92b, 0x9e,
> +	0x909e, 0x90,
> +	0x909f, 0x82,
> +	0x90a0, 0x18,
> +	0x90a1, 0xe0,
> +	0x90a2, 0xb4,
> +	0x90a3, 0x03,
> +	0x90a4, 0x0e,
> +	0x90a5, 0x90,
> +	0x90a6, 0x83,
> +	0x90a7, 0xbf,
> +	0x90a8, 0xe0,
> +	0x90a9, 0x60,
> +	0x90aa, 0x08,
> +	0x90ab, 0x90,
> +	0x90ac, 0x81,
> +	0x90ad, 0xfc,
> +	0x90ae, 0xe0,
> +	0x90af, 0xff,
> +	0x90b0, 0xc3,
> +	0x90b1, 0x13,
> +	0x90b2, 0xf0,
> +	0x90b3, 0x90,
> +	0x90b4, 0x81,
> +	0x90b5, 0xfc,
> +	0x90b6, 0xe0,
> +	0x90b7, 0xff,
> +	0x90b8, 0x02,
> +	0x90b9, 0x20,
> +	0x90ba, 0xda,
> +	0xc92c, 0x70,
> +	0xc92d, 0xbc,
> +	0xc92e, 0x80,
> +	0xc92f, 0xbb,
> +	0x90bb, 0x90,
> +	0x90bc, 0x82,
> +	0x90bd, 0x18,
> +	0x90be, 0xe0,
> +	0x90bf, 0xb4,
> +	0x90c0, 0x03,
> +	0x90c1, 0x06,
> +	0x90c2, 0x90,
> +	0x90c3, 0xc1,
> +	0x90c4, 0x06,
> +	0x90c5, 0x74,
> +	0x90c6, 0x05,
> +	0x90c7, 0xf0,
> +	0x90c8, 0x90,
> +	0x90c9, 0xd3,
> +	0x90ca, 0xa0,
> +	0x90cb, 0x02,
> +	0x90cc, 0x70,
> +	0x90cd, 0xbf,
> +	0xc930, 0x72,
> +	0xc931, 0x21,
> +	0xc932, 0x81,
> +	0xc933, 0x3b,
> +	0x913b, 0x7d,
> +	0x913c, 0x02,
> +	0x913d, 0x7f,
> +	0x913e, 0x7b,
> +	0x913f, 0x02,
> +	0x9140, 0x72,
> +	0x9141, 0x25,
> +	0xc934, 0x28,
> +	0xc935, 0xae,
> +	0xc936, 0x80,
> +	0xc937, 0xd2,
> +	0x90d2, 0xf0,
> +	0x90d3, 0x90,
> +	0x90d4, 0xd2,
> +	0x90d5, 0x0a,
> +	0x90d6, 0x02,
> +	0x90d7, 0x28,
> +	0x90d8, 0xb4,
> +	0xc938, 0x28,
> +	0xc939, 0xb1,
> +	0xc93a, 0x80,
> +	0xc93b, 0xd9,
> +	0x90d9, 0x90,
> +	0x90da, 0x83,
> +	0x90db, 0xba,
> +	0x90dc, 0xe0,
> +	0x90dd, 0xff,
> +	0x90de, 0x90,
> +	0x90df, 0xd2,
> +	0x90e0, 0x08,
> +	0x90e1, 0xe0,
> +	0x90e2, 0xe4,
> +	0x90e3, 0xef,
> +	0x90e4, 0xf0,
> +	0x90e5, 0xa3,
> +	0x90e6, 0xe0,
> +	0x90e7, 0x74,
> +	0x90e8, 0xff,
> +	0x90e9, 0xf0,
> +	0x90ea, 0x90,
> +	0x90eb, 0xd2,
> +	0x90ec, 0x0a,
> +	0x90ed, 0x02,
> +	0x90ee, 0x28,
> +	0x90ef, 0xb4,
> +	0xc93c, 0x29,
> +	0xc93d, 0x79,
> +	0xc93e, 0x80,
> +	0xc93f, 0xf0,
> +	0x90f0, 0xf0,
> +	0x90f1, 0x90,
> +	0x90f2, 0xd2,
> +	0x90f3, 0x0e,
> +	0x90f4, 0x02,
> +	0x90f5, 0x29,
> +	0x90f6, 0x7f,
> +	0xc940, 0x29,
> +	0xc941, 0x7c,
> +	0xc942, 0x80,
> +	0xc943, 0xf7,
> +	0x90f7, 0x90,
> +	0x90f8, 0x83,
> +	0x90f9, 0xba,
> +	0x90fa, 0xe0,
> +	0x90fb, 0xff,
> +	0x90fc, 0x90,
> +	0x90fd, 0xd2,
> +	0x90fe, 0x0c,
> +	0x90ff, 0xe0,
> +	0x9100, 0xe4,
> +	0x9101, 0xef,
> +	0x9102, 0xf0,
> +	0x9103, 0xa3,
> +	0x9104, 0xe0,
> +	0x9105, 0x74,
> +	0x9106, 0xff,
> +	0x9107, 0xf0,
> +	0x9108, 0x90,
> +	0x9109, 0xd2,
> +	0x910a, 0x0e,
> +	0x910b, 0x02,
> +	0x910c, 0x29,
> +	0x910d, 0x7f,
> +	0xc944, 0x2a,
> +	0xc945, 0x42,
> +	0xc946, 0x81,
> +	0xc947, 0x0e,
> +	0x910e, 0xf0,
> +	0x910f, 0x90,
> +	0x9110, 0xd2,
> +	0x9111, 0x12,
> +	0x9112, 0x02,
> +	0x9113, 0x2a,
> +	0x9114, 0x48,
> +	0xc948, 0x2a,
> +	0xc949, 0x45,
> +	0xc94a, 0x81,
> +	0xc94b, 0x15,
> +	0x9115, 0x90,
> +	0x9116, 0x83,
> +	0x9117, 0xba,
> +	0x9118, 0xe0,
> +	0x9119, 0xff,
> +	0x911a, 0x90,
> +	0x911b, 0xd2,
> +	0x911c, 0x10,
> +	0x911d, 0xe0,
> +	0x911e, 0xe4,
> +	0x911f, 0xef,
> +	0x9120, 0xf0,
> +	0x9121, 0xa3,
> +	0x9122, 0xe0,
> +	0x9123, 0x74,
> +	0x9124, 0xff,
> +	0x9125, 0xf0,
> +	0x9126, 0x90,
> +	0x9127, 0xd2,
> +	0x9128, 0x12,
> +	0x9129, 0x02,
> +	0x912a, 0x2a,
> +	0x912b, 0x48,
> +	0xc900, 0x01,
> +	0x0000, 0x00,
> +};
> +
> +static const u16 vs6624_p2[] = {
> +	0x806f, 0x01,
> +	0x058c, 0x01,
> +	0x0000, 0x00,
> +};
> +
> +static const u16 vs6624_run_setup[] = {
> +	0x1d18, 0x00,				/* Enableconstrainedwhitebalance */
> +	VS6624_PEAK_MIN_OUT_G_MSB, 0x3c,	/* Damper PeakGain Output MSB */
> +	VS6624_PEAK_MIN_OUT_G_LSB, 0x66,	/* Damper PeakGain Output LSB */
> +	VS6624_CM_LOW_THR_MSB, 0x65,		/* Damper Low MSB */
> +	VS6624_CM_LOW_THR_LSB, 0xd1,		/* Damper Low LSB */
> +	VS6624_CM_HIGH_THR_MSB, 0x66,		/* Damper High MSB */
> +	VS6624_CM_HIGH_THR_LSB, 0x62,		/* Damper High LSB */
> +	VS6624_CM_MIN_OUT_MSB, 0x00,		/* Damper Min output MSB */
> +	VS6624_CM_MIN_OUT_LSB, 0x00,		/* Damper Min output LSB */
> +	VS6624_NORA_DISABLE, 0x00,		/* Nora fDisable */
> +	VS6624_NORA_USAGE, 0x04,		/* Nora usage */
> +	VS6624_NORA_LOW_THR_MSB, 0x63,		/* Damper Low MSB Changed 0x63 to 0x65 */
> +	VS6624_NORA_LOW_THR_LSB, 0xd1,		/* Damper Low LSB */
> +	VS6624_NORA_HIGH_THR_MSB, 0x68,		/* Damper High MSB */
> +	VS6624_NORA_HIGH_THR_LSB, 0xdd,		/* Damper High LSB */
> +	VS6624_NORA_MIN_OUT_MSB, 0x3a,		/* Damper Min output MSB */
> +	VS6624_NORA_MIN_OUT_LSB, 0x00,		/* Damper Min output LSB */
> +	VS6624_F2B_DISABLE, 0x00,		/* Disable */
> +	0x1d8a, 0x30,				/* MAXWeightHigh */
> +	0x1d91, 0x62,				/* fpDamperLowThresholdHigh MSB */
> +	0x1d92, 0x4a,				/* fpDamperLowThresholdHigh LSB */
> +	0x1d95, 0x65,				/* fpDamperHighThresholdHigh MSB */
> +	0x1d96, 0x0e,				/* fpDamperHighThresholdHigh LSB */
> +	0x1da1, 0x3a,				/* fpMinimumDamperOutputLow MSB */
> +	0x1da2, 0xb8,				/* fpMinimumDamperOutputLow LSB */
> +	0x1e08, 0x06,				/* MAXWeightLow */
> +	0x1e0a, 0x0a,				/* MAXWeightHigh */
> +	0x1601, 0x3a,				/* Red A MSB */
> +	0x1602, 0x14,				/* Red A LSB */
> +	0x1605, 0x3b,				/* Blue A MSB */
> +	0x1606, 0x85,				/* BLue A LSB */
> +	0x1609, 0x3b,				/* RED B MSB */
> +	0x160a, 0x85,				/* RED B LSB */
> +	0x160d, 0x3a,				/* Blue B MSB */
> +	0x160e, 0x14,				/* Blue B LSB */
> +	0x1611, 0x30,				/* Max Distance from Locus MSB */
> +	0x1612, 0x8f,				/* Max Distance from Locus MSB */
> +	0x1614, 0x01,				/* Enable constrainer */
> +	0x0000, 0x00,
> +};
> +
> +static const u16 vs6624_default[] = {
> +	VS6624_CONTRAST0, 0x84,
> +	VS6624_SATURATION0, 0x75,
> +	VS6624_GAMMA0, 0x11,
> +	VS6624_CONTRAST1, 0x84,
> +	VS6624_SATURATION1, 0x75,
> +	VS6624_GAMMA1, 0x11,
> +	VS6624_MAN_RG, 0x80,
> +	VS6624_MAN_GG, 0x80,
> +	VS6624_MAN_BG, 0x80,
> +	VS6624_WB_MODE, 0x1,
> +	VS6624_EXPO_COMPENSATION, 0xfe,
> +	VS6624_EXPO_METER, 0x0,
> +	VS6624_LIGHT_FREQ, 0x64,
> +	VS6624_PEAK_GAIN, 0xe,
> +	VS6624_PEAK_LOW_THR, 0x28,
> +	VS6624_HMIRROR0, 0x0,
> +	VS6624_VFLIP0, 0x0,
> +	VS6624_ZOOM_HSTEP0_MSB, 0x0,
> +	VS6624_ZOOM_HSTEP0_LSB, 0x1,
> +	VS6624_ZOOM_VSTEP0_MSB, 0x0,
> +	VS6624_ZOOM_VSTEP0_LSB, 0x1,
> +	VS6624_PAN_HSTEP0_MSB, 0x0,
> +	VS6624_PAN_HSTEP0_LSB, 0xf,
> +	VS6624_PAN_VSTEP0_MSB, 0x0,
> +	VS6624_PAN_VSTEP0_LSB, 0xf,
> +	VS6624_SENSOR_MODE, 0x1,
> +	VS6624_SYNC_CODE_SETUP, 0x21,
> +	VS6624_DISABLE_FR_DAMPER, 0x0,
> +	VS6624_FR_DEN, 0x1,
> +	VS6624_FR_NUM_LSB, 0xf,
> +	VS6624_INIT_PIPE_SETUP, 0x0,
> +	VS6624_IMG_FMT0, 0x0,
> +	VS6624_YUV_SETUP, 0x1,
> +	VS6624_IMAGE_SIZE0, 0x2,
> +	0x0000, 0x00,
> +};
> +
> +static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct vs6624, sd);
> +}
> +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
> +{
> +	return &container_of(ctrl->handler, struct vs6624, hdl)->sd;
> +}
> +
> +static int vs6624_read(struct v4l2_subdev *sd, u16 index)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	u8 buf[2];
> +
> +	buf[0] = index >> 8;
> +	buf[1] = index;
> +	i2c_master_send(client, buf, 2);
> +	i2c_master_recv(client, buf, 1);
> +
> +	return buf[0];
> +}
> +
> +static int vs6624_write(struct v4l2_subdev *sd, u16 index,
> +				u8 value)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	u8 buf[3];
> +
> +	buf[0] = index >> 8;
> +	buf[1] = index;
> +	buf[2] = value;
> +
> +	return i2c_master_send(client, buf, 3);
> +}
> +
> +static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs)
> +{
> +	u16 reg;
> +	u8 data;
> +
> +	while (*regs != 0x00) {
> +		reg = *regs++;
> +		data = *regs++;
> +
> +		vs6624_write(sd, reg, data);
> +	}
> +	return 0;
> +}
> +
> +static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = to_sd(ctrl);
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_CONTRAST:
> +		vs6624_write(sd, VS6624_CONTRAST0, ctrl->val);
> +		break;
> +	case V4L2_CID_SATURATION:
> +		vs6624_write(sd, VS6624_SATURATION0, ctrl->val);
> +		break;
> +	case V4L2_CID_HFLIP:
> +		vs6624_write(sd, VS6624_HMIRROR0, ctrl->val);
> +		break;
> +	case V4L2_CID_VFLIP:
> +		vs6624_write(sd, VS6624_VFLIP0, ctrl->val);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
> +				enum v4l2_mbus_pixelcode *code)
> +{
> +	if (index >= ARRAY_SIZE(vs6624_formats))
> +		return -EINVAL;
> +
> +	*code = vs6624_formats[index].mbus_code;
> +	return 0;
> +}
> +
> +static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd,
> +				struct v4l2_mbus_framefmt *fmt)
> +{
> +	int index;
> +
> +	for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++)
> +		if (vs6624_formats[index].mbus_code == fmt->code)
> +			break;
> +	if (index >= ARRAY_SIZE(vs6624_formats)) {
> +		/* default to first format */
> +		index = 0;
> +		fmt->code = vs6624_formats[0].mbus_code;
> +	}
> +
> +	/* sensor mode is VGA */
> +	if (fmt->width > VGA_WIDTH)
> +		fmt->width = VGA_WIDTH;
> +	if (fmt->height > VGA_HEIGHT)
> +		fmt->height = VGA_HEIGHT;
> +	fmt->width = fmt->width & (~3);
> +	fmt->height = fmt->height & (~3);
> +	fmt->field = V4L2_FIELD_NONE;
> +	fmt->colorspace = vs6624_formats[index].colorspace;
> +	return 0;
> +}
> +
> +static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd,
> +				struct v4l2_mbus_framefmt *fmt)
> +{
> +	struct vs6624 *sensor = to_vs6624(sd);
> +	int ret;
> +
> +	ret = vs6624_try_mbus_fmt(sd, fmt);
> +	if (ret)
> +		return ret;
> +
> +	/* set image format */
> +	switch (fmt->code) {
> +	case V4L2_MBUS_FMT_UYVY8_2X8:
> +		vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
> +		vs6624_write(sd, VS6624_YUV_SETUP, 0x1);
> +		break;
> +	case V4L2_MBUS_FMT_YUYV8_2X8:
> +		vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
> +		vs6624_write(sd, VS6624_YUV_SETUP, 0x3);
> +		break;
> +	case V4L2_MBUS_FMT_RGB565_2X8_LE:
> +		vs6624_write(sd, VS6624_IMG_FMT0, 0x4);
> +		vs6624_write(sd, VS6624_RGB_SETUP, 0x0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* set image size */
> +	if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT))
> +		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2);
> +	else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT))
> +		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4);
> +	else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT))
> +		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6);
> +	else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT))
> +		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3);
> +	else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT))
> +		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5);
> +	else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT))
> +		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7);
> +	else {
> +		vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8);
> +		vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8);
> +		vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF);
> +		vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8);
> +		vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF);
> +		vs6624_write(sd, VS6624_CROP_CTRL0, 0x1);
> +	}
> +
> +	sensor->fmt = *fmt;
> +
> +	return 0;
> +}
> +
> +static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd,
> +				struct v4l2_mbus_framefmt *fmt)
> +{
> +	struct vs6624 *sensor = to_vs6624(sd);
> +
> +	*fmt = sensor->fmt;
> +	return 0;
> +}
> +
> +static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
> +{
> +	struct vs6624 *sensor = to_vs6624(sd);
> +	struct v4l2_captureparm *cp = &parms->parm.capture;
> +
> +	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	memset(cp, 0, sizeof(*cp));
> +	cp->capability = V4L2_CAP_TIMEPERFRAME;
> +	cp->timeperframe.numerator = sensor->frame_rate.denominator;
> +	cp->timeperframe.denominator = sensor->frame_rate.numerator;
> +	return 0;
> +}
> +
> +static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
> +{
> +	struct vs6624 *sensor = to_vs6624(sd);
> +	struct v4l2_captureparm *cp = &parms->parm.capture;
> +	struct v4l2_fract *tpf = &cp->timeperframe;
> +
> +	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +	if (cp->extendedmode != 0)
> +		return -EINVAL;
> +
> +	if (tpf->numerator == 0 || tpf->denominator == 0
> +		|| (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) {
> +		/* reset to max frame rate */
> +		tpf->numerator = 1;
> +		tpf->denominator = MAX_FRAME_RATE;
> +	}
> +	sensor->frame_rate.numerator = tpf->denominator;
> +	sensor->frame_rate.denominator = tpf->numerator;
> +	vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
> +	vs6624_write(sd, VS6624_FR_NUM_MSB,
> +			sensor->frame_rate.numerator >> 8);
> +	vs6624_write(sd, VS6624_FR_NUM_LSB,
> +			sensor->frame_rate.numerator & 0xFF);
> +	vs6624_write(sd, VS6624_FR_DEN,
> +			sensor->frame_rate.denominator & 0xFF);
> +	return 0;
> +}
> +
> +static int vs6624_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	if (enable)
> +		vs6624_write(sd, VS6624_USER_CMD, 0x2);
> +	else
> +		vs6624_write(sd, VS6624_USER_CMD, 0x4);
> +	udelay(100);
> +	return 0;
> +}
> +
> +static int vs6624_g_chip_ident(struct v4l2_subdev *sd,
> +		struct v4l2_dbg_chip_ident *chip)
> +{
> +	int rev;
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8)
> +		| vs6624_read(sd, VS6624_FW_VSN_MINOR);
> +
> +	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev);
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	if (!v4l2_chip_match_i2c_client(client, &reg->match))
> +		return -EINVAL;
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +	reg->val = vs6624_read(sd, reg->reg & 0xffff);
> +	reg->size = 1;
> +	return 0;
> +}
> +
> +static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	if (!v4l2_chip_match_i2c_client(client, &reg->match))
> +		return -EINVAL;
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EPERM;
> +	vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff);
> +	return 0;
> +}
> +#endif
> +
> +static const struct v4l2_ctrl_ops vs6624_ctrl_ops = {
> +	.s_ctrl = vs6624_s_ctrl,
> +};
> +
> +static const struct v4l2_subdev_core_ops vs6624_core_ops = {
> +	.g_chip_ident = vs6624_g_chip_ident,
> +	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
> +	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
> +	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
> +	.g_ctrl = v4l2_subdev_g_ctrl,
> +	.s_ctrl = v4l2_subdev_s_ctrl,
> +	.queryctrl = v4l2_subdev_queryctrl,
> +	.querymenu = v4l2_subdev_querymenu,

Same comment as with the adv7183.c: don't set these control ops here.
They are not needed unless this driver has to be used with a bridge driver
that doesn't yet support the control framework.

> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.g_register = vs6624_g_register,
> +	.s_register = vs6624_s_register,
> +#endif
> +};
> +
> +static const struct v4l2_subdev_video_ops vs6624_video_ops = {
> +	.enum_mbus_fmt = vs6624_enum_mbus_fmt,
> +	.try_mbus_fmt = vs6624_try_mbus_fmt,
> +	.s_mbus_fmt = vs6624_s_mbus_fmt,
> +	.g_mbus_fmt = vs6624_g_mbus_fmt,
> +	.s_parm = vs6624_s_parm,
> +	.g_parm = vs6624_g_parm,
> +	.s_stream = vs6624_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops vs6624_ops = {
> +	.core = &vs6624_core_ops,
> +	.video = &vs6624_video_ops,
> +};
> +
> +static int __devinit vs6624_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct vs6624 *sensor;
> +	struct v4l2_subdev *sd;
> +	struct v4l2_ctrl_handler *hdl;
> +	const unsigned *ce;
> +	int ret;
> +
> +	/* Check if the adapter supports the needed features */
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
> +		return -EIO;
> +
> +	ce = client->dev.platform_data;
> +	if (ce == NULL)
> +		return -EINVAL;
> +
> +	ret = gpio_request(*ce, "VS6624 Chip Enable");
> +	if (ret) {
> +		v4l_err(client, "failed to request GPIO %d\n", *ce);
> +		return ret;
> +	}
> +	gpio_direction_output(*ce, 1);
> +	/* wait 100ms before any further i2c writes are performed */
> +	mdelay(100);
> +
> +	sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
> +	if (sensor == NULL) {
> +		gpio_free(*ce);
> +		return -ENOMEM;
> +	}
> +
> +	sd = &sensor->sd;
> +	v4l2_i2c_subdev_init(sd, client, &vs6624_ops);
> +
> +	vs6624_writeregs(sd, vs6624_p1);
> +	vs6624_write(sd, VS6624_MICRO_EN, 0x2);
> +	vs6624_write(sd, VS6624_DIO_EN, 0x1);
> +	mdelay(10);
> +	vs6624_writeregs(sd, vs6624_p2);
> +
> +	vs6624_writeregs(sd, vs6624_default);
> +	vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF);
> +	vs6624_writeregs(sd, vs6624_run_setup);
> +
> +	/* set frame rate */
> +	sensor->frame_rate.numerator = MAX_FRAME_RATE;
> +	sensor->frame_rate.denominator = 1;
> +	vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
> +	vs6624_write(sd, VS6624_FR_NUM_MSB,
> +			sensor->frame_rate.numerator >> 8);
> +	vs6624_write(sd, VS6624_FR_NUM_LSB,
> +			sensor->frame_rate.numerator & 0xFF);
> +	vs6624_write(sd, VS6624_FR_DEN,
> +			sensor->frame_rate.denominator & 0xFF);
> +
> +	sensor->fmt = vs6624_default_fmt;
> +	sensor->ce_pin = *ce;
> +
> +	v4l_info(client, "chip found @ 0x%02x (%s)\n",
> +			client->addr << 1, client->adapter->name);
> +
> +	hdl = &sensor->hdl;
> +	v4l2_ctrl_handler_init(hdl, 4);
> +	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
> +			V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87);
> +	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
> +			V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78);
> +	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
> +			V4L2_CID_HFLIP, 0, 1, 1, 0);
> +	v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
> +			V4L2_CID_VFLIP, 0, 1, 1, 0);
> +	/* hook the control handler into the driver */
> +	sd->ctrl_handler = hdl;
> +	if (hdl->error) {
> +		int err = hdl->error;
> +
> +		v4l2_ctrl_handler_free(hdl);
> +		kfree(sensor);
> +		gpio_free(*ce);
> +		return err;
> +	}
> +
> +	/* initialize the hardware to the default control values */
> +	v4l2_ctrl_handler_setup(hdl);

Check and handle the error code.

> +	return 0;
> +}
> +
> +static int __devexit vs6624_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct vs6624 *sensor = to_vs6624(sd);
> +
> +	v4l2_device_unregister_subdev(sd);
> +	v4l2_ctrl_handler_free(sd->ctrl_handler);
> +	gpio_free(sensor->ce_pin);
> +	kfree(sensor);
> +	return 0;
> +}
> +
> +static const struct i2c_device_id vs6624_id[] = {
> +	{"vs6624", 0},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, vs6624_id);
> +
> +static struct i2c_driver vs6624_driver = {
> +	.driver = {
> +		.owner  = THIS_MODULE,
> +		.name   = "vs6624",
> +	},
> +	.probe          = vs6624_probe,
> +	.remove         = __devexit_p(vs6624_remove),
> +	.id_table       = vs6624_id,
> +};
> +
> +static __init int vs6624_init(void)
> +{
> +	return i2c_add_driver(&vs6624_driver);
> +}
> +
> +static __exit void vs6624_exit(void)
> +{
> +	i2c_del_driver(&vs6624_driver);
> +}
> +
> +module_init(vs6624_init);
> +module_exit(vs6624_exit);
> +
> +MODULE_DESCRIPTION("VS6624 sensor driver");
> +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h
> new file mode 100644
> index 0000000..6ba2ee2
> --- /dev/null
> +++ b/drivers/media/video/vs6624_regs.h
> @@ -0,0 +1,337 @@
> +/*
> + * vs6624 - ST VS6624 CMOS image sensor registers
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#ifndef _VS6624_REGS_H_
> +#define _VS6624_REGS_H_
> +
> +/* low level control registers */
> +#define VS6624_MICRO_EN               0xC003 /* power enable for all MCU clock */
> +#define VS6624_DIO_EN                 0xC044 /* enable digital I/O */
> +/* device parameters */
> +#define VS6624_DEV_ID_MSB             0x0001 /* device id MSB */
> +#define VS6624_DEV_ID_LSB             0x0002 /* device id LSB */
> +#define VS6624_FW_VSN_MAJOR           0x0004 /* firmware version major */
> +#define VS6624_FW_VSN_MINOR           0x0006 /* firmware version minor */
> +#define VS6624_PATCH_VSN_MAJOR        0x0008 /* patch version major */
> +#define VS6624_PATCH_VSN_MINOR        0x000A /* patch version minor */
> +/* host interface manager control */
> +#define VS6624_USER_CMD               0x0180 /* user level control of operating states */
> +/* host interface manager status */
> +#define VS6624_STATE                  0x0202 /* current state of the mode manager */
> +/* run mode control */
> +#define VS6624_METER_ON               0x0280 /* if false AE and AWB are disabled */
> +/* mode setup */
> +#define VS6624_ACTIVE_PIPE_SETUP      0x0302 /* select the active bank for non view live mode */
> +#define VS6624_SENSOR_MODE            0x0308 /* select the different sensor mode */
> +/* pipe setup bank0 */
> +#define VS6624_IMAGE_SIZE0            0x0380 /* required output dimension */
> +#define VS6624_MAN_HSIZE0_MSB         0x0383 /* input required manual H size MSB */
> +#define VS6624_MAN_HSIZE0_LSB         0x0384 /* input required manual H size LSB */
> +#define VS6624_MAN_VSIZE0_MSB         0x0387 /* input required manual V size MSB */
> +#define VS6624_MAN_VSIZE0_LSB         0x0388 /* input required manual V size LSB */
> +#define VS6624_ZOOM_HSTEP0_MSB        0x038B /* set the zoom H step MSB */
> +#define VS6624_ZOOM_HSTEP0_LSB        0x038C /* set the zoom H step LSB */
> +#define VS6624_ZOOM_VSTEP0_MSB        0x038F /* set the zoom V step MSB */
> +#define VS6624_ZOOM_VSTEP0_LSB        0x0390 /* set the zoom V step LSB */
> +#define VS6624_ZOOM_CTRL0             0x0392 /* control zoon in, out and stop */
> +#define VS6624_PAN_HSTEP0_MSB         0x0395 /* set the pan H step MSB */
> +#define VS6624_PAN_HSTEP0_LSB         0x0396 /* set the pan H step LSB */
> +#define VS6624_PAN_VSTEP0_MSB         0x0399 /* set the pan V step MSB */
> +#define VS6624_PAN_VSTEP0_LSB         0x039A /* set the pan V step LSB */
> +#define VS6624_PAN_CTRL0              0x039C /* control pan operation */
> +#define VS6624_CROP_CTRL0             0x039E /* select cropping mode */
> +#define VS6624_CROP_HSTART0_MSB       0x03A1 /* set the cropping H start address MSB */
> +#define VS6624_CROP_HSTART0_LSB       0x03A2 /* set the cropping H start address LSB */
> +#define VS6624_CROP_HSIZE0_MSB        0x03A5 /* set the cropping H size MSB */
> +#define VS6624_CROP_HSIZE0_LSB        0x03A6 /* set the cropping H size LSB */
> +#define VS6624_CROP_VSTART0_MSB       0x03A9 /* set the cropping V start address MSB */
> +#define VS6624_CROP_VSTART0_LSB       0x03AA /* set the cropping V start address LSB */
> +#define VS6624_CROP_VSIZE0_MSB        0x03AD /* set the cropping V size MSB */
> +#define VS6624_CROP_VSIZE0_LSB        0x03AE /* set the cropping V size LSB */
> +#define VS6624_IMG_FMT0               0x03B0 /* select required output image format */
> +#define VS6624_BAYER_OUT_ALIGN0       0x03B2 /* set bayer output alignment */
> +#define VS6624_CONTRAST0              0x03B4 /* contrast control for output */
> +#define VS6624_SATURATION0            0x03B6 /* saturation control for output */
> +#define VS6624_GAMMA0                 0x03B8 /* gamma settings */
> +#define VS6624_HMIRROR0               0x03BA /* horizontal image orientation flip */
> +#define VS6624_VFLIP0                 0x03BC /* vertical image orientation flip */
> +#define VS6624_CHANNEL_ID0            0x03BE /* logical DMA channel number */
> +/* pipe setup bank1 */
> +#define VS6624_IMAGE_SIZE1            0x0400 /* required output dimension */
> +#define VS6624_MAN_HSIZE1_MSB         0x0403 /* input required manual H size MSB */
> +#define VS6624_MAN_HSIZE1_LSB         0x0404 /* input required manual H size LSB */
> +#define VS6624_MAN_VSIZE1_MSB         0x0407 /* input required manual V size MSB */
> +#define VS6624_MAN_VSIZE1_LSB         0x0408 /* input required manual V size LSB */
> +#define VS6624_ZOOM_HSTEP1_MSB        0x040B /* set the zoom H step MSB */
> +#define VS6624_ZOOM_HSTEP1_LSB        0x040C /* set the zoom H step LSB */
> +#define VS6624_ZOOM_VSTEP1_MSB        0x040F /* set the zoom V step MSB */
> +#define VS6624_ZOOM_VSTEP1_LSB        0x0410 /* set the zoom V step LSB */
> +#define VS6624_ZOOM_CTRL1             0x0412 /* control zoon in, out and stop */
> +#define VS6624_PAN_HSTEP1_MSB         0x0415 /* set the pan H step MSB */
> +#define VS6624_PAN_HSTEP1_LSB         0x0416 /* set the pan H step LSB */
> +#define VS6624_PAN_VSTEP1_MSB         0x0419 /* set the pan V step MSB */
> +#define VS6624_PAN_VSTEP1_LSB         0x041A /* set the pan V step LSB */
> +#define VS6624_PAN_CTRL1              0x041C /* control pan operation */
> +#define VS6624_CROP_CTRL1             0x041E /* select cropping mode */
> +#define VS6624_CROP_HSTART1_MSB       0x0421 /* set the cropping H start address MSB */
> +#define VS6624_CROP_HSTART1_LSB       0x0422 /* set the cropping H start address LSB */
> +#define VS6624_CROP_HSIZE1_MSB        0x0425 /* set the cropping H size MSB */
> +#define VS6624_CROP_HSIZE1_LSB        0x0426 /* set the cropping H size LSB */
> +#define VS6624_CROP_VSTART1_MSB       0x0429 /* set the cropping V start address MSB */
> +#define VS6624_CROP_VSTART1_LSB       0x042A /* set the cropping V start address LSB */
> +#define VS6624_CROP_VSIZE1_MSB        0x042D /* set the cropping V size MSB */
> +#define VS6624_CROP_VSIZE1_LSB        0x042E /* set the cropping V size LSB */
> +#define VS6624_IMG_FMT1               0x0430 /* select required output image format */
> +#define VS6624_BAYER_OUT_ALIGN1       0x0432 /* set bayer output alignment */
> +#define VS6624_CONTRAST1              0x0434 /* contrast control for output */
> +#define VS6624_SATURATION1            0x0436 /* saturation control for output */
> +#define VS6624_GAMMA1                 0x0438 /* gamma settings */
> +#define VS6624_HMIRROR1               0x043A /* horizontal image orientation flip */
> +#define VS6624_VFLIP1                 0x043C /* vertical image orientation flip */
> +#define VS6624_CHANNEL_ID1            0x043E /* logical DMA channel number */
> +/* view live control */
> +#define VS6624_VIEW_LIVE_EN           0x0480 /* enable view live mode */
> +#define VS6624_INIT_PIPE_SETUP        0x0482 /* select initial pipe setup bank */
> +/* view live status */
> +#define VS6624_CUR_PIPE_SETUP         0x0500 /* indicates most recently applied setup bank */
> +/* power management */
> +#define VS6624_TIME_TO_POWER_DOWN     0x0580 /* automatically transition time to stop mode */
> +/* video timing parameter host inputs */
> +#define VS6624_EXT_CLK_FREQ_NUM_MSB   0x0605 /* external clock frequency numerator MSB */
> +#define VS6624_EXT_CLK_FREQ_NUM_LSB   0x0606 /* external clock frequency numerator LSB */
> +#define VS6624_EXT_CLK_FREQ_DEN       0x0608 /* external clock frequency denominator */
> +/* video timing control */
> +#define VS6624_SYS_CLK_MODE           0x0880 /* decides system clock frequency */
> +/* frame dimension parameter host inputs */
> +#define VS6624_LIGHT_FREQ             0x0C80 /* AC frequency used for flicker free time */
> +#define VS6624_FLICKER_COMPAT         0x0C82 /* flicker compatible frame length */
> +/* static frame rate control */
> +#define VS6624_FR_NUM_MSB             0x0D81 /* desired frame rate numerator MSB */
> +#define VS6624_FR_NUM_LSB             0x0D82 /* desired frame rate numerator LSB */
> +#define VS6624_FR_DEN                 0x0D84 /* desired frame rate denominator */
> +/* automatic frame rate control */
> +#define VS6624_DISABLE_FR_DAMPER      0x0E80 /* defines frame rate mode */
> +#define VS6624_MIN_DAMPER_OUT_MSB     0x0E8C /* minimum frame rate MSB */
> +#define VS6624_MIN_DAMPER_OUT_LSB     0x0E8A /* minimum frame rate LSB */
> +/* exposure controls */
> +#define VS6624_EXPO_MODE              0x1180 /* exposure mode */
> +#define VS6624_EXPO_METER             0x1182 /* weights to be associated with the zones */
> +#define VS6624_EXPO_TIME_NUM          0x1184 /* exposure time numerator */
> +#define VS6624_EXPO_TIME_DEN          0x1186 /* exposure time denominator */
> +#define VS6624_EXPO_TIME_MSB          0x1189 /* exposure time for the Manual Mode MSB */
> +#define VS6624_EXPO_TIME_LSB          0x118A /* exposure time for the Manual Mode LSB */
> +#define VS6624_EXPO_COMPENSATION      0x1190 /* exposure compensation */
> +#define VS6624_DIRECT_COARSE_MSB      0x1195 /* coarse integration lines for Direct Mode MSB */
> +#define VS6624_DIRECT_COARSE_LSB      0x1196 /* coarse integration lines for Direct Mode LSB */
> +#define VS6624_DIRECT_FINE_MSB        0x1199 /* fine integration pixels for Direct Mode MSB */
> +#define VS6624_DIRECT_FINE_LSB        0x119A /* fine integration pixels for Direct Mode LSB */
> +#define VS6624_DIRECT_ANAL_GAIN_MSB   0x119D /* analog gain for Direct Mode MSB */
> +#define VS6624_DIRECT_ANAL_GAIN_LSB   0x119E /* analog gain for Direct Mode LSB */
> +#define VS6624_DIRECT_DIGI_GAIN_MSB   0x11A1 /* digital gain for Direct Mode MSB */
> +#define VS6624_DIRECT_DIGI_GAIN_LSB   0x11A2 /* digital gain for Direct Mode LSB */
> +#define VS6624_FLASH_COARSE_MSB       0x11A5 /* coarse integration lines for Flash Gun Mode MSB */
> +#define VS6624_FLASH_COARSE_LSB       0x11A6 /* coarse integration lines for Flash Gun Mode LSB */
> +#define VS6624_FLASH_FINE_MSB         0x11A9 /* fine integration pixels for Flash Gun Mode MSB */
> +#define VS6624_FLASH_FINE_LSB         0x11AA /* fine integration pixels for Flash Gun Mode LSB */
> +#define VS6624_FLASH_ANAL_GAIN_MSB    0x11AD /* analog gain for Flash Gun Mode MSB */
> +#define VS6624_FLASH_ANAL_GAIN_LSB    0x11AE /* analog gain for Flash Gun Mode LSB */
> +#define VS6624_FLASH_DIGI_GAIN_MSB    0x11B1 /* digital gain for Flash Gun Mode MSB */
> +#define VS6624_FLASH_DIGI_GAIN_LSB    0x11B2 /* digital gain for Flash Gun Mode LSB */
> +#define VS6624_FREEZE_AE              0x11B4 /* freeze auto exposure */
> +#define VS6624_MAX_INT_TIME_MSB       0x11B7 /* user maximum integration time MSB */
> +#define VS6624_MAX_INT_TIME_LSB       0x11B8 /* user maximum integration time LSB */
> +#define VS6624_FLASH_AG_THR_MSB       0x11BB /* recommend flash gun analog gain threshold MSB */
> +#define VS6624_FLASH_AG_THR_LSB       0x11BC /* recommend flash gun analog gain threshold LSB */
> +#define VS6624_ANTI_FLICKER_MODE      0x11C0 /* anti flicker mode */
> +/* white balance control */
> +#define VS6624_WB_MODE                0x1480 /* set white balance mode */
> +#define VS6624_MAN_RG                 0x1482 /* user setting for red channel gain */
> +#define VS6624_MAN_GG                 0x1484 /* user setting for green channel gain */
> +#define VS6624_MAN_BG                 0x1486 /* user setting for blue channel gain */
> +#define VS6624_FLASH_RG_MSB           0x148B /* red gain for Flash Gun MSB */
> +#define VS6624_FLASH_RG_LSB           0x148C /* red gain for Flash Gun LSB */
> +#define VS6624_FLASH_GG_MSB           0x148F /* green gain for Flash Gun MSB */
> +#define VS6624_FLASH_GG_LSB           0x1490 /* green gain for Flash Gun LSB */
> +#define VS6624_FLASH_BG_MSB           0x1493 /* blue gain for Flash Gun MSB */
> +#define VS6624_FLASH_BG_LSB           0x1494 /* blue gain for Flash Gun LSB */
> +/* sensor setup */
> +#define VS6624_BC_OFFSET              0x1990 /* Black Correction Offset */
> +/* image stability */
> +#define VS6624_STABLE_WB              0x1900 /* white balance stable */
> +#define VS6624_STABLE_EXPO            0x1902 /* exposure stable */
> +#define VS6624_STABLE                 0x1906 /* system stable */
> +/* flash control */
> +#define VS6624_FLASH_MODE             0x1A80 /* flash mode */
> +#define VS6624_FLASH_OFF_LINE_MSB     0x1A83 /* off line at flash pulse mode MSB */
> +#define VS6624_FLASH_OFF_LINE_LSB     0x1A84 /* off line at flash pulse mode LSB */
> +/* flash status */
> +#define VS6624_FLASH_RECOM            0x1B00 /* flash gun is recommended */
> +#define VS6624_FLASH_GRAB_COMPLETE    0x1B02 /* flash gun image has been grabbed */
> +/* scythe filter controls */
> +#define VS6624_SCYTHE_FILTER          0x1D80 /* disable scythe defect correction */
> +/* jack filter controls */
> +#define VS6624_JACK_FILTER            0x1E00 /* disable jack defect correction */
> +/* demosaic control */
> +#define VS6624_ANTI_ALIAS_FILTER      0x1E80 /* anti alias filter suppress */
> +/* color matrix dampers */
> +#define VS6624_CM_DISABLE             0x1F00 /* disable color matrix damper */
> +#define VS6624_CM_LOW_THR_MSB         0x1F03 /* low threshold for exposure MSB */
> +#define VS6624_CM_LOW_THR_LSB         0x1F04 /* low threshold for exposure LSB */
> +#define VS6624_CM_HIGH_THR_MSB        0x1F07 /* high threshold for exposure MSB */
> +#define VS6624_CM_HIGH_THR_LSB        0x1F08 /* high threshold for exposure LSB */
> +#define VS6624_CM_MIN_OUT_MSB         0x1F0B /* minimum possible damper output MSB */
> +#define VS6624_CM_MIN_OUT_LSB         0x1F0C /* minimum possible damper output LSB */
> +/* peaking control */
> +#define VS6624_PEAK_GAIN              0x2000 /* controls peaking gain */
> +#define VS6624_PEAK_G_DISABLE         0x2002 /* disable peak gain damping */
> +#define VS6624_PEAK_LOW_THR_G_MSB     0x2005 /* low threshold for exposure for gain MSB */
> +#define VS6624_PEAK_LOW_THR_G_LSB     0x2006 /* low threshold for exposure for gain LSB */
> +#define VS6624_PEAK_HIGH_THR_G_MSB    0x2009 /* high threshold for exposure for gain MSB */
> +#define VS6624_PEAK_HIGH_THR_G_LSB    0x200A /* high threshold for exposure for gain LSB */
> +#define VS6624_PEAK_MIN_OUT_G_MSB     0x200D /* minimum damper output for gain MSB */
> +#define VS6624_PEAK_MIN_OUT_G_LSB     0x200E /* minimum damper output for gain LSB */
> +#define VS6624_PEAK_LOW_THR           0x2010 /* adjust degree of coring */
> +#define VS6624_PEAK_C_DISABLE         0x2012 /* disable coring damping */
> +#define VS6624_PEAK_HIGH_THR          0x2014 /* adjust maximum gain */
> +#define VS6624_PEAK_LOW_THR_C_MSB     0x2017 /* low threshold for exposure for coring MSB */
> +#define VS6624_PEAK_LOW_THR_C_LSB     0x2018 /* low threshold for exposure for coring LSB */
> +#define VS6624_PEAK_HIGH_THR_C_MSB    0x201B /* high threshold for exposure for coring MSB */
> +#define VS6624_PEAK_HIGH_THR_C_LSB    0x201C /* high threshold for exposure for coring LSB */
> +#define VS6624_PEAK_MIN_OUT_C_MSB     0x201F /* minimum damper output for coring MSB */
> +#define VS6624_PEAK_MIN_OUT_C_LSB     0x2020 /* minimum damper output for coring LSB */
> +/* pipe 0 RGB to YUV matrix manual control */
> +#define VS6624_RYM0_MAN_CTRL          0x2180 /* enable manual RGB to YUV matrix */
> +#define VS6624_RYM0_W00_MSB           0x2183 /* row 0 column 0 of YUV matrix MSB */
> +#define VS6624_RYM0_W00_LSB           0x2184 /* row 0 column 0 of YUV matrix LSB */
> +#define VS6624_RYM0_W01_MSB           0x2187 /* row 0 column 1 of YUV matrix MSB */
> +#define VS6624_RYM0_W01_LSB           0x2188 /* row 0 column 1 of YUV matrix LSB */
> +#define VS6624_RYM0_W02_MSB           0x218C /* row 0 column 2 of YUV matrix MSB */
> +#define VS6624_RYM0_W02_LSB           0x218D /* row 0 column 2 of YUV matrix LSB */
> +#define VS6624_RYM0_W10_MSB           0x2190 /* row 1 column 0 of YUV matrix MSB */
> +#define VS6624_RYM0_W10_LSB           0x218F /* row 1 column 0 of YUV matrix LSB */
> +#define VS6624_RYM0_W11_MSB           0x2193 /* row 1 column 1 of YUV matrix MSB */
> +#define VS6624_RYM0_W11_LSB           0x2194 /* row 1 column 1 of YUV matrix LSB */
> +#define VS6624_RYM0_W12_MSB           0x2197 /* row 1 column 2 of YUV matrix MSB */
> +#define VS6624_RYM0_W12_LSB           0x2198 /* row 1 column 2 of YUV matrix LSB */
> +#define VS6624_RYM0_W20_MSB           0x219B /* row 2 column 0 of YUV matrix MSB */
> +#define VS6624_RYM0_W20_LSB           0x219C /* row 2 column 0 of YUV matrix LSB */
> +#define VS6624_RYM0_W21_MSB           0x21A0 /* row 2 column 1 of YUV matrix MSB */
> +#define VS6624_RYM0_W21_LSB           0x219F /* row 2 column 1 of YUV matrix LSB */
> +#define VS6624_RYM0_W22_MSB           0x21A3 /* row 2 column 2 of YUV matrix MSB */
> +#define VS6624_RYM0_W22_LSB           0x21A4 /* row 2 column 2 of YUV matrix LSB */
> +#define VS6624_RYM0_YINY_MSB          0x21A7 /* Y in Y MSB */
> +#define VS6624_RYM0_YINY_LSB          0x21A8 /* Y in Y LSB */
> +#define VS6624_RYM0_YINCB_MSB         0x21AB /* Y in Cb MSB */
> +#define VS6624_RYM0_YINCB_LSB         0x21AC /* Y in Cb LSB */
> +#define VS6624_RYM0_YINCR_MSB         0x21B0 /* Y in Cr MSB */
> +#define VS6624_RYM0_YINCR_LSB         0x21AF /* Y in Cr LSB */
> +/* pipe 1 RGB to YUV matrix manual control */
> +#define VS6624_RYM1_MAN_CTRL          0x2200 /* enable manual RGB to YUV matrix */
> +#define VS6624_RYM1_W00_MSB           0x2203 /* row 0 column 0 of YUV matrix MSB */
> +#define VS6624_RYM1_W00_LSB           0x2204 /* row 0 column 0 of YUV matrix LSB */
> +#define VS6624_RYM1_W01_MSB           0x2207 /* row 0 column 1 of YUV matrix MSB */
> +#define VS6624_RYM1_W01_LSB           0x2208 /* row 0 column 1 of YUV matrix LSB */
> +#define VS6624_RYM1_W02_MSB           0x220C /* row 0 column 2 of YUV matrix MSB */
> +#define VS6624_RYM1_W02_LSB           0x220D /* row 0 column 2 of YUV matrix LSB */
> +#define VS6624_RYM1_W10_MSB           0x2210 /* row 1 column 0 of YUV matrix MSB */
> +#define VS6624_RYM1_W10_LSB           0x220F /* row 1 column 0 of YUV matrix LSB */
> +#define VS6624_RYM1_W11_MSB           0x2213 /* row 1 column 1 of YUV matrix MSB */
> +#define VS6624_RYM1_W11_LSB           0x2214 /* row 1 column 1 of YUV matrix LSB */
> +#define VS6624_RYM1_W12_MSB           0x2217 /* row 1 column 2 of YUV matrix MSB */
> +#define VS6624_RYM1_W12_LSB           0x2218 /* row 1 column 2 of YUV matrix LSB */
> +#define VS6624_RYM1_W20_MSB           0x221B /* row 2 column 0 of YUV matrix MSB */
> +#define VS6624_RYM1_W20_LSB           0x221C /* row 2 column 0 of YUV matrix LSB */
> +#define VS6624_RYM1_W21_MSB           0x2220 /* row 2 column 1 of YUV matrix MSB */
> +#define VS6624_RYM1_W21_LSB           0x221F /* row 2 column 1 of YUV matrix LSB */
> +#define VS6624_RYM1_W22_MSB           0x2223 /* row 2 column 2 of YUV matrix MSB */
> +#define VS6624_RYM1_W22_LSB           0x2224 /* row 2 column 2 of YUV matrix LSB */
> +#define VS6624_RYM1_YINY_MSB          0x2227 /* Y in Y MSB */
> +#define VS6624_RYM1_YINY_LSB          0x2228 /* Y in Y LSB */
> +#define VS6624_RYM1_YINCB_MSB         0x222B /* Y in Cb MSB */
> +#define VS6624_RYM1_YINCB_LSB         0x222C /* Y in Cb LSB */
> +#define VS6624_RYM1_YINCR_MSB         0x2220 /* Y in Cr MSB */
> +#define VS6624_RYM1_YINCR_LSB         0x222F /* Y in Cr LSB */
> +/* pipe 0 gamma manual control */
> +#define VS6624_GAMMA_MAN_CTRL0        0x2280 /* enable manual gamma setup */
> +#define VS6624_GAMMA_PEAK_R0          0x2282 /* peaked red channel gamma value */
> +#define VS6624_GAMMA_PEAK_G0          0x2284 /* peaked green channel gamma value */
> +#define VS6624_GAMMA_PEAK_B0          0x2286 /* peaked blue channel gamma value */
> +#define VS6624_GAMMA_UNPEAK_R0        0x2288 /* unpeaked red channel gamma value */
> +#define VS6624_GAMMA_UNPEAK_G0        0x228A /* unpeaked green channel gamma value */
> +#define VS6624_GAMMA_UNPEAK_B0        0x228C /* unpeaked blue channel gamma value */
> +/* pipe 1 gamma manual control */
> +#define VS6624_GAMMA_MAN_CTRL1        0x2300 /* enable manual gamma setup */
> +#define VS6624_GAMMA_PEAK_R1          0x2302 /* peaked red channel gamma value */
> +#define VS6624_GAMMA_PEAK_G1          0x2304 /* peaked green channel gamma value */
> +#define VS6624_GAMMA_PEAK_B1          0x2306 /* peaked blue channel gamma value */
> +#define VS6624_GAMMA_UNPEAK_R1        0x2308 /* unpeaked red channel gamma value */
> +#define VS6624_GAMMA_UNPEAK_G1        0x230A /* unpeaked green channel gamma value */
> +#define VS6624_GAMMA_UNPEAK_B1        0x230C /* unpeaked blue channel gamma value */
> +/* fade to black */
> +#define VS6624_F2B_DISABLE            0x2480 /* disable fade to black */
> +#define VS6624_F2B_BLACK_VAL_MSB      0x2483 /* black value MSB */
> +#define VS6624_F2B_BLACK_VAL_LSB      0x2484 /* black value LSB */
> +#define VS6624_F2B_LOW_THR_MSB        0x2487 /* low threshold for exposure MSB */
> +#define VS6624_F2B_LOW_THR_LSB        0x2488 /* low threshold for exposure LSB */
> +#define VS6624_F2B_HIGH_THR_MSB       0x248B /* high threshold for exposure MSB */
> +#define VS6624_F2B_HIGH_THR_LSB       0x248C /* high threshold for exposure LSB */
> +#define VS6624_F2B_MIN_OUT_MSB        0x248F /* minimum damper output MSB */
> +#define VS6624_F2B_MIN_OUT_LSB        0x2490 /* minimum damper output LSB */
> +/* output formatter control */
> +#define VS6624_CODE_CK_EN             0x2580 /* code check enable */
> +#define VS6624_BLANK_FMT              0x2582 /* blank format */
> +#define VS6624_SYNC_CODE_SETUP        0x2584 /* sync code setup */
> +#define VS6624_HSYNC_SETUP            0x2586 /* H sync setup */
> +#define VS6624_VSYNC_SETUP            0x2588 /* V sync setup */
> +#define VS6624_PCLK_SETUP             0x258A /* PCLK setup */
> +#define VS6624_PCLK_EN                0x258C /* PCLK enable */
> +#define VS6624_OPF_SP_SETUP           0x258E /* output formatter sp setup */
> +#define VS6624_BLANK_DATA_MSB         0x2590 /* blank data MSB */
> +#define VS6624_BLANK_DATA_LSB         0x2592 /* blank data LSB */
> +#define VS6624_RGB_SETUP              0x2594 /* RGB setup */
> +#define VS6624_YUV_SETUP              0x2596 /* YUV setup */
> +#define VS6624_VSYNC_RIS_COARSE_H     0x2598 /* V sync rising coarse high */
> +#define VS6624_VSYNC_RIS_COARSE_L     0x259A /* V sync rising coarse low */
> +#define VS6624_VSYNC_RIS_FINE_H       0x259C /* V sync rising fine high */
> +#define VS6624_VSYNC_RIS_FINE_L       0x259E /* V sync rising fine low */
> +#define VS6624_VSYNC_FALL_COARSE_H    0x25A0 /* V sync falling coarse high */
> +#define VS6624_VSYNC_FALL_COARSE_L    0x25A2 /* V sync falling coarse low */
> +#define VS6624_VSYNC_FALL_FINE_H      0x25A4 /* V sync falling fine high */
> +#define VS6624_VSYNC_FALL_FINE_L      0x25A6 /* V sync falling fine low */
> +#define VS6624_HSYNC_RIS_H            0x25A8 /* H sync rising high */
> +#define VS6624_HSYNC_RIS_L            0x25AA /* H sync rising low */
> +#define VS6624_HSYNC_FALL_H           0x25AC /* H sync falling high */
> +#define VS6624_HSYNC_FALL_L           0x25AE /* H sync falling low */
> +#define VS6624_OUT_IF                 0x25B0 /* output interface */
> +#define VS6624_CCP_EXT_DATA           0x25B2 /* CCP extra data */
> +/* NoRA controls */
> +#define VS6624_NORA_DISABLE           0x2600 /* NoRA control mode */
> +#define VS6624_NORA_USAGE             0x2602 /* usage */
> +#define VS6624_NORA_SPLIT_KN          0x2604 /* split kn */
> +#define VS6624_NORA_SPLIT_NI          0x2606 /* split ni */
> +#define VS6624_NORA_TIGHT_G           0x2608 /* tight green */
> +#define VS6624_NORA_DISABLE_NP        0x260A /* disable noro promoting */
> +#define VS6624_NORA_LOW_THR_MSB       0x260D /* low threshold for exposure MSB */
> +#define VS6624_NORA_LOW_THR_LSB       0x260E /* low threshold for exposure LSB */
> +#define VS6624_NORA_HIGH_THR_MSB      0x2611 /* high threshold for exposure MSB */
> +#define VS6624_NORA_HIGH_THR_LSB      0x2612 /* high threshold for exposure LSB */
> +#define VS6624_NORA_MIN_OUT_MSB       0x2615 /* minimum damper output MSB */
> +#define VS6624_NORA_MIN_OUT_LSB       0x2616 /* minimum damper output LSB */
> +
> +#endif
> diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
> index 20fd16d..0133b3b 100644
> --- a/include/media/v4l2-chip-ident.h
> +++ b/include/media/v4l2-chip-ident.h
> @@ -143,6 +143,9 @@ enum {
>  	/* module saa6588: just ident 6588 */
>  	V4L2_IDENT_SAA6588 = 6588,
>  
> +	/* module vs6624: just ident 6624 */
> +	V4L2_IDENT_VS6624 = 6624,
> +
>  	/* module saa6752hs: reserved range 6750-6759 */
>  	V4L2_IDENT_SAA6752HS = 6752,
>  	V4L2_IDENT_SAA6752HS_AC3 = 6753,
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver
  2011-09-19 20:59 ` [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver Scott Jiang
@ 2011-09-26 14:09   ` Hans Verkuil
  2011-09-27  8:23     ` Scott Jiang
  2011-09-30 15:07   ` Guennadi Liakhovetski
  1 sibling, 1 reply; 12+ messages in thread
From: Hans Verkuil @ 2011-09-26 14:09 UTC (permalink / raw)
  To: Scott Jiang
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Guennadi Liakhovetski,
	linux-media, uclinux-dist-devel

On Monday, September 19, 2011 22:59:41 Scott Jiang wrote:
> this is a v4l2 bridge driver for Blackfin video capture device,
> support ppi interface
> 
> Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>
> ---
>  drivers/media/video/Kconfig                 |    2 +
>  drivers/media/video/Makefile                |    2 +
>  drivers/media/video/blackfin/Kconfig        |   10 +
>  drivers/media/video/blackfin/Makefile       |    2 +
>  drivers/media/video/blackfin/bfin_capture.c | 1114 +++++++++++++++++++++++++++
>  drivers/media/video/blackfin/ppi.c          |  201 +++++
>  include/media/blackfin/bfin_capture.h       |   33 +
>  include/media/blackfin/ppi.h                |   63 ++
>  8 files changed, 1427 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/blackfin/Kconfig
>  create mode 100644 drivers/media/video/blackfin/Makefile
>  create mode 100644 drivers/media/video/blackfin/bfin_capture.c
>  create mode 100644 drivers/media/video/blackfin/ppi.c
>  create mode 100644 include/media/blackfin/bfin_capture.h
>  create mode 100644 include/media/blackfin/ppi.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 1c03abf..52cd491 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -602,6 +602,8 @@ source "drivers/media/video/davinci/Kconfig"
>  
>  source "drivers/media/video/omap/Kconfig"
>  
> +source "drivers/media/video/blackfin/Kconfig"
> +
>  source "drivers/media/video/bt8xx/Kconfig"
>  
>  config VIDEO_PMS
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 03b55ed..227a700 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -177,6 +177,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
>  
> +obj-$(CONFIG_BLACKFIN)                  += blackfin/
> +
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
>  
>  obj-$(CONFIG_VIDEO_SH_VOU)		+= sh_vou.o
> diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig
> new file mode 100644
> index 0000000..ecd5323
> --- /dev/null
> +++ b/drivers/media/video/blackfin/Kconfig
> @@ -0,0 +1,10 @@
> +config VIDEO_BLACKFIN_CAPTURE
> +	tristate "Blackfin Video Capture Driver"
> +	depends on VIDEO_V4L2 && BLACKFIN && I2C
> +	select VIDEOBUF2_DMA_CONTIG
> +	help
> +	  V4L2 bridge driver for Blackfin video capture device.
> +	  Choose PPI or EPPI as its interface.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called bfin_video_capture.
> diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile
> new file mode 100644
> index 0000000..aa3a0a2
> --- /dev/null
> +++ b/drivers/media/video/blackfin/Makefile
> @@ -0,0 +1,2 @@
> +bfin_video_capture-objs := bfin_capture.o ppi.o
> +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
> diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
> new file mode 100644
> index 0000000..30ed8bb
> --- /dev/null
> +++ b/drivers/media/video/blackfin/bfin_capture.c
> @@ -0,0 +1,1114 @@
> +/*
> + * Analog Devices video capture driver
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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/completion.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/fs.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/time.h>
> +#include <linux/types.h>
> +
> +#include <media/v4l2-chip-ident.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include <asm/dma.h>
> +
> +#include <media/blackfin/bfin_capture.h>
> +
> +#define CAPTURE_DRV_NAME        "bfin_capture"
> +#define BCAP_MIN_NUM_BUF        2
> +
> +struct bcap_format {
> +	u8 *desc;
> +	u32 pixelformat;
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	int bpp; /* bits per pixel */
> +};
> +
> +struct bcap_buffer {
> +	struct vb2_buffer vb;
> +	struct list_head list;
> +};
> +
> +struct bcap_device {
> +	/* capture device instance */
> +	struct v4l2_device v4l2_dev;
> +	/* device node data */
> +	struct video_device *video_dev;
> +	/* sub device instance */
> +	struct v4l2_subdev *sd;
> +	/* capture config */
> +	struct bfin_capture_config *cfg;
> +	/* ppi interface */
> +	struct ppi_if *ppi;
> +	/* current input */
> +	unsigned int cur_input;
> +	/* current selected standard */
> +	v4l2_std_id std;
> +	/* used to store pixel format */
> +	struct v4l2_pix_format fmt;
> +	/* bits per pixel*/
> +	int bpp;
> +	/* pointing to current video buffer */
> +	struct bcap_buffer *cur_frm;
> +	/* pointing to next video buffer */
> +	struct bcap_buffer *next_frm;
> +	/* buffer queue used in videobuf2 */
> +	struct vb2_queue buffer_queue;
> +	/* allocator-specific contexts for each plane */
> +	struct vb2_alloc_ctx *alloc_ctx;
> +	/* queue of filled frames */
> +	struct list_head dma_queue;
> +	/* used in videobuf2 callback */
> +	spinlock_t lock;
> +	/* used to access capture device */
> +	struct mutex mutex;
> +	/* used to wait ppi to complete one transfer */
> +	struct completion comp;
> +	/* number of users performing IO */
> +	unsigned int io_usrs;
> +	/* number of open instances of the device */
> +	unsigned int usrs;
> +	/* indicate whether streaming has started */
> +	bool started;
> +};
> +
> +struct bcap_fh {
> +	struct v4l2_fh fh;
> +	struct bcap_device *bcap_dev;
> +	/* indicates whether this file handle is doing IO */
> +	bool io_allowed;
> +};
> +
> +static const struct bcap_format bcap_formats[] = {
> +	{
> +		.desc        = "YCbCr 4:2:2 Interleaved UYVY",
> +		.pixelformat = V4L2_PIX_FMT_UYVY,
> +		.mbus_code   = V4L2_MBUS_FMT_UYVY8_2X8,
> +		.bpp         = 16,
> +	},
> +	{
> +		.desc        = "YCbCr 4:2:2 Interleaved YUYV",
> +		.pixelformat = V4L2_PIX_FMT_YUYV,
> +		.mbus_code   = V4L2_MBUS_FMT_YUYV8_2X8,
> +		.bpp         = 16,
> +	},
> +	{
> +		.desc        = "RGB 565",
> +		.pixelformat = V4L2_PIX_FMT_RGB565,
> +		.mbus_code   = V4L2_MBUS_FMT_RGB565_2X8_LE,
> +		.bpp         = 16,
> +	},
> +	{
> +		.desc        = "RGB 444",
> +		.pixelformat = V4L2_PIX_FMT_RGB444,
> +		.mbus_code   = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
> +		.bpp         = 16,
> +	},
> +
> +};
> +#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
> +
> +static irqreturn_t bcap_isr(int irq, void *dev_id);
> +
> +static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
> +{
> +	return container_of(vb, struct bcap_buffer, vb);
> +}
> +
> +static int bcap_open(struct file *file)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct video_device *vfd = bcap_dev->video_dev;
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	struct v4l2_pix_format *pixfmt = &bcap_dev->fmt;
> +	const struct bcap_format *bcap_fmt;
> +	struct bcap_fh *bcap_fh;
> +	int ret, i;
> +
> +	if (!bcap_dev->sd) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
> +		return -ENODEV;
> +	}
> +
> +	/* if first open, query format of subdevice as default format */
> +	if (!bcap_dev->usrs) {

Instead of keeping track of the users yourself, you can also call
v4l2_fh_is_singular(). This returns true if this is the only opened
filehandle for this video device (i.e., if this is the first open).

> +		ret = v4l2_subdev_call(bcap_dev->sd, video,
> +					g_mbus_fmt, &mbus_fmt);
> +		if (ret < 0)
> +			return ret;
> +
> +		for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +			if (mbus_fmt.code != bcap_formats[i].mbus_code)
> +				continue;
> +			bcap_fmt = &bcap_formats[i];
> +			v4l2_fill_pix_format(pixfmt, &mbus_fmt);
> +			pixfmt->pixelformat = bcap_fmt->pixelformat;
> +			pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
> +			pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
> +			break;
> +		}
> +		if (i == BCAP_MAX_FMTS) {
> +			v4l2_err(&bcap_dev->v4l2_dev,
> +					"subdev fmt is not supported by bcap\n");
> +			return -EINVAL;
> +		}

Why do this on first open? Shouldn't it be better to do this after the subdev
was loaded?

> +	}
> +
> +	bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
> +	if (!bcap_fh) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +			 "unable to allocate memory for file handle object\n");
> +		return -ENOMEM;
> +	}
> +
> +	v4l2_fh_init(&bcap_fh->fh, vfd);
> +
> +	/* store pointer to v4l2_fh in private_data member of file */
> +	file->private_data = &bcap_fh->fh;
> +	v4l2_fh_add(&bcap_fh->fh);
> +	bcap_fh->bcap_dev = bcap_dev;

There is no need for the bcap_dev field in bcap_fh: from v4l2_fh you can get
the video_device struct, and from there you can get bcap_dev.

> +	bcap_dev->usrs++;
> +	bcap_fh->io_allowed = false;
> +	return 0;
> +}
> +
> +static int bcap_release(struct file *file)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	/* if this instance is doing IO */
> +	if (bcap_fh->io_allowed) {
> +		vb2_queue_release(&bcap_dev->buffer_queue);
> +		bcap_dev->io_usrs = 0;
> +	}
> +
> +	bcap_dev->usrs--;
> +	file->private_data = NULL;
> +	v4l2_fh_del(&bcap_fh->fh);
> +	v4l2_fh_exit(&bcap_fh->fh);
> +	kfree(bcap_fh);
> +	return 0;
> +}
> +
> +static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return vb2_mmap(&bcap_dev->buffer_queue, vma);
> +}
> +
> +#ifndef CONFIG_MMU
> +static unsigned long bcap_get_unmapped_area(struct file *file,
> +					    unsigned long addr,
> +					    unsigned long len,
> +					    unsigned long pgoff,
> +					    unsigned long flags)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
> +				     addr,
> +				     len,
> +				     pgoff,
> +				     flags);
> +}
> +#endif
> +
> +static unsigned int bcap_poll(struct file *file, poll_table *wait)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (bcap_dev->started)

Polling can also be done when there is no streaming in progress.

> +		return vb2_poll(&bcap_dev->buffer_queue, file, wait);
> +	return 0;
> +}
> +
> +static int bcap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> +				unsigned int *nplanes, unsigned long sizes[],
> +				void *alloc_ctxs[])
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +
> +	if (*nbuffers < BCAP_MIN_NUM_BUF)
> +		*nbuffers = BCAP_MIN_NUM_BUF;
> +
> +	*nplanes = 1;
> +	sizes[0] = bcap_dev->fmt.sizeimage;
> +	alloc_ctxs[0] = bcap_dev->alloc_ctx;
> +
> +	return 0;
> +}
> +
> +static int bcap_buffer_init(struct vb2_buffer *vb)
> +{
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +
> +	INIT_LIST_HEAD(&buf->list);
> +	return 0;
> +}
> +
> +static int bcap_buffer_prepare(struct vb2_buffer *vb)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +	unsigned long size;
> +
> +	size = bcap_dev->fmt.sizeimage;
> +	if (vb2_plane_size(vb, 0) < size) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
> +				vb2_plane_size(vb, 0), size);
> +		return -EINVAL;
> +	}
> +	vb2_set_plane_payload(&buf->vb, 0, size);
> +
> +	return 0;
> +}
> +
> +static void bcap_buffer_queue(struct vb2_buffer *vb)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +	unsigned long flags = 0;
> +
> +	spin_lock_irqsave(&bcap_dev->lock, flags);
> +	list_add_tail(&buf->list, &bcap_dev->dma_queue);
> +	spin_unlock_irqrestore(&bcap_dev->lock, flags);
> +}
> +
> +static void bcap_buffer_cleanup(struct vb2_buffer *vb)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +	unsigned long flags = 0;
> +
> +	spin_lock_irqsave(&bcap_dev->lock, flags);
> +	list_del_init(&buf->list);
> +	spin_unlock_irqrestore(&bcap_dev->lock, flags);
> +}
> +
> +static void bcap_lock(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	mutex_lock(&bcap_dev->mutex);
> +}
> +
> +static void bcap_unlock(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	mutex_unlock(&bcap_dev->mutex);
> +}
> +
> +static int bcap_start_streaming(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	struct ppi_if *ppi = bcap_dev->ppi;
> +	struct ppi_params params;
> +	int ret;
> +
> +	/* enable streamon on the sub device */
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
> +	if (ret && (ret != -ENOIOCTLCMD)) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
> +		return ret;
> +	}
> +
> +	/* attach ppi DMA irq handler */
> +	ret = ppi->ops->attach_irq(ppi, bcap_isr);
> +	if (ret < 0) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Error in attaching interrupt handler\n");
> +		return -EFAULT;
> +	}
> +
> +	/* set ppi params */
> +	params.width = bcap_dev->fmt.width;
> +	params.height = bcap_dev->fmt.height;
> +	params.bpp = bcap_dev->bpp;
> +	params.ppi_control = bcap_dev->cfg->ppi_control;
> +	ret = ppi->ops->set_params(ppi, &params);
> +	if (ret < 0) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Error in setting ppi params\n");
> +		ppi->ops->detach_irq(ppi);
> +		return -EINVAL;
> +	}
> +
> +	INIT_COMPLETION(bcap_dev->comp);
> +	return 0;
> +}
> +
> +static int bcap_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	struct ppi_if *ppi = bcap_dev->ppi;
> +	int ret;
> +
> +	if (!bcap_dev->started)
> +		return 0;
> +
> +	bcap_dev->started = false;

Is 'started' really needed? You can do the same with vb2_is_streaming().

> +	wait_for_completion(&bcap_dev->comp);
> +	ppi->ops->stop(ppi);
> +	ppi->ops->detach_irq(ppi);
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
> +	if (ret && (ret != -ENOIOCTLCMD))
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"stream off failed in subdev\n");
> +
> +	/* release all active buffers */
> +	while (!list_empty(&bcap_dev->dma_queue)) {
> +		bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
> +						struct bcap_buffer, list);
> +		list_del(&bcap_dev->next_frm->list);
> +		vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
> +	}
> +	return 0;
> +}
> +
> +static struct vb2_ops bcap_video_qops = {
> +	.queue_setup            = bcap_queue_setup,
> +	.buf_init               = bcap_buffer_init,
> +	.buf_prepare            = bcap_buffer_prepare,
> +	.buf_cleanup            = bcap_buffer_cleanup,
> +	.buf_queue              = bcap_buffer_queue,
> +	.wait_prepare           = bcap_unlock,
> +	.wait_finish            = bcap_lock,
> +	.start_streaming        = bcap_start_streaming,
> +	.stop_streaming         = bcap_stop_streaming,
> +};
> +
> +static int bcap_reqbufs(struct file *file, void *priv,
> +			struct v4l2_requestbuffers *req_buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	if (bcap_dev->io_usrs != 0) {

Hmm, I think you can do the same with vb2_is_busy().

> +		v4l2_err(&bcap_dev->v4l2_dev, "Only one IO user allowed\n");

Don't make this an error. v4l2_dbg is better. This is a perfectly 'normal'
occurence that shouldn't pollute the kernel log.

> +		return -EBUSY;
> +	}
> +
> +	bcap_fh->io_allowed = true;
> +	bcap_dev->io_usrs = 1;
> +
> +	return vb2_reqbufs(&bcap_dev->buffer_queue, req_buf);
> +}
> +
> +static int bcap_querybuf(struct file *file, void *priv,
> +				struct v4l2_buffer *buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return vb2_querybuf(&bcap_dev->buffer_queue, buf);
> +}
> +
> +static int bcap_qbuf(struct file *file, void *priv,
> +			struct v4l2_buffer *buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	if (!bcap_fh->io_allowed)
> +		return -EACCES;
> +
> +	return vb2_qbuf(&bcap_dev->buffer_queue, buf);
> +}
> +
> +static int bcap_dqbuf(struct file *file, void *priv,
> +			struct v4l2_buffer *buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	if (!bcap_fh->io_allowed)
> +		return -EACCES;

Should be EBUSY. Ditto for bcap_qbuf.

> +	return vb2_dqbuf(&bcap_dev->buffer_queue,
> +				buf, file->f_flags & O_NONBLOCK);
> +}
> +
> +static irqreturn_t bcap_isr(int irq, void *dev_id)
> +{
> +	struct ppi_if *ppi = dev_id;
> +	struct bcap_device *bcap_dev = ppi->priv;
> +	struct timeval timevalue;
> +	struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
> +	dma_addr_t addr;
> +
> +	spin_lock(&bcap_dev->lock);
> +
> +	if (bcap_dev->cur_frm != bcap_dev->next_frm) {
> +		do_gettimeofday(&timevalue);
> +		vb->v4l2_buf.timestamp = timevalue;
> +		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
> +		bcap_dev->cur_frm = bcap_dev->next_frm;
> +	}
> +
> +	ppi->ops->stop(ppi);
> +
> +	if (!bcap_dev->started) {
> +		complete(&bcap_dev->comp);
> +	} else {
> +		if (!list_empty(&bcap_dev->dma_queue)) {
> +			bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
> +						struct bcap_buffer, list);
> +			list_del(&bcap_dev->next_frm->list);
> +			addr = vb2_dma_contig_plane_paddr(&bcap_dev->next_frm->vb, 0);
> +			ppi->ops->update_addr(ppi, (unsigned long)addr);
> +		}
> +		ppi->ops->start(ppi);
> +	}
> +
> +	spin_unlock(&bcap_dev->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int bcap_streamon(struct file *file, void *priv,
> +				enum v4l2_buf_type buf_type)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bcap_fh *fh = file->private_data;
> +	struct ppi_if *ppi = bcap_dev->ppi;
> +	dma_addr_t addr;
> +	int ret;
> +
> +	if (!fh->io_allowed)
> +		return -EACCES;

EBUSY.

> +
> +	if (bcap_dev->started)
> +		return -EBUSY;

No need for this, vb2_streamon already tests for that.

> +
> +	/* call streamon to start streaming in videobuf */
> +	ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
> +	if (ret)
> +		return ret;
> +
> +	/* if dma queue is empty, return error */
> +	if (list_empty(&bcap_dev->dma_queue)) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	/* get the next frame from the dma queue */
> +	bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
> +					struct bcap_buffer, list);
> +	bcap_dev->cur_frm = bcap_dev->next_frm;
> +	/* remove buffer from the dma queue */
> +	list_del(&bcap_dev->cur_frm->list);
> +	addr = vb2_dma_contig_plane_paddr(&bcap_dev->cur_frm->vb, 0);
> +	/* update DMA address */
> +	ppi->ops->update_addr(ppi, (unsigned long)addr);
> +	/* enable ppi */
> +	ppi->ops->start(ppi);
> +	bcap_dev->started = true;

This should be done in the start_streaming callback.

> +
> +	return 0;
> +err:
> +	vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
> +	return ret;
> +}
> +
> +static int bcap_streamoff(struct file *file, void *priv,
> +				enum v4l2_buf_type buf_type)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bcap_fh *fh = file->private_data;
> +
> +	if (!fh->io_allowed)
> +		return -EACCES;

EBUSY

> +
> +	if (!bcap_dev->started)
> +		return -EINVAL;

Already checked by vb2_streamoff.

> +	return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
> +}
> +
> +static int bcap_queryctrl(struct file *file, void *priv,
> +				struct v4l2_queryctrl *qctrl)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core, queryctrl, qctrl);
> +}
> +
> +static int bcap_g_ctrl(struct file *file, void *priv,
> +			struct v4l2_control *ctrl)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core, g_ctrl, ctrl);
> +}
> +
> +static int bcap_s_ctrl(struct file *file, void *priv,
> +			struct v4l2_control *ctrl)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core, s_ctrl, ctrl);
> +}

Remove these three: no longer needed if you use the control framework.

Hmm, you apparently don't use the control framework yet: please add it.
All new drivers *must* use the control framework.

> +
> +static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	int ret;
> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
> +	return ret;

No need for the 'ret' variable here. Just do 'return v4l2_subdev_call()'.

> +}
> +
> +static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (bcap_dev->std) {
> +		*std = bcap_dev->std;
> +		return 0;
> +	} else
> +		return -EINVAL;

Always set 'std' to some default standard. This function should never have to
return an error.

> +}
> +
> +static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	int ret;
> +
> +	/* if streaming is started, return error */
> +	if (bcap_dev->started) {

if (vb2_is_busy(...)) is the right test. REQBUFS already sets up the buffers
based on the standard, so from that point on you can't change the standard.

> +		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");

v4l2_dbg.

> +		return -EBUSY;
> +	}
> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
> +	if (ret < 0)
> +		return ret;
> +
> +	bcap_dev->std = *std;
> +	return 0;
> +}
> +
> +static int bcap_enum_input(struct file *file, void *priv,
> +				struct v4l2_input *input)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bfin_capture_config *config = bcap_dev->cfg;
> +	int ret;
> +	u32 status;
> +
> +	if (input->index >= config->num_inputs)
> +		return -EINVAL;
> +
> +	*input = config->inputs[input->index];
> +	/* get input status */
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
> +	if (!ret)
> +		input->status = status;
> +	return 0;
> +}
> +
> +static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	*index = bcap_dev->cur_input;
> +	return 0;
> +}
> +
> +static int bcap_s_input(struct file *file, void *priv, unsigned int index)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bfin_capture_config *config = bcap_dev->cfg;
> +	struct bcap_route *route;
> +	int ret;
> +
> +	/* if streaming is started, return error */
> +	if (bcap_dev->started) {

vb2_is_busy()

> +		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");

v4l2_dbg

> +		return -EBUSY;
> +	}
> +
> +	if (index >= config->num_inputs)
> +		return -EINVAL;
> +
> +	route = &config->routes[index];
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
> +				route->input, route->output, 0);
> +	if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
> +		return ret;
> +	}
> +	bcap_dev->cur_input = index;
> +	return 0;
> +}
> +
> +static int bcap_try_format(struct bcap_device *bcap,
> +				struct v4l2_pix_format *pixfmt,
> +				enum v4l2_mbus_pixelcode *mbus_code,
> +				int *bpp)
> +{
> +	const struct bcap_format *fmt;
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	int ret, i;
> +
> +	for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +		if (pixfmt->pixelformat != bcap_formats[i].pixelformat)
> +			continue;
> +		fmt = &bcap_formats[i];
> +		if (mbus_code)
> +			*mbus_code = fmt->mbus_code;
> +		if (bpp)
> +			*bpp = fmt->bpp;
> +		v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
> +		ret = v4l2_subdev_call(bcap->sd, video,
> +					try_mbus_fmt, &mbus_fmt);
> +		if (ret < 0)
> +			return ret;
> +		v4l2_fill_pix_format(pixfmt, &mbus_fmt);
> +		pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
> +		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
> +		return 0;
> +	}
> +	return -EINVAL;
> +}
> +
> +static int bcap_enum_fmt_vid_cap(struct file *file, void  *priv,
> +					struct v4l2_fmtdesc *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	u32 index = fmt->index;
> +	int ret, i;
> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, video,
> +				enum_mbus_fmt, index, &mbus_code);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +		if (mbus_code != bcap_formats[i].mbus_code)
> +			continue;
> +		fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		strlcpy(fmt->description,
> +			bcap_formats[index].desc,
> +			sizeof(fmt->description));
> +		fmt->pixelformat = bcap_formats[index].pixelformat;
> +		return 0;
> +	}
> +	v4l2_err(&bcap_dev->v4l2_dev,
> +			"subdev fmt is not supported by bcap\n");
> +	return -EINVAL;
> +}
> +
> +static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
> +					struct v4l2_format *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> +
> +	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
> +}
> +
> +static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
> +				struct v4l2_format *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	const struct bcap_format *bcap_fmt;
> +	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> +	int ret, i;
> +
> +	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;

No need to test, it's implicit in the function name. Ditto for the try/set
variants.

> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, video,
> +				g_mbus_fmt, &mbus_fmt);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +		if (mbus_fmt.code != bcap_formats[i].mbus_code)
> +			continue;
> +		bcap_fmt = &bcap_formats[i];
> +		v4l2_fill_pix_format(pixfmt, &mbus_fmt);
> +		pixfmt->pixelformat = bcap_fmt->pixelformat;
> +		pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
> +		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
> +		return 0;
> +	}
> +	v4l2_err(&bcap_dev->v4l2_dev,
> +			"subdev fmt is not supported by bcap\n");
> +	return -EINVAL;
> +}
> +
> +static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
> +				struct v4l2_format *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> +	int ret, bpp;
> +
> +	/* if streaming is started, return error */
> +	if (bcap_dev->started) {

vb2_is_busy().

> +		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");

v4l2_dbg

> +		return -EBUSY;
> +	}
> +
> +	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +	/* see if format works */
> +	ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
> +	if (ret < 0)
> +		return ret;
> +
> +	v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
> +	if (ret < 0)
> +		return ret;
> +	bcap_dev->fmt = *pixfmt;
> +	bcap_dev->bpp = bpp;
> +	return 0;
> +}
> +
> +static int bcap_querycap(struct file *file, void  *priv,
> +				struct v4l2_capability *cap)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
> +	strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
> +	strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
> +	return 0;
> +}
> +
> +static int bcap_cropcap(struct file *file, void *priv,
> +			struct v4l2_cropcap *crop)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	return v4l2_subdev_call(bcap_dev->sd, video, cropcap, crop);
> +}
> +
> +static int bcap_g_parm(struct file *file, void *fh,
> +				struct v4l2_streamparm *a)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +	return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
> +}
> +
> +static int bcap_s_parm(struct file *file, void *fh,
> +				struct v4l2_streamparm *a)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +	return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
> +}
> +
> +static int bcap_g_chip_ident(struct file *file, void *priv,
> +		struct v4l2_dbg_chip_ident *chip)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	chip->ident = V4L2_IDENT_NONE;
> +	chip->revision = 0;
> +	if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
> +			chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
> +		return -EINVAL;
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core,
> +			g_chip_ident, chip);
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int bcap_dbg_g_register(struct file *file, void *priv,
> +		struct v4l2_dbg_register *reg)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core,
> +			g_register, reg);
> +}
> +
> +static int bcap_dbg_s_register(struct file *file, void *priv,
> +		struct v4l2_dbg_register *reg)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core,
> +			s_register, reg);
> +}
> +#endif
> +
> +static int bcap_log_status(struct file *file, void *priv)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	/* status for sub devices */
> +	v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
> +	.vidioc_querycap         = bcap_querycap,
> +	.vidioc_g_fmt_vid_cap    = bcap_g_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap    = bcap_s_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap  = bcap_try_fmt_vid_cap,
> +	.vidioc_enum_input       = bcap_enum_input,
> +	.vidioc_g_input          = bcap_g_input,
> +	.vidioc_s_input          = bcap_s_input,
> +	.vidioc_querystd         = bcap_querystd,
> +	.vidioc_s_std            = bcap_s_std,
> +	.vidioc_g_std            = bcap_g_std,
> +	.vidioc_queryctrl        = bcap_queryctrl,
> +	.vidioc_g_ctrl           = bcap_g_ctrl,
> +	.vidioc_s_ctrl           = bcap_s_ctrl,

Remove these three control callbacks and use the control framework instead.

> +	.vidioc_reqbufs          = bcap_reqbufs,
> +	.vidioc_querybuf         = bcap_querybuf,
> +	.vidioc_qbuf             = bcap_qbuf,
> +	.vidioc_dqbuf            = bcap_dqbuf,
> +	.vidioc_streamon         = bcap_streamon,
> +	.vidioc_streamoff        = bcap_streamoff,
> +	.vidioc_cropcap          = bcap_cropcap,
> +	.vidioc_g_parm           = bcap_g_parm,
> +	.vidioc_s_parm           = bcap_s_parm,
> +	.vidioc_g_chip_ident     = bcap_g_chip_ident,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.vidioc_g_register       = bcap_dbg_g_register,
> +	.vidioc_s_register       = bcap_dbg_s_register,
> +#endif
> +	.vidioc_log_status       = bcap_log_status,
> +};
> +
> +static struct v4l2_file_operations bcap_fops = {
> +	.owner = THIS_MODULE,
> +	.open = bcap_open,
> +	.release = bcap_release,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = bcap_mmap,
> +#ifndef CONFIG_MMU
> +	.get_unmapped_area = bcap_get_unmapped_area,
> +#endif
> +	.poll = bcap_poll
> +};
> +
> +static int __devinit bcap_probe(struct platform_device *pdev)
> +{
> +	struct bcap_device *bcap_dev;
> +	struct video_device *vfd;
> +	struct i2c_adapter *i2c_adap;
> +	struct bfin_capture_config *config;
> +	struct vb2_queue *q;
> +	int ret;
> +
> +	config = pdev->dev.platform_data;
> +	if (!config) {
> +		v4l2_err(pdev->dev.driver, "Unable to get board config\n");
> +		return -ENODEV;
> +	}
> +
> +	bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
> +	if (!bcap_dev) {
> +		v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
> +		return -ENOMEM;
> +	}
> +
> +	bcap_dev->cfg = config;
> +
> +	bcap_dev->ppi = ppi_create_instance(config->ppi_info);
> +	if (!bcap_dev->ppi) {
> +		v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
> +		ret = -ENODEV;
> +		goto err_free_dev;
> +	}
> +	bcap_dev->ppi->priv = bcap_dev;
> +
> +	bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> +	if (IS_ERR(bcap_dev->alloc_ctx)) {
> +		ret = PTR_ERR(bcap_dev->alloc_ctx);
> +		goto err_free_ppi;
> +	}
> +
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		ret = -ENOMEM;
> +		v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
> +		goto err_cleanup_ctx;
> +	}
> +
> +	/* initialize field of video device */
> +	vfd->release            = video_device_release;
> +	vfd->fops               = &bcap_fops;
> +	vfd->ioctl_ops          = &bcap_ioctl_ops;
> +	vfd->tvnorms            = 0;
> +	vfd->v4l2_dev           = &bcap_dev->v4l2_dev;
> +	set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
> +	strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
> +	bcap_dev->video_dev     = vfd;
> +
> +	ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
> +	if (ret) {
> +		v4l2_err(pdev->dev.driver,
> +				"Unable to register v4l2 device\n");
> +		goto err_release_vdev;
> +	}
> +	v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
> +
> +	spin_lock_init(&bcap_dev->lock);
> +	/* initialize queue */
> +	q = &bcap_dev->buffer_queue;
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP;
> +	q->drv_priv = bcap_dev;
> +	q->buf_struct_size = sizeof(struct bcap_buffer);
> +	q->ops = &bcap_video_qops;
> +	q->mem_ops = &vb2_dma_contig_memops;
> +
> +	vb2_queue_init(q);
> +
> +	mutex_init(&bcap_dev->mutex);
> +	init_completion(&bcap_dev->comp);
> +
> +	/* init video dma queues */
> +	INIT_LIST_HEAD(&bcap_dev->dma_queue);
> +
> +	vfd->lock = &bcap_dev->mutex;
> +
> +	/* register video device */
> +	ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Unable to register video device\n");
> +		goto err_unreg_v4l2;
> +	}
> +	video_set_drvdata(bcap_dev->video_dev, bcap_dev);
> +	v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
> +			video_device_node_name(vfd));
> +
> +	/* load up the subdevice */
> +	i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
> +	if (!i2c_adap) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Unable to find i2c adapter\n");
> +		goto err_unreg_vdev;
> +
> +	}
> +	bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
> +						 i2c_adap,
> +						 &config->board_info,
> +						 NULL);
> +	if (bcap_dev->sd) {
> +		int i;
> +		/* update tvnorms from the sub devices */
> +		for (i = 0; i < config->num_inputs; i++)
> +			vfd->tvnorms |= config->inputs[i].std;
> +		/* set default std */
> +		bcap_dev->std = vfd->tvnorms;
> +	} else {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Unable to register sub device\n");
> +		goto err_unreg_vdev;
> +	}
> +	v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
> +	return 0;
> +err_unreg_vdev:
> +	video_unregister_device(bcap_dev->video_dev);
> +err_unreg_v4l2:
> +	v4l2_device_unregister(&bcap_dev->v4l2_dev);
> +err_release_vdev:
> +	if (!video_is_registered(bcap_dev->video_dev))
> +		video_device_release(bcap_dev->video_dev);
> +err_cleanup_ctx:
> +	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
> +err_free_ppi:
> +	ppi_delete_instance(bcap_dev->ppi);
> +err_free_dev:
> +	kfree(bcap_dev);
> +	return ret;
> +}
> +
> +static int __devexit bcap_remove(struct platform_device *pdev)
> +{
> +	struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
> +	struct bcap_device *bcap_dev = container_of(v4l2_dev,
> +						struct bcap_device, v4l2_dev);
> +
> +	video_unregister_device(bcap_dev->video_dev);
> +	v4l2_device_unregister(v4l2_dev);
> +	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
> +	ppi_delete_instance(bcap_dev->ppi);
> +	kfree(bcap_dev);
> +	return 0;
> +}
> +
> +static struct platform_driver bcap_driver = {
> +	.driver = {
> +		.name   = CAPTURE_DRV_NAME,
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = bcap_probe,
> +	.remove = __devexit_p(bcap_remove),
> +};
> +
> +static __init int bcap_init(void)
> +{
> +	return platform_driver_register(&bcap_driver);
> +}
> +
> +static __exit void bcap_exit(void)
> +{
> +	platform_driver_unregister(&bcap_driver);
> +}
> +
> +module_init(bcap_init);
> +module_exit(bcap_exit);
> +
> +MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
> +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/video/blackfin/ppi.c
> new file mode 100644
> index 0000000..3b5f43e
> --- /dev/null
> +++ b/drivers/media/video/blackfin/ppi.c
> @@ -0,0 +1,201 @@
> +/*
> + * ppi.c Analog Devices Parallel Peripheral Interface driver
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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/slab.h>
> +
> +#include <asm/bfin_ppi.h>
> +#include <asm/blackfin.h>
> +#include <asm/cacheflush.h>
> +#include <asm/dma.h>
> +#include <asm/portmux.h>
> +
> +#include <media/blackfin/ppi.h>
> +
> +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
> +static void ppi_detach_irq(struct ppi_if *ppi);
> +static int ppi_start(struct ppi_if *ppi);
> +static int ppi_stop(struct ppi_if *ppi);
> +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
> +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
> +
> +static const struct ppi_ops ppi_ops = {
> +	.attach_irq = ppi_attach_irq,
> +	.detach_irq = ppi_detach_irq,
> +	.start = ppi_start,
> +	.stop = ppi_stop,
> +	.set_params = ppi_set_params,
> +	.update_addr = ppi_update_addr,
> +};
> +
> +static irqreturn_t ppi_irq_err(int irq, void *dev_id)
> +{
> +	struct ppi_if *ppi = dev_id;
> +	const struct ppi_info *info = ppi->info;
> +	unsigned short status;
> +
> +	if (!strcmp(info->name, "ppi")) {
> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;
> +		status = bfin_read16(reg->status);
> +		if (printk_ratelimit())
> +			pr_info("%s: status = 0x%x\n", __func__, status);
> +		bfin_write16(&reg->status, 0xff);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	if (request_dma(info->dma_ch, "PPI_DMA") < 0) {
> +		pr_err("Unable to allocate DMA channel for PPI\n");
> +		return -EBUSY;
> +	}
> +	set_dma_callback(info->dma_ch, handler, ppi);
> +
> +	if (request_irq(info->irq_err, ppi_irq_err, IRQF_DISABLED,
> +				"PPI ERROR", ppi)) {
> +		pr_err("Unable to allocate IRQ for PPI\n");
> +		free_dma(info->dma_ch);
> +		return -EBUSY;
> +	}
> +	return 0;
> +}
> +
> +static void ppi_detach_irq(struct ppi_if *ppi)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	free_irq(info->irq_err, ppi);
> +	free_dma(info->dma_ch);
> +}
> +
> +static int ppi_start(struct ppi_if *ppi)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	/* enable DMA */
> +	enable_dma(info->dma_ch);
> +
> +	/* enable PPI */
> +	ppi->ppi_control |= PORT_EN;
> +	if (!strcmp(info->name, "ppi")) {
> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;
> +		bfin_write16(&reg->control, ppi->ppi_control);
> +	}
> +
> +	SSYNC();
> +	return 0;
> +}
> +
> +static int ppi_stop(struct ppi_if *ppi)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	/* disable PPI */
> +	ppi->ppi_control &= ~PORT_EN;
> +	if (!strcmp(info->name, "ppi")) {
> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;
> +		bfin_write16(&reg->control, ppi->ppi_control);
> +	}
> +
> +	/* disable DMA */
> +	clear_dma_irqstat(info->dma_ch);
> +	disable_dma(info->dma_ch);
> +
> +	SSYNC();
> +	return 0;
> +}
> +
> +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	ppi->bytes_per_line = params->width * params->bpp / 8;
> +	ppi->lines_per_frame = params->height;
> +
> +	/* config DMA */
> +	ppi->dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN);
> +	if (params->ppi_control & DMA32) {
> +		ppi->dma_config |= WDSIZE_32;
> +		set_dma_x_count(info->dma_ch, ppi->bytes_per_line >> 2);
> +		set_dma_x_modify(info->dma_ch, 4);
> +		set_dma_y_modify(info->dma_ch, 4);
> +	} else {
> +		ppi->dma_config |= WDSIZE_16;
> +		set_dma_x_count(info->dma_ch, ppi->bytes_per_line >> 1);
> +		set_dma_x_modify(info->dma_ch, 2);
> +		set_dma_y_modify(info->dma_ch, 2);
> +	}
> +	set_dma_y_count(info->dma_ch, ppi->lines_per_frame);
> +	set_dma_config(info->dma_ch, ppi->dma_config);
> +
> +	/* config PPI */
> +	ppi->ppi_control = params->ppi_control & ~PORT_EN;
> +	if (!strcmp(info->name, "ppi")) {
> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;
> +		bfin_write16(&reg->control, ppi->ppi_control);
> +		bfin_write16(&reg->count, ppi->bytes_per_line - 1);
> +		bfin_write16(&reg->frame, ppi->lines_per_frame);
> +	}
> +
> +	SSYNC();
> +	return 0;
> +}
> +
> +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
> +{
> +	set_dma_start_addr(ppi->info->dma_ch, addr);
> +}
> +
> +struct ppi_if *ppi_create_instance(const struct ppi_info *info)
> +{
> +	struct ppi_if *ppi;
> +
> +	if (!info || !info->name || !info->pin_req)
> +		return NULL;
> +
> +	if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
> +		pr_err("request peripheral failed\n");
> +		return NULL;
> +	}
> +
> +	ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
> +	if (!ppi) {
> +		peripheral_free_list(info->pin_req);
> +		pr_err("unable to allocate memory for ppi handle\n");
> +		return NULL;
> +	}
> +	ppi->ops = &ppi_ops;
> +	ppi->info = info;
> +
> +	pr_info("ppi probe success\n");
> +	return ppi;
> +}
> +
> +void ppi_delete_instance(struct ppi_if *ppi)
> +{
> +	peripheral_free_list(ppi->info->pin_req);
> +	kfree(ppi);
> +}
> diff --git a/include/media/blackfin/bfin_capture.h b/include/media/blackfin/bfin_capture.h
> new file mode 100644
> index 0000000..0905837
> --- /dev/null
> +++ b/include/media/blackfin/bfin_capture.h
> @@ -0,0 +1,33 @@
> +#ifndef _BFIN_CAPTURE_H_
> +#define _BFIN_CAPTURE_H_
> +
> +#include <linux/i2c.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/blackfin/ppi.h>
> +
> +struct bcap_route {
> +	u32 input;
> +	u32 output;
> +};
> +
> +struct bfin_capture_config {
> +	/* card name */
> +	char *card_name;
> +	/* inputs available at the sub device */
> +	struct v4l2_input *inputs;
> +	/* number of inputs supported */
> +	int num_inputs;
> +	/* routing information for each input */
> +	struct bcap_route *routes;
> +	/* i2c bus adapter no */
> +	int i2c_adapter_id;
> +	/* i2c subdevice board info */
> +	struct i2c_board_info board_info;
> +	/* ppi board info */
> +	const struct ppi_info *ppi_info;
> +	/* ppi control */
> +	unsigned short ppi_control;
> +};
> +
> +#endif
> diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h
> new file mode 100644
> index 0000000..4e7711a
> --- /dev/null
> +++ b/include/media/blackfin/ppi.h
> @@ -0,0 +1,63 @@
> +/*
> + * Analog Devices PPI header file
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#ifndef _PPI_H_
> +#define _PPI_H_
> +
> +#include <linux/interrupt.h>
> +
> +struct ppi_if;
> +
> +struct ppi_params {
> +	int width;
> +	int height;
> +	int bpp;
> +	unsigned short ppi_control;
> +};
> +
> +struct ppi_ops {
> +	int (*attach_irq)(struct ppi_if *ppi, irq_handler_t handler);
> +	void (*detach_irq)(struct ppi_if *ppi);
> +	int (*start)(struct ppi_if *ppi);
> +	int (*stop)(struct ppi_if *ppi);
> +	int (*set_params)(struct ppi_if *ppi, struct ppi_params *params);
> +	void (*update_addr)(struct ppi_if *ppi, unsigned long addr);
> +};
> +
> +struct ppi_info {
> +	const char *name; /* ppi or eppi */
> +	int dma_ch;
> +	int irq_err;
> +	unsigned long base;
> +	const unsigned short *pin_req;
> +};
> +
> +struct ppi_if {
> +	int dma_config;
> +	int bytes_per_line;
> +	int lines_per_frame;
> +	unsigned short ppi_control;
> +	const struct ppi_ops *ops;
> +	const struct ppi_info *info;
> +	void *priv;
> +};
> +
> +struct ppi_if *ppi_create_instance(const struct ppi_info *info);
> +void ppi_delete_instance(struct ppi_if *ppi);
> +#endif
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver
  2011-09-26 14:09   ` Hans Verkuil
@ 2011-09-27  8:23     ` Scott Jiang
  2011-09-27  9:42       ` Hans Verkuil
  0 siblings, 1 reply; 12+ messages in thread
From: Scott Jiang @ 2011-09-27  8:23 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Guennadi Liakhovetski,
	linux-media, uclinux-dist-devel

>
>> +             ret = v4l2_subdev_call(bcap_dev->sd, video,
>> +                                     g_mbus_fmt, &mbus_fmt);
>> +             if (ret < 0)
>> +                     return ret;
>> +
>> +             for (i = 0; i < BCAP_MAX_FMTS; i++) {
>> +                     if (mbus_fmt.code != bcap_formats[i].mbus_code)
>> +                             continue;
>> +                     bcap_fmt = &bcap_formats[i];
>> +                     v4l2_fill_pix_format(pixfmt, &mbus_fmt);
>> +                     pixfmt->pixelformat = bcap_fmt->pixelformat;
>> +                     pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
>> +                     pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
>> +                     break;
>> +             }
>> +             if (i == BCAP_MAX_FMTS) {
>> +                     v4l2_err(&bcap_dev->v4l2_dev,
>> +                                     "subdev fmt is not supported by bcap\n");
>> +                     return -EINVAL;
>> +             }
>
> Why do this on first open? Shouldn't it be better to do this after the subdev
> was loaded?
>
Hi Hans, thank you for your comments.
This point I haven't had a good solution. PPI is only a parallel port,
it has no default std or format.
That's why you always found I have no default std and format.
Sylwester Nawrocki recommend me add this code here, but different
input can has different std and format according to v4l2 spec.
That means if app only set input, or set input and std without setting
format, the default format getting here may be invalid.
Do you have any better solution for this?

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver
  2011-09-27  8:23     ` Scott Jiang
@ 2011-09-27  9:42       ` Hans Verkuil
  2011-09-27 10:00         ` Scott Jiang
  0 siblings, 1 reply; 12+ messages in thread
From: Hans Verkuil @ 2011-09-27  9:42 UTC (permalink / raw)
  To: Scott Jiang
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Guennadi Liakhovetski,
	linux-media, uclinux-dist-devel

On Tuesday, September 27, 2011 10:23:35 Scott Jiang wrote:
> >
> >> +             ret = v4l2_subdev_call(bcap_dev->sd, video,
> >> +                                     g_mbus_fmt, &mbus_fmt);
> >> +             if (ret < 0)
> >> +                     return ret;
> >> +
> >> +             for (i = 0; i < BCAP_MAX_FMTS; i++) {
> >> +                     if (mbus_fmt.code != bcap_formats[i].mbus_code)
> >> +                             continue;
> >> +                     bcap_fmt = &bcap_formats[i];
> >> +                     v4l2_fill_pix_format(pixfmt, &mbus_fmt);
> >> +                     pixfmt->pixelformat = bcap_fmt->pixelformat;
> >> +                     pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
> >> +                     pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
> >> +                     break;
> >> +             }
> >> +             if (i == BCAP_MAX_FMTS) {
> >> +                     v4l2_err(&bcap_dev->v4l2_dev,
> >> +                                     "subdev fmt is not supported by bcap\n");
> >> +                     return -EINVAL;
> >> +             }
> >
> > Why do this on first open? Shouldn't it be better to do this after the subdev
> > was loaded?
> >
> Hi Hans, thank you for your comments.
> This point I haven't had a good solution. PPI is only a parallel port,
> it has no default std or format.
> That's why you always found I have no default std and format.
> Sylwester Nawrocki recommend me add this code here, but different
> input can has different std and format according to v4l2 spec.
> That means if app only set input, or set input and std without setting
> format, the default format getting here may be invalid.
> Do you have any better solution for this?

What you would typically do in a case like this (if I understand it
correctly) is that in the s_input ioctl you first select the input in the
subdev, and then you can call the subdev to determine the standard and
format and use that information to set up the bridge. This requires that
the subdev is able to return a proper standard/format after an input
change.

By also selecting an initial input at driver load you will ensure that
you always have a default std/fmt available from the very beginning.

It also looks like the s_input in the bridge driver allows for inputs that
return a subdev format that can't be supported by the bridge. Is that correct?
If so, then the board code should disallow such inputs. Frankly, that's a
WARN_ON since that is something that is never supposed to happen.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver
  2011-09-27  9:42       ` Hans Verkuil
@ 2011-09-27 10:00         ` Scott Jiang
  2011-09-27 10:18           ` Hans Verkuil
  0 siblings, 1 reply; 12+ messages in thread
From: Scott Jiang @ 2011-09-27 10:00 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Guennadi Liakhovetski,
	linux-media, uclinux-dist-devel

>
> What you would typically do in a case like this (if I understand it
> correctly) is that in the s_input ioctl you first select the input in the
> subdev, and then you can call the subdev to determine the standard and
> format and use that information to set up the bridge. This requires that
> the subdev is able to return a proper standard/format after an input
> change.
>
> By also selecting an initial input at driver load you will ensure that
> you always have a default std/fmt available from the very beginning.
>
The default input is 0. So you mean I ask the subdev std and fmt in
probe instead of open?

> It also looks like the s_input in the bridge driver allows for inputs that
> return a subdev format that can't be supported by the bridge. Is that correct?
> If so, then the board code should disallow such inputs. Frankly, that's a
> WARN_ON since that is something that is never supposed to happen.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver
  2011-09-27 10:00         ` Scott Jiang
@ 2011-09-27 10:18           ` Hans Verkuil
  0 siblings, 0 replies; 12+ messages in thread
From: Hans Verkuil @ 2011-09-27 10:18 UTC (permalink / raw)
  To: Scott Jiang
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Guennadi Liakhovetski,
	linux-media, uclinux-dist-devel

On Tuesday, September 27, 2011 12:00:03 Scott Jiang wrote:
> >
> > What you would typically do in a case like this (if I understand it
> > correctly) is that in the s_input ioctl you first select the input in the
> > subdev, and then you can call the subdev to determine the standard and
> > format and use that information to set up the bridge. This requires that
> > the subdev is able to return a proper standard/format after an input
> > change.
> >
> > By also selecting an initial input at driver load you will ensure that
> > you always have a default std/fmt available from the very beginning.
> >
> The default input is 0. So you mean I ask the subdev std and fmt in
> probe instead of open?

Yes. In general that's the best place. There are cases where you want to do
such initialization on the first open, but that's when you need to do e.g. a
slow firmware upload. If you do not have such special requirements, then
setting up the hardware to a sane state is best done in probe.

Regards,

	Hans

> > It also looks like the s_input in the bridge driver allows for inputs that
> > return a subdev format that can't be supported by the bridge. Is that correct?
> > If so, then the board code should disallow such inputs. Frankly, that's a
> > WARN_ON since that is something that is never supposed to happen.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver
  2011-09-19 20:59 ` [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver Scott Jiang
  2011-09-26 14:09   ` Hans Verkuil
@ 2011-09-30 15:07   ` Guennadi Liakhovetski
  1 sibling, 0 replies; 12+ messages in thread
From: Guennadi Liakhovetski @ 2011-09-30 15:07 UTC (permalink / raw)
  To: Scott Jiang
  Cc: Mauro Carvalho Chehab, Laurent Pinchart, Hans Verkuil,
	linux-media, uclinux-dist-devel

On Mon, 19 Sep 2011, Scott Jiang wrote:

> this is a v4l2 bridge driver for Blackfin video capture device,
> support ppi interface
> 
> Signed-off-by: Scott Jiang <scott.jiang.linux@gmail.com>

Ok, I know, coming up with new comments to old code in a patch is bad, 
sorry about that... But, I think, Hans has requested some changes too, so, 
you anyway will be doing a v3.

> ---
>  drivers/media/video/Kconfig                 |    2 +
>  drivers/media/video/Makefile                |    2 +
>  drivers/media/video/blackfin/Kconfig        |   10 +
>  drivers/media/video/blackfin/Makefile       |    2 +
>  drivers/media/video/blackfin/bfin_capture.c | 1114 +++++++++++++++++++++++++++
>  drivers/media/video/blackfin/ppi.c          |  201 +++++
>  include/media/blackfin/bfin_capture.h       |   33 +
>  include/media/blackfin/ppi.h                |   63 ++
>  8 files changed, 1427 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/blackfin/Kconfig
>  create mode 100644 drivers/media/video/blackfin/Makefile
>  create mode 100644 drivers/media/video/blackfin/bfin_capture.c
>  create mode 100644 drivers/media/video/blackfin/ppi.c
>  create mode 100644 include/media/blackfin/bfin_capture.h
>  create mode 100644 include/media/blackfin/ppi.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 1c03abf..52cd491 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -602,6 +602,8 @@ source "drivers/media/video/davinci/Kconfig"
>  
>  source "drivers/media/video/omap/Kconfig"
>  
> +source "drivers/media/video/blackfin/Kconfig"
> +
>  source "drivers/media/video/bt8xx/Kconfig"
>  
>  config VIDEO_PMS
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 03b55ed..227a700 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -177,6 +177,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) 	+= s5p-fimc/
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC)	+= s5p-mfc/
>  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV)	+= s5p-tv/
>  
> +obj-$(CONFIG_BLACKFIN)                  += blackfin/

Please, use TABs

> +
>  obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
>  
>  obj-$(CONFIG_VIDEO_SH_VOU)		+= sh_vou.o
> diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig
> new file mode 100644
> index 0000000..ecd5323
> --- /dev/null
> +++ b/drivers/media/video/blackfin/Kconfig
> @@ -0,0 +1,10 @@
> +config VIDEO_BLACKFIN_CAPTURE
> +	tristate "Blackfin Video Capture Driver"
> +	depends on VIDEO_V4L2 && BLACKFIN && I2C
> +	select VIDEOBUF2_DMA_CONTIG
> +	help
> +	  V4L2 bridge driver for Blackfin video capture device.
> +	  Choose PPI or EPPI as its interface.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called bfin_video_capture.
> diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile
> new file mode 100644
> index 0000000..aa3a0a2
> --- /dev/null
> +++ b/drivers/media/video/blackfin/Makefile
> @@ -0,0 +1,2 @@
> +bfin_video_capture-objs := bfin_capture.o ppi.o
> +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
> diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
> new file mode 100644
> index 0000000..30ed8bb
> --- /dev/null
> +++ b/drivers/media/video/blackfin/bfin_capture.c
> @@ -0,0 +1,1114 @@
> +/*
> + * Analog Devices video capture driver
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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/completion.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/fs.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/time.h>
> +#include <linux/types.h>
> +
> +#include <media/v4l2-chip-ident.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include <asm/dma.h>
> +
> +#include <media/blackfin/bfin_capture.h>
> +
> +#define CAPTURE_DRV_NAME        "bfin_capture"
> +#define BCAP_MIN_NUM_BUF        2
> +
> +struct bcap_format {
> +	u8 *desc;

No need for "u8," use "char" instead - you use it for strings.

> +	u32 pixelformat;
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	int bpp; /* bits per pixel */
> +};
> +
> +struct bcap_buffer {
> +	struct vb2_buffer vb;
> +	struct list_head list;
> +};
> +
> +struct bcap_device {
> +	/* capture device instance */
> +	struct v4l2_device v4l2_dev;
> +	/* device node data */
> +	struct video_device *video_dev;
> +	/* sub device instance */
> +	struct v4l2_subdev *sd;
> +	/* capture config */
> +	struct bfin_capture_config *cfg;
> +	/* ppi interface */
> +	struct ppi_if *ppi;
> +	/* current input */
> +	unsigned int cur_input;
> +	/* current selected standard */
> +	v4l2_std_id std;
> +	/* used to store pixel format */
> +	struct v4l2_pix_format fmt;
> +	/* bits per pixel*/
> +	int bpp;
> +	/* pointing to current video buffer */
> +	struct bcap_buffer *cur_frm;
> +	/* pointing to next video buffer */
> +	struct bcap_buffer *next_frm;
> +	/* buffer queue used in videobuf2 */
> +	struct vb2_queue buffer_queue;
> +	/* allocator-specific contexts for each plane */
> +	struct vb2_alloc_ctx *alloc_ctx;
> +	/* queue of filled frames */
> +	struct list_head dma_queue;
> +	/* used in videobuf2 callback */
> +	spinlock_t lock;
> +	/* used to access capture device */
> +	struct mutex mutex;
> +	/* used to wait ppi to complete one transfer */
> +	struct completion comp;
> +	/* number of users performing IO */
> +	unsigned int io_usrs;
> +	/* number of open instances of the device */
> +	unsigned int usrs;
> +	/* indicate whether streaming has started */
> +	bool started;
> +};
> +
> +struct bcap_fh {
> +	struct v4l2_fh fh;
> +	struct bcap_device *bcap_dev;
> +	/* indicates whether this file handle is doing IO */
> +	bool io_allowed;
> +};
> +
> +static const struct bcap_format bcap_formats[] = {
> +	{
> +		.desc        = "YCbCr 4:2:2 Interleaved UYVY",
> +		.pixelformat = V4L2_PIX_FMT_UYVY,
> +		.mbus_code   = V4L2_MBUS_FMT_UYVY8_2X8,
> +		.bpp         = 16,
> +	},
> +	{
> +		.desc        = "YCbCr 4:2:2 Interleaved YUYV",
> +		.pixelformat = V4L2_PIX_FMT_YUYV,
> +		.mbus_code   = V4L2_MBUS_FMT_YUYV8_2X8,
> +		.bpp         = 16,
> +	},
> +	{
> +		.desc        = "RGB 565",
> +		.pixelformat = V4L2_PIX_FMT_RGB565,
> +		.mbus_code   = V4L2_MBUS_FMT_RGB565_2X8_LE,
> +		.bpp         = 16,
> +	},
> +	{
> +		.desc        = "RGB 444",
> +		.pixelformat = V4L2_PIX_FMT_RGB444,
> +		.mbus_code   = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
> +		.bpp         = 16,
> +	},
> +
> +};
> +#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
> +
> +static irqreturn_t bcap_isr(int irq, void *dev_id);
> +
> +static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
> +{
> +	return container_of(vb, struct bcap_buffer, vb);
> +}
> +
> +static int bcap_open(struct file *file)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct video_device *vfd = bcap_dev->video_dev;
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	struct v4l2_pix_format *pixfmt = &bcap_dev->fmt;
> +	const struct bcap_format *bcap_fmt;
> +	struct bcap_fh *bcap_fh;
> +	int ret, i;
> +
> +	if (!bcap_dev->sd) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
> +		return -ENODEV;
> +	}
> +
> +	/* if first open, query format of subdevice as default format */
> +	if (!bcap_dev->usrs) {
> +		ret = v4l2_subdev_call(bcap_dev->sd, video,
> +					g_mbus_fmt, &mbus_fmt);
> +		if (ret < 0)
> +			return ret;
> +
> +		for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +			if (mbus_fmt.code != bcap_formats[i].mbus_code)
> +				continue;
> +			bcap_fmt = &bcap_formats[i];
> +			v4l2_fill_pix_format(pixfmt, &mbus_fmt);
> +			pixfmt->pixelformat = bcap_fmt->pixelformat;
> +			pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
> +			pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
> +			break;
> +		}
> +		if (i == BCAP_MAX_FMTS) {
> +			v4l2_err(&bcap_dev->v4l2_dev,
> +					"subdev fmt is not supported by bcap\n");
> +			return -EINVAL;
> +		}

Now, this looks ugly to me. Maybe this would be better:

		for (i = 0; i < BCAP_MAX_FMTS; i++)
			if (mbus_fmt.code == bcap_formats[i].mbus_code)
				break;
		if (i == BCAP_MAX_FMTS) {
			v4l2_err(&bcap_dev->v4l2_dev,
					"subdev fmt is not supported by bcap\n");
			return -EINVAL;
		}
		bcap_fmt = &bcap_formats[i];
		v4l2_fill_pix_format(pixfmt, &mbus_fmt);
		...

> +	}
> +
> +	bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
> +	if (!bcap_fh) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +			 "unable to allocate memory for file handle object\n");
> +		return -ENOMEM;
> +	}
> +
> +	v4l2_fh_init(&bcap_fh->fh, vfd);
> +
> +	/* store pointer to v4l2_fh in private_data member of file */
> +	file->private_data = &bcap_fh->fh;
> +	v4l2_fh_add(&bcap_fh->fh);
> +	bcap_fh->bcap_dev = bcap_dev;
> +	bcap_dev->usrs++;
> +	bcap_fh->io_allowed = false;
> +	return 0;
> +}
> +
> +static int bcap_release(struct file *file)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	/* if this instance is doing IO */
> +	if (bcap_fh->io_allowed) {
> +		vb2_queue_release(&bcap_dev->buffer_queue);
> +		bcap_dev->io_usrs = 0;
> +	}
> +
> +	bcap_dev->usrs--;
> +	file->private_data = NULL;
> +	v4l2_fh_del(&bcap_fh->fh);
> +	v4l2_fh_exit(&bcap_fh->fh);
> +	kfree(bcap_fh);
> +	return 0;
> +}
> +
> +static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return vb2_mmap(&bcap_dev->buffer_queue, vma);
> +}
> +
> +#ifndef CONFIG_MMU
> +static unsigned long bcap_get_unmapped_area(struct file *file,
> +					    unsigned long addr,
> +					    unsigned long len,
> +					    unsigned long pgoff,
> +					    unsigned long flags)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
> +				     addr,
> +				     len,
> +				     pgoff,
> +				     flags);
> +}
> +#endif
> +
> +static unsigned int bcap_poll(struct file *file, poll_table *wait)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (bcap_dev->started)
> +		return vb2_poll(&bcap_dev->buffer_queue, file, wait);
> +	return 0;
> +}
> +
> +static int bcap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
> +				unsigned int *nplanes, unsigned long sizes[],
> +				void *alloc_ctxs[])
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +
> +	if (*nbuffers < BCAP_MIN_NUM_BUF)
> +		*nbuffers = BCAP_MIN_NUM_BUF;
> +
> +	*nplanes = 1;
> +	sizes[0] = bcap_dev->fmt.sizeimage;
> +	alloc_ctxs[0] = bcap_dev->alloc_ctx;
> +
> +	return 0;
> +}
> +
> +static int bcap_buffer_init(struct vb2_buffer *vb)
> +{
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +
> +	INIT_LIST_HEAD(&buf->list);
> +	return 0;
> +}
> +
> +static int bcap_buffer_prepare(struct vb2_buffer *vb)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +	unsigned long size;
> +
> +	size = bcap_dev->fmt.sizeimage;
> +	if (vb2_plane_size(vb, 0) < size) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
> +				vb2_plane_size(vb, 0), size);
> +		return -EINVAL;
> +	}
> +	vb2_set_plane_payload(&buf->vb, 0, size);
> +
> +	return 0;
> +}
> +
> +static void bcap_buffer_queue(struct vb2_buffer *vb)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +	unsigned long flags = 0;

No need to initialise, same everywhere below.

> +
> +	spin_lock_irqsave(&bcap_dev->lock, flags);
> +	list_add_tail(&buf->list, &bcap_dev->dma_queue);
> +	spin_unlock_irqrestore(&bcap_dev->lock, flags);
> +}
> +
> +static void bcap_buffer_cleanup(struct vb2_buffer *vb)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
> +	struct bcap_buffer *buf = to_bcap_vb(vb);
> +	unsigned long flags = 0;
> +
> +	spin_lock_irqsave(&bcap_dev->lock, flags);
> +	list_del_init(&buf->list);
> +	spin_unlock_irqrestore(&bcap_dev->lock, flags);
> +}
> +
> +static void bcap_lock(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	mutex_lock(&bcap_dev->mutex);
> +}
> +
> +static void bcap_unlock(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	mutex_unlock(&bcap_dev->mutex);
> +}
> +
> +static int bcap_start_streaming(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	struct ppi_if *ppi = bcap_dev->ppi;
> +	struct ppi_params params;
> +	int ret;
> +
> +	/* enable streamon on the sub device */
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
> +	if (ret && (ret != -ENOIOCTLCMD)) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
> +		return ret;
> +	}
> +
> +	/* attach ppi DMA irq handler */
> +	ret = ppi->ops->attach_irq(ppi, bcap_isr);
> +	if (ret < 0) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Error in attaching interrupt handler\n");
> +		return -EFAULT;
> +	}
> +
> +	/* set ppi params */
> +	params.width = bcap_dev->fmt.width;
> +	params.height = bcap_dev->fmt.height;
> +	params.bpp = bcap_dev->bpp;
> +	params.ppi_control = bcap_dev->cfg->ppi_control;
> +	ret = ppi->ops->set_params(ppi, &params);

Why are you using these ppi operations at all? why not just call functions 
directly? Are you expecting a different implementation in the future?

> +	if (ret < 0) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Error in setting ppi params\n");
> +		ppi->ops->detach_irq(ppi);
> +		return -EINVAL;

return ret?

> +	}
> +
> +	INIT_COMPLETION(bcap_dev->comp);
> +	return 0;
> +}
> +
> +static int bcap_stop_streaming(struct vb2_queue *vq)
> +{
> +	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
> +	struct ppi_if *ppi = bcap_dev->ppi;
> +	int ret;
> +
> +	if (!bcap_dev->started)
> +		return 0;
> +
> +	bcap_dev->started = false;
> +	wait_for_completion(&bcap_dev->comp);
> +	ppi->ops->stop(ppi);
> +	ppi->ops->detach_irq(ppi);
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
> +	if (ret && (ret != -ENOIOCTLCMD))
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"stream off failed in subdev\n");
> +
> +	/* release all active buffers */
> +	while (!list_empty(&bcap_dev->dma_queue)) {
> +		bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
> +						struct bcap_buffer, list);
> +		list_del(&bcap_dev->next_frm->list);
> +		vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
> +	}
> +	return 0;
> +}
> +
> +static struct vb2_ops bcap_video_qops = {
> +	.queue_setup            = bcap_queue_setup,
> +	.buf_init               = bcap_buffer_init,
> +	.buf_prepare            = bcap_buffer_prepare,
> +	.buf_cleanup            = bcap_buffer_cleanup,
> +	.buf_queue              = bcap_buffer_queue,
> +	.wait_prepare           = bcap_unlock,
> +	.wait_finish            = bcap_lock,
> +	.start_streaming        = bcap_start_streaming,
> +	.stop_streaming         = bcap_stop_streaming,
> +};
> +
> +static int bcap_reqbufs(struct file *file, void *priv,
> +			struct v4l2_requestbuffers *req_buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	if (bcap_dev->io_usrs != 0) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "Only one IO user allowed\n");
> +		return -EBUSY;
> +	}
> +
> +	bcap_fh->io_allowed = true;
> +	bcap_dev->io_usrs = 1;
> +
> +	return vb2_reqbufs(&bcap_dev->buffer_queue, req_buf);
> +}
> +
> +static int bcap_querybuf(struct file *file, void *priv,
> +				struct v4l2_buffer *buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return vb2_querybuf(&bcap_dev->buffer_queue, buf);
> +}
> +
> +static int bcap_qbuf(struct file *file, void *priv,
> +			struct v4l2_buffer *buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	if (!bcap_fh->io_allowed)
> +		return -EACCES;
> +
> +	return vb2_qbuf(&bcap_dev->buffer_queue, buf);
> +}
> +
> +static int bcap_dqbuf(struct file *file, void *priv,
> +			struct v4l2_buffer *buf)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
> +	struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
> +
> +	if (!bcap_fh->io_allowed)
> +		return -EACCES;
> +
> +	return vb2_dqbuf(&bcap_dev->buffer_queue,
> +				buf, file->f_flags & O_NONBLOCK);
> +}
> +
> +static irqreturn_t bcap_isr(int irq, void *dev_id)
> +{
> +	struct ppi_if *ppi = dev_id;
> +	struct bcap_device *bcap_dev = ppi->priv;
> +	struct timeval timevalue;
> +	struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
> +	dma_addr_t addr;
> +
> +	spin_lock(&bcap_dev->lock);
> +
> +	if (bcap_dev->cur_frm != bcap_dev->next_frm) {
> +		do_gettimeofday(&timevalue);
> +		vb->v4l2_buf.timestamp = timevalue;
> +		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
> +		bcap_dev->cur_frm = bcap_dev->next_frm;
> +	}
> +
> +	ppi->ops->stop(ppi);
> +
> +	if (!bcap_dev->started) {
> +		complete(&bcap_dev->comp);
> +	} else {
> +		if (!list_empty(&bcap_dev->dma_queue)) {
> +			bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
> +						struct bcap_buffer, list);
> +			list_del(&bcap_dev->next_frm->list);
> +			addr = vb2_dma_contig_plane_paddr(&bcap_dev->next_frm->vb, 0);

vb2_dma_contig_plane_dma_addr(), you might want to rebase your work on top 
of

git://linuxtv.org/media_tree.git / staging/for_v3.2

> +			ppi->ops->update_addr(ppi, (unsigned long)addr);
> +		}
> +		ppi->ops->start(ppi);
> +	}
> +
> +	spin_unlock(&bcap_dev->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int bcap_streamon(struct file *file, void *priv,
> +				enum v4l2_buf_type buf_type)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bcap_fh *fh = file->private_data;
> +	struct ppi_if *ppi = bcap_dev->ppi;
> +	dma_addr_t addr;
> +	int ret;
> +
> +	if (!fh->io_allowed)
> +		return -EACCES;
> +
> +	if (bcap_dev->started)
> +		return -EBUSY;
> +
> +	/* call streamon to start streaming in videobuf */
> +	ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
> +	if (ret)
> +		return ret;
> +
> +	/* if dma queue is empty, return error */
> +	if (list_empty(&bcap_dev->dma_queue)) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	/* get the next frame from the dma queue */
> +	bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
> +					struct bcap_buffer, list);
> +	bcap_dev->cur_frm = bcap_dev->next_frm;
> +	/* remove buffer from the dma queue */
> +	list_del(&bcap_dev->cur_frm->list);
> +	addr = vb2_dma_contig_plane_paddr(&bcap_dev->cur_frm->vb, 0);

ditto: ..._dma_addr()

> +	/* update DMA address */
> +	ppi->ops->update_addr(ppi, (unsigned long)addr);
> +	/* enable ppi */
> +	ppi->ops->start(ppi);
> +	bcap_dev->started = true;

Even if ov2640's .s_stream() operation is a NOP, you still might want to 
call it here and in streamoff() below.

> +
> +	return 0;
> +err:
> +	vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
> +	return ret;
> +}
> +
> +static int bcap_streamoff(struct file *file, void *priv,
> +				enum v4l2_buf_type buf_type)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bcap_fh *fh = file->private_data;
> +
> +	if (!fh->io_allowed)
> +		return -EACCES;
> +
> +	if (!bcap_dev->started)
> +		return -EINVAL;
> +
> +	return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
> +}
> +
> +static int bcap_queryctrl(struct file *file, void *priv,
> +				struct v4l2_queryctrl *qctrl)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core, queryctrl, qctrl);
> +}
> +
> +static int bcap_g_ctrl(struct file *file, void *priv,
> +			struct v4l2_control *ctrl)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core, g_ctrl, ctrl);
> +}
> +
> +static int bcap_s_ctrl(struct file *file, void *priv,
> +			struct v4l2_control *ctrl)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core, s_ctrl, ctrl);
> +}
> +
> +static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	int ret;
> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
> +	return ret;
> +}
> +
> +static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (bcap_dev->std) {
> +		*std = bcap_dev->std;
> +		return 0;
> +	} else
> +		return -EINVAL;

Either add braces or change to something like

	if (!bcap_dev->std)
		return -EINVAL;

	*std = bcap_dev->std;
	return 0;

> +}
> +
> +static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	int ret;
> +
> +	/* if streaming is started, return error */
> +	if (bcap_dev->started) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");
> +		return -EBUSY;
> +	}
> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
> +	if (ret < 0)
> +		return ret;
> +
> +	bcap_dev->std = *std;
> +	return 0;
> +}
> +
> +static int bcap_enum_input(struct file *file, void *priv,
> +				struct v4l2_input *input)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bfin_capture_config *config = bcap_dev->cfg;
> +	int ret;
> +	u32 status;
> +
> +	if (input->index >= config->num_inputs)
> +		return -EINVAL;
> +
> +	*input = config->inputs[input->index];
> +	/* get input status */
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
> +	if (!ret)
> +		input->status = status;
> +	return 0;
> +}
> +
> +static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	*index = bcap_dev->cur_input;
> +	return 0;
> +}
> +
> +static int bcap_s_input(struct file *file, void *priv, unsigned int index)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct bfin_capture_config *config = bcap_dev->cfg;
> +	struct bcap_route *route;
> +	int ret;
> +
> +	/* if streaming is started, return error */
> +	if (bcap_dev->started) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");
> +		return -EBUSY;
> +	}
> +
> +	if (index >= config->num_inputs)
> +		return -EINVAL;
> +
> +	route = &config->routes[index];
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
> +				route->input, route->output, 0);
> +	if ((ret < 0) && (ret != -ENOIOCTLCMD)) {

Superfluous parenthesis

> +		v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
> +		return ret;
> +	}
> +	bcap_dev->cur_input = index;
> +	return 0;
> +}
> +
> +static int bcap_try_format(struct bcap_device *bcap,
> +				struct v4l2_pix_format *pixfmt,
> +				enum v4l2_mbus_pixelcode *mbus_code,
> +				int *bpp)
> +{
> +	const struct bcap_format *fmt;
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	int ret, i;
> +
> +	for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +		if (pixfmt->pixelformat != bcap_formats[i].pixelformat)
> +			continue;

IMHO, also here this would look prettier:

	for (...)
		if (...)
			break;

	if (i == MAX)
		return -EINVAL;

	...
	return 0;

But in fact .try_fmt() shouldn't fail. I would only consider failing, if 
the subdevice failed - then you have no chance. Otherwise just pick up any 
format you like, but do not fail.

> +		fmt = &bcap_formats[i];
> +		if (mbus_code)
> +			*mbus_code = fmt->mbus_code;
> +		if (bpp)
> +			*bpp = fmt->bpp;
> +		v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
> +		ret = v4l2_subdev_call(bcap->sd, video,
> +					try_mbus_fmt, &mbus_fmt);
> +		if (ret < 0)
> +			return ret;
> +		v4l2_fill_pix_format(pixfmt, &mbus_fmt);
> +		pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
> +		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
> +		return 0;
> +	}
> +	return -EINVAL;
> +}
> +
> +static int bcap_enum_fmt_vid_cap(struct file *file, void  *priv,
> +					struct v4l2_fmtdesc *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	u32 index = fmt->index;
> +	int ret, i;
> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, video,
> +				enum_mbus_fmt, index, &mbus_code);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +		if (mbus_code != bcap_formats[i].mbus_code)
> +			continue;

ditto - loop formatting

> +		fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		strlcpy(fmt->description,
> +			bcap_formats[index].desc,
> +			sizeof(fmt->description));
> +		fmt->pixelformat = bcap_formats[index].pixelformat;
> +		return 0;
> +	}
> +	v4l2_err(&bcap_dev->v4l2_dev,
> +			"subdev fmt is not supported by bcap\n");
> +	return -EINVAL;
> +}
> +
> +static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
> +					struct v4l2_format *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> +
> +	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
> +}
> +
> +static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
> +				struct v4l2_format *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	const struct bcap_format *bcap_fmt;
> +	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> +	int ret, i;
> +
> +	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	ret = v4l2_subdev_call(bcap_dev->sd, video,
> +				g_mbus_fmt, &mbus_fmt);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < BCAP_MAX_FMTS; i++) {
> +		if (mbus_fmt.code != bcap_formats[i].mbus_code)

-"-

> +			continue;
> +		bcap_fmt = &bcap_formats[i];
> +		v4l2_fill_pix_format(pixfmt, &mbus_fmt);
> +		pixfmt->pixelformat = bcap_fmt->pixelformat;
> +		pixfmt->bytesperline = pixfmt->width * bcap_fmt->bpp / 8;
> +		pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
> +		return 0;
> +	}
> +	v4l2_err(&bcap_dev->v4l2_dev,
> +			"subdev fmt is not supported by bcap\n");
> +	return -EINVAL;

g_fmt() should not be failing either, but you're stuck here in case the 
subdevice returns an unsupported format. That's why in soc-camera we first 
perform some initial format negotiation at probe time - also like Hans has 
suggested - then you know at all times, that you have a valid 
configuration, or you just fail probing straight away.

> +}
> +
> +static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
> +				struct v4l2_format *fmt)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	struct v4l2_mbus_framefmt mbus_fmt;
> +	enum v4l2_mbus_pixelcode mbus_code;
> +	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
> +	int ret, bpp;
> +
> +	/* if streaming is started, return error */
> +	if (bcap_dev->started) {
> +		v4l2_err(&bcap_dev->v4l2_dev, "Streaming is started\n");
> +		return -EBUSY;
> +	}
> +
> +	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +	/* see if format works */
> +	ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
> +	if (ret < 0)
> +		return ret;
> +
> +	v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
> +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
> +	if (ret < 0)
> +		return ret;

the subdevice might have changed geometry and the pixel code - just like 
ov2640 does. You, probably, want to double-check the result.

> +	bcap_dev->fmt = *pixfmt;
> +	bcap_dev->bpp = bpp;
> +	return 0;
> +}
> +
> +static int bcap_querycap(struct file *file, void  *priv,
> +				struct v4l2_capability *cap)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
> +	strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
> +	strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
> +	return 0;
> +}
> +
> +static int bcap_cropcap(struct file *file, void *priv,
> +			struct v4l2_cropcap *crop)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	return v4l2_subdev_call(bcap_dev->sd, video, cropcap, crop);
> +}

Hm, does it make much sense to support cropcap and not to support g_crop 
or s_crop? Not sure what others do in this case, or whether the spec says 
anything about it, but if you cannot crop, it would be logical to return 
defrect == bounds from cropcap.

> +
> +static int bcap_g_parm(struct file *file, void *fh,
> +				struct v4l2_streamparm *a)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +	return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
> +}
> +
> +static int bcap_s_parm(struct file *file, void *fh,
> +				struct v4l2_streamparm *a)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +	return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
> +}
> +
> +static int bcap_g_chip_ident(struct file *file, void *priv,
> +		struct v4l2_dbg_chip_ident *chip)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	chip->ident = V4L2_IDENT_NONE;
> +	chip->revision = 0;
> +	if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
> +			chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
> +		return -EINVAL;
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core,
> +			g_chip_ident, chip);
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int bcap_dbg_g_register(struct file *file, void *priv,
> +		struct v4l2_dbg_register *reg)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core,
> +			g_register, reg);
> +}
> +
> +static int bcap_dbg_s_register(struct file *file, void *priv,
> +		struct v4l2_dbg_register *reg)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +
> +	return v4l2_subdev_call(bcap_dev->sd, core,
> +			s_register, reg);
> +}
> +#endif
> +
> +static int bcap_log_status(struct file *file, void *priv)
> +{
> +	struct bcap_device *bcap_dev = video_drvdata(file);
> +	/* status for sub devices */
> +	v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
> +	.vidioc_querycap         = bcap_querycap,
> +	.vidioc_g_fmt_vid_cap    = bcap_g_fmt_vid_cap,
> +	.vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
> +	.vidioc_s_fmt_vid_cap    = bcap_s_fmt_vid_cap,
> +	.vidioc_try_fmt_vid_cap  = bcap_try_fmt_vid_cap,
> +	.vidioc_enum_input       = bcap_enum_input,
> +	.vidioc_g_input          = bcap_g_input,
> +	.vidioc_s_input          = bcap_s_input,
> +	.vidioc_querystd         = bcap_querystd,
> +	.vidioc_s_std            = bcap_s_std,
> +	.vidioc_g_std            = bcap_g_std,
> +	.vidioc_queryctrl        = bcap_queryctrl,
> +	.vidioc_g_ctrl           = bcap_g_ctrl,
> +	.vidioc_s_ctrl           = bcap_s_ctrl,
> +	.vidioc_reqbufs          = bcap_reqbufs,
> +	.vidioc_querybuf         = bcap_querybuf,
> +	.vidioc_qbuf             = bcap_qbuf,
> +	.vidioc_dqbuf            = bcap_dqbuf,
> +	.vidioc_streamon         = bcap_streamon,
> +	.vidioc_streamoff        = bcap_streamoff,
> +	.vidioc_cropcap          = bcap_cropcap,
> +	.vidioc_g_parm           = bcap_g_parm,
> +	.vidioc_s_parm           = bcap_s_parm,
> +	.vidioc_g_chip_ident     = bcap_g_chip_ident,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.vidioc_g_register       = bcap_dbg_g_register,
> +	.vidioc_s_register       = bcap_dbg_s_register,
> +#endif
> +	.vidioc_log_status       = bcap_log_status,
> +};
> +
> +static struct v4l2_file_operations bcap_fops = {
> +	.owner = THIS_MODULE,
> +	.open = bcap_open,
> +	.release = bcap_release,
> +	.unlocked_ioctl = video_ioctl2,
> +	.mmap = bcap_mmap,
> +#ifndef CONFIG_MMU
> +	.get_unmapped_area = bcap_get_unmapped_area,
> +#endif
> +	.poll = bcap_poll
> +};
> +
> +static int __devinit bcap_probe(struct platform_device *pdev)
> +{
> +	struct bcap_device *bcap_dev;
> +	struct video_device *vfd;
> +	struct i2c_adapter *i2c_adap;
> +	struct bfin_capture_config *config;
> +	struct vb2_queue *q;
> +	int ret;
> +
> +	config = pdev->dev.platform_data;
> +	if (!config) {
> +		v4l2_err(pdev->dev.driver, "Unable to get board config\n");
> +		return -ENODEV;
> +	}
> +
> +	bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
> +	if (!bcap_dev) {
> +		v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
> +		return -ENOMEM;
> +	}
> +
> +	bcap_dev->cfg = config;
> +
> +	bcap_dev->ppi = ppi_create_instance(config->ppi_info);
> +	if (!bcap_dev->ppi) {
> +		v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
> +		ret = -ENODEV;
> +		goto err_free_dev;
> +	}
> +	bcap_dev->ppi->priv = bcap_dev;
> +
> +	bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
> +	if (IS_ERR(bcap_dev->alloc_ctx)) {
> +		ret = PTR_ERR(bcap_dev->alloc_ctx);
> +		goto err_free_ppi;
> +	}
> +
> +	vfd = video_device_alloc();
> +	if (!vfd) {
> +		ret = -ENOMEM;
> +		v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
> +		goto err_cleanup_ctx;
> +	}
> +
> +	/* initialize field of video device */
> +	vfd->release            = video_device_release;
> +	vfd->fops               = &bcap_fops;
> +	vfd->ioctl_ops          = &bcap_ioctl_ops;
> +	vfd->tvnorms            = 0;
> +	vfd->v4l2_dev           = &bcap_dev->v4l2_dev;
> +	set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
> +	strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
> +	bcap_dev->video_dev     = vfd;
> +
> +	ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
> +	if (ret) {
> +		v4l2_err(pdev->dev.driver,
> +				"Unable to register v4l2 device\n");
> +		goto err_release_vdev;
> +	}
> +	v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
> +
> +	spin_lock_init(&bcap_dev->lock);
> +	/* initialize queue */
> +	q = &bcap_dev->buffer_queue;
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP;
> +	q->drv_priv = bcap_dev;
> +	q->buf_struct_size = sizeof(struct bcap_buffer);
> +	q->ops = &bcap_video_qops;
> +	q->mem_ops = &vb2_dma_contig_memops;
> +
> +	vb2_queue_init(q);
> +
> +	mutex_init(&bcap_dev->mutex);
> +	init_completion(&bcap_dev->comp);
> +
> +	/* init video dma queues */
> +	INIT_LIST_HEAD(&bcap_dev->dma_queue);
> +
> +	vfd->lock = &bcap_dev->mutex;
> +
> +	/* register video device */
> +	ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
> +	if (ret) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Unable to register video device\n");
> +		goto err_unreg_v4l2;
> +	}
> +	video_set_drvdata(bcap_dev->video_dev, bcap_dev);
> +	v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
> +			video_device_node_name(vfd));
> +
> +	/* load up the subdevice */
> +	i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
> +	if (!i2c_adap) {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Unable to find i2c adapter\n");
> +		goto err_unreg_vdev;
> +
> +	}
> +	bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
> +						 i2c_adap,
> +						 &config->board_info,
> +						 NULL);
> +	if (bcap_dev->sd) {
> +		int i;
> +		/* update tvnorms from the sub devices */
> +		for (i = 0; i < config->num_inputs; i++)
> +			vfd->tvnorms |= config->inputs[i].std;
> +		/* set default std */
> +		bcap_dev->std = vfd->tvnorms;
> +	} else {
> +		v4l2_err(&bcap_dev->v4l2_dev,
> +				"Unable to register sub device\n");
> +		goto err_unreg_vdev;
> +	}
> +	v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
> +	return 0;
> +err_unreg_vdev:
> +	video_unregister_device(bcap_dev->video_dev);
> +err_unreg_v4l2:
> +	v4l2_device_unregister(&bcap_dev->v4l2_dev);
> +err_release_vdev:
> +	if (!video_is_registered(bcap_dev->video_dev))
> +		video_device_release(bcap_dev->video_dev);

this is strange: if you come here from err_unreg_vdev, 
video_unregister_device() will be called, then the device's release 
method, i.e., video_device_release(), right? That will call 
kfree(bcap_dev->video_dev). Then you arrive here and try to dereference a 
freed pointer... Or am I missing something?If I'm right, setting video_dev 
= NULL just above err_unreg_v4l2 shall avoid this problem.

> +err_cleanup_ctx:
> +	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
> +err_free_ppi:
> +	ppi_delete_instance(bcap_dev->ppi);
> +err_free_dev:
> +	kfree(bcap_dev);
> +	return ret;
> +}
> +
> +static int __devexit bcap_remove(struct platform_device *pdev)
> +{
> +	struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
> +	struct bcap_device *bcap_dev = container_of(v4l2_dev,
> +						struct bcap_device, v4l2_dev);
> +
> +	video_unregister_device(bcap_dev->video_dev);
> +	v4l2_device_unregister(v4l2_dev);
> +	vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
> +	ppi_delete_instance(bcap_dev->ppi);
> +	kfree(bcap_dev);
> +	return 0;
> +}
> +
> +static struct platform_driver bcap_driver = {
> +	.driver = {
> +		.name   = CAPTURE_DRV_NAME,
> +		.owner = THIS_MODULE,

bad alignment

> +	},
> +	.probe = bcap_probe,
> +	.remove = __devexit_p(bcap_remove),
> +};
> +
> +static __init int bcap_init(void)
> +{
> +	return platform_driver_register(&bcap_driver);
> +}
> +
> +static __exit void bcap_exit(void)
> +{
> +	platform_driver_unregister(&bcap_driver);
> +}
> +
> +module_init(bcap_init);
> +module_exit(bcap_exit);
> +
> +MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
> +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/video/blackfin/ppi.c
> new file mode 100644
> index 0000000..3b5f43e
> --- /dev/null
> +++ b/drivers/media/video/blackfin/ppi.c
> @@ -0,0 +1,201 @@
> +/*
> + * ppi.c Analog Devices Parallel Peripheral Interface driver

So, you expect to have other interfaces (serial or something), which would 
be implementing the same operations and attach to your bfin_capture.c 
instead of ppi.c?

> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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/slab.h>
> +
> +#include <asm/bfin_ppi.h>
> +#include <asm/blackfin.h>
> +#include <asm/cacheflush.h>
> +#include <asm/dma.h>
> +#include <asm/portmux.h>
> +
> +#include <media/blackfin/ppi.h>
> +
> +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
> +static void ppi_detach_irq(struct ppi_if *ppi);
> +static int ppi_start(struct ppi_if *ppi);
> +static int ppi_stop(struct ppi_if *ppi);
> +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
> +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
> +
> +static const struct ppi_ops ppi_ops = {
> +	.attach_irq = ppi_attach_irq,
> +	.detach_irq = ppi_detach_irq,
> +	.start = ppi_start,
> +	.stop = ppi_stop,
> +	.set_params = ppi_set_params,
> +	.update_addr = ppi_update_addr,
> +};
> +
> +static irqreturn_t ppi_irq_err(int irq, void *dev_id)
> +{
> +	struct ppi_if *ppi = dev_id;
> +	const struct ppi_info *info = ppi->info;
> +	unsigned short status;
> +
> +	if (!strcmp(info->name, "ppi")) {
> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;
> +		status = bfin_read16(reg->status);
> +		if (printk_ratelimit())
> +			pr_info("%s: status = 0x%x\n", __func__, status);

You don't want this in the mainline version...

> +		bfin_write16(&reg->status, 0xff);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	if (request_dma(info->dma_ch, "PPI_DMA") < 0) {
> +		pr_err("Unable to allocate DMA channel for PPI\n");
> +		return -EBUSY;

Why don't you propagate return code from request_dma()?

> +	}
> +	set_dma_callback(info->dma_ch, handler, ppi);
> +
> +	if (request_irq(info->irq_err, ppi_irq_err, IRQF_DISABLED,

AFAIK, IRQF_DISABLED is gone from the kernel.

> +				"PPI ERROR", ppi)) {
> +		pr_err("Unable to allocate IRQ for PPI\n");
> +		free_dma(info->dma_ch);
> +		return -EBUSY;

ditto ret = request_irq()...

> +	}
> +	return 0;
> +}
> +
> +static void ppi_detach_irq(struct ppi_if *ppi)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	free_irq(info->irq_err, ppi);
> +	free_dma(info->dma_ch);
> +}
> +
> +static int ppi_start(struct ppi_if *ppi)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	/* enable DMA */
> +	enable_dma(info->dma_ch);
> +
> +	/* enable PPI */
> +	ppi->ppi_control |= PORT_EN;
> +	if (!strcmp(info->name, "ppi")) {
> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;

Cannot you declare base as "void __iomem *" or something similar to avoid 
all these casts?

> +		bfin_write16(&reg->control, ppi->ppi_control);
> +	}
> +
> +	SSYNC();
> +	return 0;
> +}
> +
> +static int ppi_stop(struct ppi_if *ppi)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	/* disable PPI */
> +	ppi->ppi_control &= ~PORT_EN;
> +	if (!strcmp(info->name, "ppi")) {

Wouldn't an "enum ppi_type" be better with the ability to do

	switch (type) {
	case PPI:
		...
	case EPPI:
		...
	}

?

> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;
> +		bfin_write16(&reg->control, ppi->ppi_control);
> +	}
> +
> +	/* disable DMA */
> +	clear_dma_irqstat(info->dma_ch);
> +	disable_dma(info->dma_ch);
> +
> +	SSYNC();
> +	return 0;
> +}
> +
> +static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
> +{
> +	const struct ppi_info *info = ppi->info;
> +
> +	ppi->bytes_per_line = params->width * params->bpp / 8;
> +	ppi->lines_per_frame = params->height;
> +
> +	/* config DMA */
> +	ppi->dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN);
> +	if (params->ppi_control & DMA32) {
> +		ppi->dma_config |= WDSIZE_32;
> +		set_dma_x_count(info->dma_ch, ppi->bytes_per_line >> 2);
> +		set_dma_x_modify(info->dma_ch, 4);
> +		set_dma_y_modify(info->dma_ch, 4);
> +	} else {
> +		ppi->dma_config |= WDSIZE_16;
> +		set_dma_x_count(info->dma_ch, ppi->bytes_per_line >> 1);
> +		set_dma_x_modify(info->dma_ch, 2);
> +		set_dma_y_modify(info->dma_ch, 2);
> +	}
> +	set_dma_y_count(info->dma_ch, ppi->lines_per_frame);
> +	set_dma_config(info->dma_ch, ppi->dma_config);
> +
> +	/* config PPI */
> +	ppi->ppi_control = params->ppi_control & ~PORT_EN;
> +	if (!strcmp(info->name, "ppi")) {
> +		struct bfin_ppi_regs __iomem *reg =
> +			(struct bfin_ppi_regs __iomem *)info->base;
> +		bfin_write16(&reg->control, ppi->ppi_control);
> +		bfin_write16(&reg->count, ppi->bytes_per_line - 1);
> +		bfin_write16(&reg->frame, ppi->lines_per_frame);
> +	}
> +
> +	SSYNC();
> +	return 0;
> +}
> +
> +static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
> +{
> +	set_dma_start_addr(ppi->info->dma_ch, addr);
> +}
> +
> +struct ppi_if *ppi_create_instance(const struct ppi_info *info)
> +{
> +	struct ppi_if *ppi;
> +
> +	if (!info || !info->name || !info->pin_req)
> +		return NULL;
> +
> +	if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
> +		pr_err("request peripheral failed\n");
> +		return NULL;
> +	}
> +
> +	ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
> +	if (!ppi) {
> +		peripheral_free_list(info->pin_req);
> +		pr_err("unable to allocate memory for ppi handle\n");
> +		return NULL;
> +	}
> +	ppi->ops = &ppi_ops;
> +	ppi->info = info;
> +
> +	pr_info("ppi probe success\n");
> +	return ppi;
> +}
> +
> +void ppi_delete_instance(struct ppi_if *ppi)
> +{
> +	peripheral_free_list(ppi->info->pin_req);
> +	kfree(ppi);
> +}
> diff --git a/include/media/blackfin/bfin_capture.h b/include/media/blackfin/bfin_capture.h
> new file mode 100644
> index 0000000..0905837
> --- /dev/null
> +++ b/include/media/blackfin/bfin_capture.h
> @@ -0,0 +1,33 @@
> +#ifndef _BFIN_CAPTURE_H_
> +#define _BFIN_CAPTURE_H_
> +
> +#include <linux/i2c.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/blackfin/ppi.h>

Looks like you only really need i2c.h, for others you can just 
forward-declare structs.

> +
> +struct bcap_route {
> +	u32 input;
> +	u32 output;
> +};
> +
> +struct bfin_capture_config {
> +	/* card name */
> +	char *card_name;
> +	/* inputs available at the sub device */
> +	struct v4l2_input *inputs;
> +	/* number of inputs supported */
> +	int num_inputs;
> +	/* routing information for each input */
> +	struct bcap_route *routes;
> +	/* i2c bus adapter no */
> +	int i2c_adapter_id;
> +	/* i2c subdevice board info */
> +	struct i2c_board_info board_info;
> +	/* ppi board info */
> +	const struct ppi_info *ppi_info;
> +	/* ppi control */
> +	unsigned short ppi_control;
> +};
> +
> +#endif
> diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h
> new file mode 100644
> index 0000000..4e7711a
> --- /dev/null
> +++ b/include/media/blackfin/ppi.h
> @@ -0,0 +1,63 @@
> +/*
> + * Analog Devices PPI header file
> + *
> + * Copyright (c) 2011 Analog Devices Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#ifndef _PPI_H_
> +#define _PPI_H_
> +
> +#include <linux/interrupt.h>
> +
> +struct ppi_if;
> +
> +struct ppi_params {
> +	int width;
> +	int height;
> +	int bpp;
> +	unsigned short ppi_control;
> +};
> +
> +struct ppi_ops {
> +	int (*attach_irq)(struct ppi_if *ppi, irq_handler_t handler);
> +	void (*detach_irq)(struct ppi_if *ppi);
> +	int (*start)(struct ppi_if *ppi);
> +	int (*stop)(struct ppi_if *ppi);
> +	int (*set_params)(struct ppi_if *ppi, struct ppi_params *params);
> +	void (*update_addr)(struct ppi_if *ppi, unsigned long addr);
> +};
> +
> +struct ppi_info {
> +	const char *name; /* ppi or eppi */
> +	int dma_ch;
> +	int irq_err;
> +	unsigned long base;
> +	const unsigned short *pin_req;
> +};
> +
> +struct ppi_if {
> +	int dma_config;
> +	int bytes_per_line;
> +	int lines_per_frame;
> +	unsigned short ppi_control;
> +	const struct ppi_ops *ops;
> +	const struct ppi_info *info;
> +	void *priv;
> +};
> +
> +struct ppi_if *ppi_create_instance(const struct ppi_info *info);
> +void ppi_delete_instance(struct ppi_if *ppi);
> +#endif
> -- 
> 1.7.0.4

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2011-09-30 15:07 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-19 20:59 [PATCH 1/4 v2][FOR 3.1] v4l2: add vb2_get_unmapped_area in vb2 core Scott Jiang
2011-09-19 20:59 ` [PATCH 2/4 v2][FOR 3.1] v4l2: add adv7183 decoder driver Scott Jiang
2011-09-26 13:37   ` Hans Verkuil
2011-09-19 20:59 ` [PATCH 3/4 v2][FOR 3.1] v4l2: add vs6624 sensor driver Scott Jiang
2011-09-26 13:39   ` Hans Verkuil
2011-09-19 20:59 ` [PATCH 4/4 v2][FOR 3.1] v4l2: add blackfin capture bridge driver Scott Jiang
2011-09-26 14:09   ` Hans Verkuil
2011-09-27  8:23     ` Scott Jiang
2011-09-27  9:42       ` Hans Verkuil
2011-09-27 10:00         ` Scott Jiang
2011-09-27 10:18           ` Hans Verkuil
2011-09-30 15:07   ` Guennadi Liakhovetski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.