All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/38] i.MX5/6 Video Capture
@ 2016-06-14 22:48 Steve Longerbeam
  2016-06-14 22:48 ` [PATCH 01/38] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
                   ` (40 more replies)
  0 siblings, 41 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:48 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Tested on imx6q SabreAuto with ADV7180, and imx6q SabreSD with
mipi-csi2 OV5640. There is device-tree support also for imx6qdl
SabreLite, but that is not tested. Also, this driver should
theoretically work on i.MX5 targets, but that is also untested.

Not run through v4l2-compliance yet, but that is in my queue.


Philipp Zabel (2):
  ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their
    connections
  media: imx: Add video switch

Steve Longerbeam (35):
  gpu: ipu-v3: Add Video Deinterlacer unit
  gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  gpu: ipu-v3: Add ipu_get_num()
  gpu: ipu-v3: Add IDMA channel linking support
  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  gpu: ipu-v3: Add VDI input IDMAC channels
  gpu: ipu-v3: Add ipu_csi_set_src()
  gpu: ipu-v3: Add ipu_ic_set_src()
  gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  gpu: ipu-v3: Fix IRT usage
  gpu: ipu-ic: Add complete image conversion support with tiling
  gpu: ipu-ic: allow multiple handles to ic
  gpu: ipu-v3: rename CSI client device
  ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
  ARM: dts: imx6-sabrelite: add video capture ports and connections
  ARM: dts: imx6-sabresd: add video capture ports and connections
  ARM: dts: imx6-sabreauto: create i2cmux for i2c3
  ARM: dts: imx6-sabreauto: add reset-gpios property for max7310
  ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
  ARM: dts: imx6-sabreauto: add video capture ports and connections
  ARM: dts: imx6qdl: add mem2mem device for sabre* boards
  gpio: pca953x: Add reset-gpios property
  clocksource/drivers/imx: add input capture support
  v4l: Add signal lock status to source change events
  media: Add camera interface driver for i.MX5/6
  media: imx: Add MIPI CSI-2 Receiver driver
  media: imx: Add support for MIPI CSI-2 OV5640
  media: imx: Add support for Parallel OV5642
  media: imx: Add support for ADV7180 Video Decoder
  media: adv7180: add power pin control
  media: adv7180: implement g_parm
  media: Add i.MX5/6 mem2mem driver
  ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers

Suresh Dhandapani (1):
  gpu: ipu-v3: Fix CSI0 blur in NTSC format

 Documentation/DocBook/media/v4l/vidioc-dqevent.xml |   12 +-
 Documentation/devicetree/bindings/media/imx.txt    |  449 ++
 Documentation/video4linux/imx_camera.txt           |  243 ++
 arch/arm/boot/dts/imx6dl-sabresd.dts               |   44 +
 arch/arm/boot/dts/imx6dl.dtsi                      |  183 +
 arch/arm/boot/dts/imx6q-sabreauto.dts              |    7 +
 arch/arm/boot/dts/imx6q-sabrelite.dts              |    6 +
 arch/arm/boot/dts/imx6q-sabresd.dts                |   22 +
 arch/arm/boot/dts/imx6q.dtsi                       |  120 +
 arch/arm/boot/dts/imx6qdl-sabreauto.dtsi           |  166 +-
 arch/arm/boot/dts/imx6qdl-sabrelite.dtsi           |   95 +
 arch/arm/boot/dts/imx6qdl-sabresd.dtsi             |  145 +-
 arch/arm/boot/dts/imx6qdl.dtsi                     |   13 +
 arch/arm/configs/imx_v6_v7_defconfig               |    2 +
 drivers/clocksource/timer-imx-gpt.c                |  463 ++-
 drivers/gpio/gpio-pca953x.c                        |   28 +
 drivers/gpu/ipu-v3/Makefile                        |    2 +-
 drivers/gpu/ipu-v3/ipu-common.c                    |  155 +-
 drivers/gpu/ipu-v3/ipu-cpmem.c                     |   13 +
 drivers/gpu/ipu-v3/ipu-csi.c                       |   36 +-
 drivers/gpu/ipu-v3/ipu-ic.c                        | 1769 +++++++-
 drivers/gpu/ipu-v3/ipu-prv.h                       |    7 +
 drivers/gpu/ipu-v3/ipu-vdi.c                       |  266 ++
 drivers/media/i2c/adv7180.c                        |   73 +
 drivers/staging/media/Kconfig                      |    2 +
 drivers/staging/media/Makefile                     |    1 +
 drivers/staging/media/imx/Kconfig                  |   35 +
 drivers/staging/media/imx/Makefile                 |    2 +
 drivers/staging/media/imx/capture/Kconfig          |   42 +
 drivers/staging/media/imx/capture/Makefile         |   10 +
 drivers/staging/media/imx/capture/adv7180.c        | 1533 +++++++
 drivers/staging/media/imx/capture/imx-camif.c      | 2496 +++++++++++
 drivers/staging/media/imx/capture/imx-camif.h      |  281 ++
 drivers/staging/media/imx/capture/imx-csi.c        |  195 +
 drivers/staging/media/imx/capture/imx-ic-prpenc.c  |  660 +++
 drivers/staging/media/imx/capture/imx-of.c         |  354 ++
 drivers/staging/media/imx/capture/imx-of.h         |   18 +
 drivers/staging/media/imx/capture/imx-smfc.c       |  505 +++
 drivers/staging/media/imx/capture/imx-vdic.c       |  994 +++++
 .../staging/media/imx/capture/imx-video-switch.c   |  348 ++
 drivers/staging/media/imx/capture/mipi-csi2.c      |  373 ++
 drivers/staging/media/imx/capture/ov5640-mipi.c    | 2318 +++++++++++
 drivers/staging/media/imx/capture/ov5642.c         | 4333 ++++++++++++++++++++
 drivers/staging/media/imx/m2m/Makefile             |    1 +
 drivers/staging/media/imx/m2m/imx-m2m.c            | 1049 +++++
 include/linux/mxc_icap.h                           |   20 +
 include/media/imx.h                                |   15 +
 include/uapi/Kbuild                                |    1 +
 include/uapi/linux/v4l2-controls.h                 |    8 +
 include/uapi/linux/videodev2.h                     |    1 +
 include/uapi/media/Kbuild                          |    3 +
 include/uapi/media/adv718x.h                       |   42 +
 include/uapi/media/imx.h                           |   22 +
 include/video/imx-ipu-v3.h                         |   96 +-
 54 files changed, 19946 insertions(+), 131 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/imx.txt
 create mode 100644 Documentation/video4linux/imx_camera.txt
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c
 create mode 100644 drivers/staging/media/imx/Kconfig
 create mode 100644 drivers/staging/media/imx/Makefile
 create mode 100644 drivers/staging/media/imx/capture/Kconfig
 create mode 100644 drivers/staging/media/imx/capture/Makefile
 create mode 100644 drivers/staging/media/imx/capture/adv7180.c
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.c
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.h
 create mode 100644 drivers/staging/media/imx/capture/imx-csi.c
 create mode 100644 drivers/staging/media/imx/capture/imx-ic-prpenc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.h
 create mode 100644 drivers/staging/media/imx/capture/imx-smfc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-vdic.c
 create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c
 create mode 100644 drivers/staging/media/imx/capture/mipi-csi2.c
 create mode 100644 drivers/staging/media/imx/capture/ov5640-mipi.c
 create mode 100644 drivers/staging/media/imx/capture/ov5642.c
 create mode 100644 drivers/staging/media/imx/m2m/Makefile
 create mode 100644 drivers/staging/media/imx/m2m/imx-m2m.c
 create mode 100644 include/linux/mxc_icap.h
 create mode 100644 include/media/imx.h
 create mode 100644 include/uapi/media/Kbuild
 create mode 100644 include/uapi/media/adv718x.h
 create mode 100644 include/uapi/media/imx.h

-- 
1.9.1


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

* [PATCH 01/38] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
@ 2016-06-14 22:48 ` Steve Longerbeam
  2016-06-14 22:48 ` [PATCH 02/38] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
                   ` (39 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:48 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds the Video Deinterlacer (VDIC) unit.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/Makefile     |   2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  11 ++
 drivers/gpu/ipu-v3/ipu-prv.h    |   6 +
 drivers/gpu/ipu-v3/ipu-vdi.c    | 266 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  27 ++++
 5 files changed, 311 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile
index 107ec23..aeba9dc 100644
--- a/drivers/gpu/ipu-v3/Makefile
+++ b/drivers/gpu/ipu-v3/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
 
 imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
-		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
+		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 99dcacf..30dc115 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -833,6 +833,14 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
 		goto err_ic;
 	}
 
+	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
+			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
+			   IPU_CONF_IC_INPUT);
+	if (ret) {
+		unit = "vdi";
+		goto err_vdi;
+	}
+
 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
 			  IPU_CONF_DI0_EN, ipu_clk);
 	if (ret) {
@@ -887,6 +895,8 @@ err_dc:
 err_di_1:
 	ipu_di_exit(ipu, 0);
 err_di_0:
+	ipu_vdi_exit(ipu);
+err_vdi:
 	ipu_ic_exit(ipu);
 err_ic:
 	ipu_csi_exit(ipu, 1);
@@ -971,6 +981,7 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
 	ipu_dc_exit(ipu);
 	ipu_di_exit(ipu, 1);
 	ipu_di_exit(ipu, 0);
+	ipu_vdi_exit(ipu);
 	ipu_ic_exit(ipu);
 	ipu_csi_exit(ipu, 1);
 	ipu_csi_exit(ipu, 0);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index bfb1e8a..845f64c 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -138,6 +138,7 @@ struct ipu_dc_priv;
 struct ipu_dmfc_priv;
 struct ipu_di;
 struct ipu_ic_priv;
+struct ipu_vdi;
 struct ipu_smfc_priv;
 
 struct ipu_devtype;
@@ -169,6 +170,7 @@ struct ipu_soc {
 	struct ipu_di		*di_priv[2];
 	struct ipu_csi		*csi_priv[2];
 	struct ipu_ic_priv	*ic_priv;
+	struct ipu_vdi          *vdi_priv;
 	struct ipu_smfc_priv	*smfc_priv;
 };
 
@@ -199,6 +201,10 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 		unsigned long base, unsigned long tpmem_base);
 void ipu_ic_exit(struct ipu_soc *ipu);
 
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module);
+void ipu_vdi_exit(struct ipu_soc *ipu);
+
 int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
 		unsigned long base, u32 module, struct clk *ipu_clk);
 void ipu_di_exit(struct ipu_soc *ipu, int id);
diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c
new file mode 100644
index 0000000..1303bcc
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-vdi.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2012 Mentor Graphics Inc.
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <uapi/linux/v4l2-mediabus.h>
+
+#include "ipu-prv.h"
+
+struct ipu_vdi {
+	void __iomem *base;
+	u32 module;
+	spinlock_t lock;
+	int use_count;
+	struct ipu_soc *ipu;
+};
+
+
+/* VDI Register Offsets */
+#define VDI_FSIZE 0x0000
+#define VDI_C     0x0004
+
+/* VDI Register Fields */
+#define VDI_C_CH_420             (0 << 1)
+#define VDI_C_CH_422             (1 << 1)
+#define VDI_C_MOT_SEL_MASK       (0x3 << 2)
+#define VDI_C_MOT_SEL_FULL       (2 << 2)
+#define VDI_C_MOT_SEL_LOW        (1 << 2)
+#define VDI_C_MOT_SEL_MED        (0 << 2)
+#define VDI_C_BURST_SIZE1_4      (3 << 4)
+#define VDI_C_BURST_SIZE2_4      (3 << 8)
+#define VDI_C_BURST_SIZE3_4      (3 << 12)
+#define VDI_C_BURST_SIZE_MASK    0xF
+#define VDI_C_BURST_SIZE1_OFFSET 4
+#define VDI_C_BURST_SIZE2_OFFSET 8
+#define VDI_C_BURST_SIZE3_OFFSET 12
+#define VDI_C_VWM1_SET_1         (0 << 16)
+#define VDI_C_VWM1_SET_2         (1 << 16)
+#define VDI_C_VWM1_CLR_2         (1 << 19)
+#define VDI_C_VWM3_SET_1         (0 << 22)
+#define VDI_C_VWM3_SET_2         (1 << 22)
+#define VDI_C_VWM3_CLR_2         (1 << 25)
+#define VDI_C_TOP_FIELD_MAN_1    (1 << 30)
+#define VDI_C_TOP_FIELD_AUTO_1   (1 << 31)
+
+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset)
+{
+	return readl(vdi->base + offset);
+}
+
+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value,
+				 unsigned int offset)
+{
+	writel(value, vdi->base + offset);
+}
+
+static void __ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	if (top_field_0)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+static void __ipu_vdi_set_motion(struct ipu_vdi *vdi,
+				 enum ipu_motion_sel motion_sel)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+
+	reg &= ~VDI_C_MOT_SEL_MASK;
+
+	switch (motion_sel) {
+	case MED_MOTION:
+		reg |= VDI_C_MOT_SEL_MED;
+		break;
+	case HIGH_MOTION:
+		reg |= VDI_C_MOT_SEL_FULL;
+		break;
+	default:
+		reg |= VDI_C_MOT_SEL_LOW;
+		break;
+	}
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres,
+		   u32 field, enum ipu_motion_sel motion_sel)
+{
+	unsigned long flags;
+	u32 pixel_fmt, reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ((yres - 1) << 16) | (xres - 1);
+	ipu_vdi_write(vdi, reg, VDI_FSIZE);
+
+	/*
+	 * Full motion, only vertical filter is used.
+	 * Burst size is 4 accesses
+	 */
+	if (code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+	    code == MEDIA_BUS_FMT_UYVY8_1X16 ||
+	    code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+	    code == MEDIA_BUS_FMT_YUYV8_1X16)
+		pixel_fmt = VDI_C_CH_422;
+	else
+		pixel_fmt = VDI_C_CH_420;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	reg |= pixel_fmt;
+	reg |= VDI_C_BURST_SIZE2_4;
+	reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2;
+	reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2;
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	if (field == V4L2_FIELD_INTERLACED_TB)
+		__ipu_vdi_set_top_field_man(vdi, false);
+	else if (field == V4L2_FIELD_INTERLACED_BT)
+		__ipu_vdi_set_top_field_man(vdi, true);
+
+	__ipu_vdi_set_motion(vdi, motion_sel);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_setup);
+
+void ipu_vdi_unsetup(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+	ipu_vdi_write(vdi, 0, VDI_FSIZE);
+	ipu_vdi_write(vdi, 0, VDI_C);
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
+
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+	u32 reg;
+	u32 mask_reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+	if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
+
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
+{
+	ipu_set_vdi_src_mux(vdi->ipu, csi);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_set_src);
+
+int ipu_vdi_enable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	if (!vdi->use_count)
+		ipu_module_enable(vdi->ipu, vdi->module);
+
+	vdi->use_count++;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_enable);
+
+int ipu_vdi_disable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	vdi->use_count--;
+
+	if (!vdi->use_count)
+		ipu_module_disable(vdi->ipu, vdi->module);
+
+	if (vdi->use_count < 0)
+		vdi->use_count = 0;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_disable);
+
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu)
+{
+	return ipu->vdi_priv;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_get);
+
+void ipu_vdi_put(struct ipu_vdi *vdi)
+{
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_put);
+
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module)
+{
+	struct ipu_vdi *vdi;
+
+	vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL);
+	if (!vdi)
+		return -ENOMEM;
+
+	ipu->vdi_priv = vdi;
+
+	spin_lock_init(&vdi->lock);
+	vdi->module = module;
+	vdi->base = devm_ioremap(dev, base, PAGE_SIZE);
+	if (!vdi->base)
+		return -ENOMEM;
+
+	dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base);
+	vdi->ipu = ipu;
+
+	return 0;
+}
+
+void ipu_vdi_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 3a2a794..22662a1 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -80,6 +80,16 @@ enum ipu_color_space {
 	IPUV3_COLORSPACE_UNKNOWN,
 };
 
+/*
+ * Enumeration of VDI MOTION select
+ */
+enum ipu_motion_sel {
+	MOTION_NONE = 0,
+	LOW_MOTION,
+	MED_MOTION,
+	HIGH_MOTION,
+};
+
 struct ipuv3_channel;
 
 enum ipu_channel_irq {
@@ -320,6 +330,23 @@ void ipu_ic_put(struct ipu_ic *ic);
 void ipu_ic_dump(struct ipu_ic *ic);
 
 /*
+ * IPU Video De-Interlacer (vdi) functions
+ */
+struct ipu_vdi;
+void ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0);
+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel);
+void ipu_vdi_setup(struct ipu_vdi *vdi,
+		   u32 code, int xres, int yres, u32 field,
+		   enum ipu_motion_sel motion_sel);
+void ipu_vdi_unsetup(struct ipu_vdi *vdi);
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi);
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi);
+int ipu_vdi_enable(struct ipu_vdi *vdi);
+int ipu_vdi_disable(struct ipu_vdi *vdi);
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu);
+void ipu_vdi_put(struct ipu_vdi *vdi);
+
+/*
  * IPU Sensor Multiple FIFO Controller (SMFC) functions
  */
 struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno);
-- 
1.9.1


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

* [PATCH 02/38] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
  2016-06-14 22:48 ` [PATCH 01/38] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
@ 2016-06-14 22:48 ` Steve Longerbeam
  2016-06-14 22:48 ` [PATCH 03/38] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
                   ` (38 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:48 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index 6494a4d..a36c35e 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
 
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
+{
+	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
+	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
+
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 22662a1..904fd12 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -194,6 +194,7 @@ void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres);
 void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch);
 void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
-- 
1.9.1


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

* [PATCH 03/38] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
  2016-06-14 22:48 ` [PATCH 01/38] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
  2016-06-14 22:48 ` [PATCH 02/38] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
@ 2016-06-14 22:48 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 04/38] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
                   ` (37 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:48 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_cpmem_get_burstsize().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 6 ++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 7 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index a36c35e..fcb7dc8 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -275,6 +275,12 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
 
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch)
+{
+	return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize);
+
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 904fd12..60540ead 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -197,6 +197,7 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
 void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
 void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
 void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
-- 
1.9.1


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

* [PATCH 04/38] gpu: ipu-v3: Add ipu_get_num()
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (2 preceding siblings ...)
  2016-06-14 22:48 ` [PATCH 03/38] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 05/38] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
                   ` (36 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds of-alias id to ipu_soc and retrieve with ipu_get_num().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 8 ++++++++
 drivers/gpu/ipu-v3/ipu-prv.h    | 1 +
 include/video/imx-ipu-v3.h      | 1 +
 3 files changed, 10 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 30dc115..49af121 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -45,6 +45,12 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
 	writel(value, ipu->cm_reg + offset);
 }
 
+int ipu_get_num(struct ipu_soc *ipu)
+{
+	return ipu->id;
+}
+EXPORT_SYMBOL_GPL(ipu_get_num);
+
 void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
 {
 	u32 val;
@@ -1220,6 +1226,7 @@ static int ipu_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id =
 			of_match_device(imx_ipu_dt_ids, &pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
 	struct ipu_soc *ipu;
 	struct resource *res;
 	unsigned long ipu_base;
@@ -1248,6 +1255,7 @@ static int ipu_probe(struct platform_device *pdev)
 		ipu->channel[i].ipu = ipu;
 	ipu->devtype = devtype;
 	ipu->ipu_type = devtype->type;
+	ipu->id = of_alias_get_id(np, "ipu");
 
 	spin_lock_init(&ipu->lock);
 	mutex_init(&ipu->channel_lock);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index 845f64c..02057d8 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -153,6 +153,7 @@ struct ipu_soc {
 	void __iomem		*cm_reg;
 	void __iomem		*idmac_reg;
 
+	int			id;
 	int			usecount;
 
 	struct clk		*clk;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 60540ead..b174f8a 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -148,6 +148,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 /*
  * IPU Common functions
  */
+int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
 void ipu_dump(struct ipu_soc *ipu);
-- 
1.9.1


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

* [PATCH 05/38] gpu: ipu-v3: Add IDMA channel linking support
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (3 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 04/38] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 06/38] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
                   ` (35 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds functions to link and unlink IDMAC source channels to sink
channels.

So far the following links are supported:

IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP

More links can be added to the idmac_link_info[] array.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 112 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |   3 ++
 2 files changed, 115 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 49af121..6d1676e 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,118 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+
+/* IDMAC Channel Linking */
+
+struct idmac_link_reg_info {
+	int chno;
+	u32 reg;
+	int shift;
+	int bits;
+	u32 sel;
+};
+
+struct idmac_link_info {
+	struct idmac_link_reg_info src;
+	struct idmac_link_reg_info sink;
+};
+
+static const struct idmac_link_info idmac_link_info[] = {
+	{
+		.src  = { 20, IPU_FS_PROC_FLOW1,  0, 4, 7 },
+		.sink = { 45, IPU_FS_PROC_FLOW2,  0, 4, 1 },
+	}, {
+		.src =  { 21, IPU_FS_PROC_FLOW1,  8, 4, 8 },
+		.sink = { 46, IPU_FS_PROC_FLOW2,  4, 4, 1 },
+	}, {
+		.src =  { 22, IPU_FS_PROC_FLOW1, 16, 4, 5 },
+		.sink = { 47, IPU_FS_PROC_FLOW2, 12, 4, 3 },
+	},
+};
+
+static const struct idmac_link_info *find_idmac_link_info(
+	struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(idmac_link_info); i++) {
+		if (src->num == idmac_link_info[i].src.chno &&
+		    sink->num == idmac_link_info[i].sink.chno)
+			return &idmac_link_info[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Links an IDMAC source channel to a sink channel.
+ */
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	src_reg |= (link->src.sel << link->src.shift);
+
+	sink_reg &= ~sink_mask;
+	sink_reg |= (link->sink.sel << link->sink.shift);
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_link);
+
+/*
+ * Unlinks IDMAC source and sink channels.
+ */
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	sink_reg &= ~sink_mask;
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
+
 struct ipu_devtype {
 	const char *name;
 	unsigned long cm_ofs;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index b174f8a..0a39c64 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -128,6 +128,7 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_ROT_VF_MEM		49
 #define IPUV3_CHANNEL_ROT_PP_MEM		50
 #define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA		51
+#define IPUV3_NUM_CHANNELS			64
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
@@ -171,6 +172,8 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
 bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num);
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink);
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink);
 
 /*
  * IPU Channel Parameter Memory (cpmem) functions
-- 
1.9.1


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

* [PATCH 06/38] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (4 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 05/38] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 07/38] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
                   ` (34 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_set_vdi_src_mux() that selects the VDIC input
(from CSI or memory).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  1 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 6d1676e..374100e 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+/*
+ * Set the source for the VDIC. Selects either from CSI[01] or memory.
+ */
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+	val &= ~(0x3 << 28);
+	if (csi)
+		val |= (0x01 << 28);
+	ipu_cm_write(ipu, val, IPU_FS_PROC_FLOW1);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_set_vdi_src_mux);
+
 
 /* IDMAC Channel Linking */
 
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 0a39c64..586979e 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -152,6 +152,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi);
 void ipu_dump(struct ipu_soc *ipu);
 
 /*
-- 
1.9.1


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

* [PATCH 07/38] gpu: ipu-v3: Add VDI input IDMAC channels
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (5 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 06/38] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 08/38] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
                   ` (33 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds the VDIC field input IDMAC channels. These channels
transfer fields F(n-1), F(n), and F(N+1) from memory to
the VDIC (channels 8, 9, 10 respectively).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 include/video/imx-ipu-v3.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 586979e..2302fc5 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -107,6 +107,9 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_CSI2			 2
 #define IPUV3_CHANNEL_CSI3			 3
 #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
+#define IPUV3_CHANNEL_MEM_VDI_P			 8
+#define IPUV3_CHANNEL_MEM_VDI			 9
+#define IPUV3_CHANNEL_MEM_VDI_N			10
 #define IPUV3_CHANNEL_MEM_IC_PP			11
 #define IPUV3_CHANNEL_MEM_IC_PRP_VF		12
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF		14
-- 
1.9.1


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

* [PATCH 08/38] gpu: ipu-v3: Add ipu_csi_set_src()
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (6 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 07/38] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 09/38] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
                   ` (32 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_csi_set_src() which is just a wrapper around
ipu_set_csi_src_mux().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 8 ++++++++
 include/video/imx-ipu-v3.h   | 1 +
 2 files changed, 9 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 06631ac..336dc06 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -609,6 +609,14 @@ int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
 }
 EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
 
+int ipu_csi_set_src(struct ipu_csi *csi, u32 vc, bool select_mipi_csi2)
+{
+	ipu_set_csi_src_mux(csi->ipu, csi->id, select_mipi_csi2);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_src);
+
 int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest)
 {
 	unsigned long flags;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 2302fc5..57b487d 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -301,6 +301,7 @@ int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
 			      struct v4l2_mbus_framefmt *mbus_fmt);
 int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
 			  u32 max_ratio, u32 id);
+int ipu_csi_set_src(struct ipu_csi *csi, u32 vc, bool select_mipi_csi2);
 int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest);
 int ipu_csi_enable(struct ipu_csi *csi);
 int ipu_csi_disable(struct ipu_csi *csi);
-- 
1.9.1


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

* [PATCH 09/38] gpu: ipu-v3: Add ipu_ic_set_src()
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (7 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 08/38] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 10/38] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
                   ` (31 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_ic_set_src() which is just aa wrapper around
ipu_set_ic_src_mux().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 10 ++++++++++
 include/video/imx-ipu-v3.h  |  1 +
 2 files changed, 11 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 1dcb96c..f306a9c 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -629,6 +629,16 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	ipu_set_ic_src_mux(priv->ipu, csi_id, vdi);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_set_src);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 57b487d..8f77ddb 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -334,6 +334,7 @@ void ipu_ic_task_disable(struct ipu_ic *ic);
 int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
+int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1


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

* [PATCH 10/38] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (8 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 09/38] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 11/38] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
                   ` (30 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Set the sensor full frame based on whether the passed in mbus_fmt
is 720x480 (NTSC) or 720x576 (PAL).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 336dc06..07c7091 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -365,10 +365,14 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 {
 	struct ipu_csi_bus_config cfg;
 	unsigned long flags;
-	u32 data = 0;
+	u32 width, height, data = 0;
 
 	fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
 
+	/* set default sensor frame width and height */
+	width = mbus_fmt->width;
+	height = mbus_fmt->height;
+
 	/* Set the CSI_SENS_CONF register remaining fields */
 	data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
 		cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
@@ -386,11 +390,6 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 	ipu_csi_write(csi, data, CSI_SENS_CONF);
 
-	/* Setup sensor frame size */
-	ipu_csi_write(csi,
-		      (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
-		      CSI_SENS_FRM_SIZE);
-
 	/* Set CCIR registers */
 
 	switch (cfg.clk_mode) {
@@ -408,11 +407,12 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
 			 * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
 			 */
+			height = 625; /* framelines for PAL */
+
 			ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
-
 		} else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
 			/*
 			 * NTSC case
@@ -422,6 +422,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
 			 * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
 			 */
+			height = 525; /* framelines for NTSC */
+
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
@@ -447,6 +449,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 		break;
 	}
 
+	/* Setup sensor frame size */
+	ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
+		      CSI_SENS_FRM_SIZE);
+
 	dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
 		ipu_csi_read(csi, CSI_SENS_CONF));
 	dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
-- 
1.9.1


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

* [PATCH 11/38] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (9 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 10/38] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 12/38] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
                   ` (29 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

The CSI data format was being programmed incorrectly for the
1x16 media bus formats. The CSI data format for 16-bit must
be bayer/generic (CSI_SENS_CONF_DATA_FMT_BAYER).

Suggested-by: Carsten Resch <Carsten.Resch@de.bosch.com>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 07c7091..0eac28c 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
 		cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 		break;
 	case MEDIA_BUS_FMT_UYVY8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
-		cfg->mipi_dt = MIPI_DT_YUV422;
-		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
-		break;
 	case MEDIA_BUS_FMT_YUYV8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 		cfg->mipi_dt = MIPI_DT_YUV422;
 		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
 		break;
-- 
1.9.1


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

* [PATCH 12/38] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (10 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 11/38] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 13/38] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
                   ` (28 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Suresh Dhandapani

From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>

This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
0x40596 to 0x405A6. The change is related to the Start of field 1
first blanking line command bit[5-3] for NTSC format only. This
change is dependent with ADV chip where the NEWAVMODE is set to 0
in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
codes generated to suit analog devices encoders".

Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 0eac28c..ec81958 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
-			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
+			ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
 		} else {
 			dev_err(csi->ipu->dev,
-- 
1.9.1


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

* [PATCH 13/38] gpu: ipu-v3: Fix IRT usage
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (11 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 12/38] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 14/38] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
                   ` (27 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

There can be multiple IC tasks using the IRT, so the IRT needs
a separate use counter. Create a private ipu_irt_enable() to
enable the IRT module when any IC task requires rotation, and
ipu_irt_disable() when a task no longer needs the IRT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 43 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index f306a9c..5329bfe 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -160,6 +160,7 @@ struct ipu_ic_priv {
 	spinlock_t lock;
 	struct ipu_soc *ipu;
 	int use_count;
+	int irt_use_count;
 	struct ipu_ic task[IC_NUM_TASKS];
 };
 
@@ -379,8 +380,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic)
 
 	ipu_ic_write(ic, ic_conf, IC_CONF);
 
-	ic->rotation = ic->graphics = false;
-
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
@@ -639,22 +638,44 @@ int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_ic_set_src);
 
+static void ipu_irt_enable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	if (!priv->irt_use_count)
+		ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN);
+
+	priv->irt_use_count++;
+}
+
+static void ipu_irt_disable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	priv->irt_use_count--;
+
+	if (!priv->irt_use_count)
+		ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN);
+
+	if (priv->irt_use_count < 0)
+		priv->irt_use_count = 0;
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	if (ic->rotation)
-		module |= IPU_CONF_ROT_EN;
-
 	if (!priv->use_count)
-		ipu_module_enable(priv->ipu, module);
+		ipu_module_enable(priv->ipu, IPU_CONF_IC_EN);
 
 	priv->use_count++;
 
+	if (ic->rotation)
+		ipu_irt_enable(ic);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -665,18 +686,22 @@ int ipu_ic_disable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
 	priv->use_count--;
 
 	if (!priv->use_count)
-		ipu_module_disable(priv->ipu, module);
+		ipu_module_disable(priv->ipu, IPU_CONF_IC_EN);
 
 	if (priv->use_count < 0)
 		priv->use_count = 0;
 
+	if (ic->rotation)
+		ipu_irt_disable(ic);
+
+	ic->rotation = ic->graphics = false;
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
-- 
1.9.1


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

* [PATCH 14/38] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (12 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 13/38] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 15/38] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
                   ` (26 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This patch implements complete image conversion support to ipu-ic,
with tiling to support scaling to and from images up to 4096x4096.
Image rotation is also supported.

The internal API is subsystem agnostic (no V4L2 dependency except
for the use of V4L2 fourcc pixel formats).

Callers prepare for image conversion by calling
ipu_image_convert_prepare(), which initializes the parameters of
the conversion. The caller passes in the ipu_ic task to use for
the conversion, the input and output image formats, a rotation mode,
and a completion callback and completion context pointer:

struct image_converter_ctx *
ipu_image_convert_prepare(struct ipu_ic *ic,
                          struct ipu_image *in, struct ipu_image *out,
                          enum ipu_rotate_mode rot_mode,
                          image_converter_cb_t complete,
                          void *complete_context);

The caller is given a new conversion context that must be passed to
the further APIs:

struct image_converter_run *
ipu_image_convert_run(struct image_converter_ctx *ctx,
                      dma_addr_t in_phys, dma_addr_t out_phys);

This queues a new image conversion request to a run queue, and
starts the conversion immediately if the run queue is empty. Only
the physaddr's of the input and output image buffers are needed,
since the conversion context was created previously with
ipu_image_convert_prepare(). Returns a new run object pointer. When
the conversion completes, the run pointer is returned to the
completion callback.

void image_convert_abort(struct image_converter_ctx *ctx);

This will abort any active or pending conversions for this context.
Any currently active or pending runs belonging to this context are
returned via the completion callback with an error status.

void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);

Unprepares the conversion context. Any active or pending runs will
be aborted by calling image_convert_abort().
---
 drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |   57 +-
 2 files changed, 1736 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 5329bfe..f6a1125 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -17,6 +17,8 @@
 #include <linux/bitrev.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
 #include "ipu-prv.h"
 
 /* IC Register Offsets */
@@ -82,6 +84,40 @@
 #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
 #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
 
+/*
+ * The IC Resizer has a restriction that the output frame from the
+ * resizer must be 1024 or less in both width (pixels) and height
+ * (lines).
+ *
+ * The image conversion support attempts to split up a conversion when
+ * the desired output (converted) frame resolution exceeds the IC resizer
+ * limit of 1024 in either dimension.
+ *
+ * If either dimension of the output frame exceeds the limit, the
+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum
+ * of 4*4 or 16 tiles. A conversion is then carried out for each
+ * tile (but taking care to pass the full frame stride length to
+ * the DMA channel's parameter memory!). IDMA double-buffering is used
+ * to convert each tile back-to-back when possible (see note below
+ * when double_buffering boolean is set).
+ *
+ * Note that the input frame must be split up into the same number
+ * of tiles as the output frame.
+ */
+#define MAX_STRIPES_W    4
+#define MAX_STRIPES_H    4
+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
+
+#define MIN_W     128
+#define MIN_H     128
+#define MAX_W     4096
+#define MAX_H     4096
+
+enum image_convert_type {
+	IMAGE_CONVERT_IN = 0,
+	IMAGE_CONVERT_OUT,
+};
+
 struct ic_task_regoffs {
 	u32 rsc;
 	u32 tpmem_csc[2];
@@ -96,6 +132,16 @@ struct ic_task_bitfields {
 	u32 ic_cmb_galpha_bit;
 };
 
+struct ic_task_channels {
+	int in;
+	int out;
+	int rot_in;
+	int rot_out;
+	int vdi_in_p;
+	int vdi_in;
+	int vdi_in_n;
+};
+
 static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
 	[IC_TASK_ENCODER] = {
 		.rsc = IC_PRP_ENC_RSC,
@@ -138,12 +184,159 @@ static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
 	},
 };
 
+static const struct ic_task_channels ic_task_ch[IC_NUM_TASKS] = {
+	[IC_TASK_ENCODER] = {
+		.out = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_ENC,
+		.rot_out = IPUV3_CHANNEL_ROT_ENC_MEM,
+	},
+	[IC_TASK_VIEWFINDER] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+		.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_P,
+		.vdi_in = IPUV3_CHANNEL_MEM_VDI,
+		.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_N,
+	},
+	[IC_TASK_POST_PROCESSOR] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PP,
+		.out = IPUV3_CHANNEL_IC_PP_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
+		.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
+	},
+};
+
+struct ipu_ic_dma_buf {
+	void          *virt;
+	dma_addr_t    phys;
+	unsigned long len;
+};
+
+/* dimensions of one tile */
+struct ipu_ic_tile {
+	unsigned int width;
+	unsigned int height;
+	/* size and strides are in bytes */
+	unsigned int size;
+	unsigned int stride;
+	unsigned int rot_stride;
+};
+
+struct ipu_ic_tile_off {
+	/* start Y or packed offset of this tile */
+	u32     offset;
+	/* offset from start to tile in U plane, for planar formats */
+	u32     u_off;
+	/* offset from start to tile in V plane, for planar formats */
+	u32     v_off;
+};
+
+struct ipu_ic_pixfmt {
+	char	*name;
+	u32	fourcc;        /* V4L2 fourcc */
+	int     bpp;           /* total bpp */
+	int     y_depth;       /* depth of Y plane for planar formats */
+	int     uv_width_dec;  /* decimation in width for U/V planes */
+	int     uv_height_dec; /* decimation in height for U/V planes */
+	bool    uv_swapped;    /* U and V planes are swapped */
+	bool    uv_packed;     /* partial planar (U and V in same plane) */
+};
+
+struct ipu_ic_image {
+	struct ipu_image base;
+	enum image_convert_type type;
+
+	const struct ipu_ic_pixfmt *fmt;
+	unsigned int stride;
+
+	/* # of rows (horizontal stripes) if dest height is > 1024 */
+	unsigned int num_rows;
+	/* # of columns (vertical stripes) if dest width is > 1024 */
+	unsigned int num_cols;
+
+	struct ipu_ic_tile tile;
+	struct ipu_ic_tile_off tile_off[MAX_TILES];
+};
+
+struct image_converter_ctx;
+struct image_converter;
 struct ipu_ic_priv;
+struct ipu_ic;
+
+struct image_converter_run {
+	struct image_converter_ctx *ctx;
+
+	dma_addr_t in_phys;
+	dma_addr_t out_phys;
+
+	int status;
+
+	struct list_head list;
+};
+
+struct image_converter_ctx {
+	struct image_converter *cvt;
+
+	image_converter_cb_t complete;
+	void *complete_context;
+
+	/* Source/destination image data and rotation mode */
+	struct ipu_ic_image in;
+	struct ipu_ic_image out;
+	enum ipu_rotate_mode rot_mode;
+
+	/* intermediate buffer for rotation */
+	struct ipu_ic_dma_buf rot_intermediate[2];
+
+	/* current buffer number for double buffering */
+	int cur_buf_num;
+
+	bool aborting;
+	struct completion aborted;
+
+	/* can we use double-buffering for this conversion operation? */
+	bool double_buffering;
+	/* num_rows * num_cols */
+	unsigned int num_tiles;
+	/* next tile to process */
+	unsigned int next_tile;
+	/* where to place converted tile in dest image */
+	unsigned int out_tile_map[MAX_TILES];
+
+	struct list_head list;
+};
+
+struct image_converter {
+	struct ipu_ic *ic;
+
+	struct ipuv3_channel *in_chan;
+	struct ipuv3_channel *out_chan;
+	struct ipuv3_channel *rotation_in_chan;
+	struct ipuv3_channel *rotation_out_chan;
+
+	/* the IPU end-of-frame irqs */
+	int out_eof_irq;
+	int rot_out_eof_irq;
+
+	spinlock_t irqlock;
+
+	/* list of convert contexts */
+	struct list_head ctx_list;
+	/* queue of conversion runs */
+	struct list_head pending_q;
+	/* queue of completed runs */
+	struct list_head done_q;
+
+	/* the current conversion run */
+	struct image_converter_run *current_run;
+};
 
 struct ipu_ic {
 	enum ipu_ic_task task;
 	const struct ic_task_regoffs *reg;
 	const struct ic_task_bitfields *bit;
+	const struct ic_task_channels *ch;
 
 	enum ipu_color_space in_cs, g_in_cs;
 	enum ipu_color_space out_cs;
@@ -151,6 +344,8 @@ struct ipu_ic {
 	bool rotation;
 	bool in_use;
 
+	struct image_converter cvt;
+
 	struct ipu_ic_priv *priv;
 };
 
@@ -619,7 +814,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
 	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
 
-	if (rot >= IPU_ROTATE_90_RIGHT)
+	if (ipu_rot_mode_is_irt(rot))
 		ic->rotation = true;
 
 unlock:
@@ -661,6 +856,1480 @@ static void ipu_irt_disable(struct ipu_ic *ic)
 		priv->irt_use_count = 0;
 }
 
+/*
+ * Complete image conversion support follows
+ */
+
+static const struct ipu_ic_pixfmt ipu_ic_formats[] = {
+	{
+		.name	= "RGB565",
+		.fourcc	= V4L2_PIX_FMT_RGB565,
+		.bpp    = 16,
+	}, {
+		.name	= "RGB24",
+		.fourcc	= V4L2_PIX_FMT_RGB24,
+		.bpp    = 24,
+	}, {
+		.name	= "BGR24",
+		.fourcc	= V4L2_PIX_FMT_BGR24,
+		.bpp    = 24,
+	}, {
+		.name	= "RGB32",
+		.fourcc	= V4L2_PIX_FMT_RGB32,
+		.bpp    = 32,
+	}, {
+		.name	= "BGR32",
+		.fourcc	= V4L2_PIX_FMT_BGR32,
+		.bpp    = 32,
+	}, {
+		.name	= "4:2:2 packed, YUYV",
+		.fourcc	= V4L2_PIX_FMT_YUYV,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:2 packed, UYVY",
+		.fourcc	= V4L2_PIX_FMT_UYVY,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:0 planar, YUV",
+		.fourcc	= V4L2_PIX_FMT_YUV420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+	}, {
+		.name	= "4:2:0 planar, YVU",
+		.fourcc	= V4L2_PIX_FMT_YVU420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_swapped = true,
+	}, {
+		.name   = "4:2:0 partial planar, NV12",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_packed = true,
+	}, {
+		.name   = "4:2:2 planar, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name   = "4:2:2 partial planar, NV16",
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+		.uv_packed = true,
+	},
+};
+
+static const struct ipu_ic_pixfmt *ipu_ic_get_format(u32 fourcc)
+{
+	const struct ipu_ic_pixfmt *ret = NULL;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_ic_formats); i++) {
+		if (ipu_ic_formats[i].fourcc == fourcc) {
+			ret = &ipu_ic_formats[i];
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void ipu_ic_dump_format(struct image_converter_ctx *ctx,
+			       struct ipu_ic_image *ic_image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev,
+		"ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
+		ctx,
+		ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
+		ic_image->base.pix.width, ic_image->base.pix.height,
+		ic_image->num_cols, ic_image->num_rows,
+		ic_image->tile.width, ic_image->tile.height,
+		ic_image->fmt->fourcc & 0xff,
+		(ic_image->fmt->fourcc >> 8) & 0xff,
+		(ic_image->fmt->fourcc >> 16) & 0xff,
+		(ic_image->fmt->fourcc >> 24) & 0xff);
+}
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc)
+{
+	const struct ipu_ic_pixfmt *fmt;
+
+	if (index >= (int)ARRAY_SIZE(ipu_ic_formats))
+		return -EINVAL;
+
+	/* Format found */
+	fmt = &ipu_ic_formats[index];
+	*desc = fmt->name;
+	*fourcc = fmt->fourcc;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
+
+static void ipu_ic_free_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf)
+{
+	if (buf->virt)
+		dma_free_coherent(priv->ipu->dev,
+				  buf->len, buf->virt, buf->phys);
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int ipu_ic_alloc_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf,
+				int size)
+{
+	unsigned long newlen = PAGE_ALIGN(size);
+
+	if (buf->virt) {
+		if (buf->len == newlen)
+			return 0;
+		ipu_ic_free_dma_buf(priv, buf);
+	}
+
+	buf->len = newlen;
+	buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static inline int ipu_ic_num_stripes(int dim)
+{
+	if (dim <= 1024)
+		return 1;
+	else if (dim <= 2048)
+		return 2;
+	else
+		return 4;
+}
+
+static void ipu_ic_calc_tile_dimensions(struct image_converter_ctx *ctx,
+					struct ipu_ic_image *image)
+{
+	struct ipu_ic_tile *tile = &image->tile;
+
+	tile->height = image->base.pix.height / image->num_rows;
+	tile->width = image->base.pix.width / image->num_cols;
+	tile->size = ((tile->height * image->fmt->bpp) >> 3) * tile->width;
+
+	if (image->fmt->y_depth) {
+		tile->stride = (image->fmt->y_depth * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->y_depth * tile->height) >> 3;
+	} else {
+		tile->stride = (image->fmt->bpp * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->bpp * tile->height) >> 3;
+	}
+}
+
+/*
+ * Use the rotation transformation to find the tile coordinates
+ * (row, col) of a tile in the destination frame that corresponds
+ * to the given tile coordinates of a source frame. The destination
+ * coordinate is then converted to a tile index.
+ */
+static int ipu_ic_transform_tile_index(struct image_converter_ctx *ctx,
+				       int src_row, int src_col)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	int cos, sin, dst_row, dst_col;
+
+	/* with no rotation it's a 1:1 mapping */
+	if (ctx->rot_mode == IPU_ROTATE_NONE)
+		return src_row * s_image->num_cols + src_col;
+
+	if (ctx->rot_mode & IPU_ROT_BIT_90) {
+		cos = 0;
+		sin = 1;
+	} else {
+		cos = 1;
+		sin = 0;
+	}
+
+	/*
+	 * before doing the transform, first we have to translate
+	 * source row,col for an origin in the center of s_image
+	 */
+	src_row *= 2;
+	src_col *= 2;
+	src_row -= s_image->num_rows - 1;
+	src_col -= s_image->num_cols - 1;
+
+	/* do the rotation transform */
+	dst_col = src_col * cos - src_row * sin;
+	dst_row = src_col * sin + src_row * cos;
+
+	/* apply flip */
+	if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
+		dst_col = -dst_col;
+	if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
+		dst_row = -dst_row;
+
+	dev_dbg(priv->ipu->dev, "ctx %p: [%d,%d] --> [%d,%d]\n",
+		ctx, src_col, src_row, dst_col, dst_row);
+
+	/*
+	 * finally translate dest row,col using an origin in upper
+	 * left of d_image
+	 */
+	dst_row += d_image->num_rows - 1;
+	dst_col += d_image->num_cols - 1;
+	dst_row /= 2;
+	dst_col /= 2;
+
+	return dst_row * d_image->num_cols + dst_col;
+}
+
+/*
+ * Fill the out_tile_map[] with transformed destination tile indeces.
+ */
+static void ipu_ic_calc_out_tile_map(struct image_converter_ctx *ctx)
+{
+	struct ipu_ic_image *s_image = &ctx->in;
+	unsigned int row, col, tile = 0;
+
+	for (row = 0; row < s_image->num_rows; row++) {
+		for (col = 0; col < s_image->num_cols; col++) {
+			ctx->out_tile_map[tile] =
+				ipu_ic_transform_tile_index(ctx, row, col);
+			tile++;
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_planar(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 H, w, h, y_depth, y_stride, uv_stride;
+	u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
+	u32 y_row_off, y_col_off, y_off;
+	u32 y_size, uv_size;
+
+	/* setup some convenience vars */
+	H = image->base.pix.height;
+	w = image->tile.width;
+	h = image->tile.height;
+
+	y_depth = fmt->y_depth;
+	y_stride = image->stride;
+	uv_stride = y_stride / fmt->uv_width_dec;
+	if (fmt->uv_packed)
+		uv_stride *= 2;
+
+	y_size = H * y_stride;
+	uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
+
+	for (row = 0; row < image->num_rows; row++) {
+		y_row_off = row * h * y_stride;
+		uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
+
+		for (col = 0; col < image->num_cols; col++) {
+			y_col_off = (col * w * y_depth) >> 3;
+			uv_col_off = y_col_off / fmt->uv_width_dec;
+			if (fmt->uv_packed)
+				uv_col_off *= 2;
+
+			y_off = y_row_off + y_col_off;
+			uv_off = uv_row_off + uv_col_off;
+
+			u_off = y_size - y_off + uv_off;
+			v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
+			if (fmt->uv_swapped) {
+				tmp = u_off;
+				u_off = v_off;
+				v_off = tmp;
+			}
+
+			image->tile_off[tile].offset = y_off;
+			image->tile_off[tile].u_off = u_off;
+			image->tile_off[tile++].v_off = v_off;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
+				ctx, image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				y_off, u_off, v_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_packed(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 w, h, bpp, stride;
+	u32 row_off, col_off;
+
+	/* setup some convenience vars */
+	w = image->tile.width;
+	h = image->tile.height;
+	stride = image->stride;
+	bpp = fmt->bpp;
+
+	for (row = 0; row < image->num_rows; row++) {
+		row_off = row * h * stride;
+
+		for (col = 0; col < image->num_cols; col++) {
+			col_off = (col * w * bpp) >> 3;
+
+			image->tile_off[tile].offset = row_off + col_off;
+			image->tile_off[tile].u_off = 0;
+			image->tile_off[tile++].v_off = 0;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: phys %08x\n", ctx,
+				image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				row_off + col_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets(struct image_converter_ctx *ctx,
+				     struct ipu_ic_image *image)
+{
+	memset(image->tile_off, 0, sizeof(image->tile_off));
+
+	if (image->fmt->y_depth)
+		ipu_ic_calc_tile_offsets_planar(ctx, image);
+	else
+		ipu_ic_calc_tile_offsets_packed(ctx, image);
+}
+
+/*
+ * return the number of runs in given queue (pending_q or done_q)
+ * for this context. hold irqlock when calling.
+ */
+static int ipu_ic_get_run_count(struct image_converter_ctx *ctx,
+				struct list_head *q)
+{
+	struct image_converter_run *run;
+	int count = 0;
+
+	list_for_each_entry(run, q, list) {
+		if (run->ctx == ctx)
+			count++;
+	}
+
+	return count;
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_convert_stop(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev, "%s: stopping ctx %p run %p\n",
+		__func__, ctx, run);
+
+	/* disable IC tasks and the channels */
+	ipu_ic_task_disable(cvt->ic);
+	ipu_idmac_disable_channel(cvt->in_chan);
+	ipu_idmac_disable_channel(cvt->out_chan);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_disable_channel(cvt->rotation_in_chan);
+		ipu_idmac_disable_channel(cvt->rotation_out_chan);
+		ipu_idmac_unlink(cvt->out_chan, cvt->rotation_in_chan);
+	}
+
+	ipu_ic_disable(cvt->ic);
+}
+
+/* hold irqlock when calling */
+static void init_idmac_channel(struct image_converter_ctx *ctx,
+			       struct ipuv3_channel *channel,
+			       struct ipu_ic_image *image,
+			       enum ipu_rotate_mode rot_mode,
+			       bool rot_swap_width_height)
+{
+	struct image_converter *cvt = ctx->cvt;
+	unsigned int burst_size;
+	u32 width, height, stride;
+	dma_addr_t addr0, addr1 = 0;
+	struct ipu_image tile_image;
+	unsigned int tile_idx[2];
+
+	if (image->type == IMAGE_CONVERT_OUT) {
+		tile_idx[0] = ctx->out_tile_map[0];
+		tile_idx[1] = ctx->out_tile_map[1];
+	} else {
+		tile_idx[0] = 0;
+		tile_idx[1] = 1;
+	}
+
+	if (rot_swap_width_height) {
+		width = image->tile.height;
+		height = image->tile.width;
+		stride = image->tile.rot_stride;
+		addr0 = ctx->rot_intermediate[0].phys;
+		if (ctx->double_buffering)
+			addr1 = ctx->rot_intermediate[1].phys;
+	} else {
+		width = image->tile.width;
+		height = image->tile.height;
+		stride = image->stride;
+		addr0 = image->base.phys0 +
+			image->tile_off[tile_idx[0]].offset;
+		if (ctx->double_buffering)
+			addr1 = image->base.phys0 +
+				image->tile_off[tile_idx[1]].offset;
+	}
+
+	ipu_cpmem_zero(channel);
+
+	memset(&tile_image, 0, sizeof(tile_image));
+	tile_image.pix.width = tile_image.rect.width = width;
+	tile_image.pix.height = tile_image.rect.height = height;
+	tile_image.pix.bytesperline = stride;
+	tile_image.pix.pixelformat =  image->fmt->fourcc;
+	tile_image.phys0 = addr0;
+	tile_image.phys1 = addr1;
+	ipu_cpmem_set_image(channel, &tile_image);
+
+	if (image->fmt->y_depth && !rot_swap_width_height)
+		ipu_cpmem_set_uv_offset(channel,
+					image->tile_off[tile_idx[0]].u_off,
+					image->tile_off[tile_idx[0]].v_off);
+
+	if (rot_mode)
+		ipu_cpmem_set_rotation(channel, rot_mode);
+
+	if (channel == cvt->rotation_in_chan ||
+	    channel == cvt->rotation_out_chan) {
+		burst_size = 8;
+		ipu_cpmem_set_block_mode(channel);
+	} else
+		burst_size = (width % 16) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	ipu_ic_task_idma_init(cvt->ic, channel, width, height,
+			      burst_size, rot_mode);
+
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_convert_start(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	enum ipu_color_space src_cs, dest_cs;
+	unsigned int dest_width, dest_height;
+	int ret;
+
+	dev_dbg(priv->ipu->dev, "%s: starting ctx %p run %p\n",
+		__func__, ctx, run);
+
+	src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
+	dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* swap width/height for resizer */
+		dest_width = d_image->tile.height;
+		dest_height = d_image->tile.width;
+	} else {
+		dest_width = d_image->tile.width;
+		dest_height = d_image->tile.height;
+	}
+
+	/* setup the IC resizer and CSC */
+	ret = ipu_ic_task_init(cvt->ic,
+			       s_image->tile.width,
+			       s_image->tile.height,
+			       dest_width,
+			       dest_height,
+			       src_cs, dest_cs);
+	if (ret) {
+		dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
+		return ret;
+	}
+
+	/* init the source MEM-->IC PP IDMAC channel */
+	init_idmac_channel(ctx, cvt->in_chan, s_image,
+			   IPU_ROTATE_NONE, false);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* init the IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   IPU_ROTATE_NONE, true);
+
+		/* init the MEM-->IC PP ROT IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_in_chan, d_image,
+				   ctx->rot_mode, true);
+
+		/* init the destination IC PP ROT-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_out_chan, d_image,
+				   IPU_ROTATE_NONE, false);
+
+		/* now link IC PP-->MEM to MEM-->IC PP ROT */
+		ipu_idmac_link(cvt->out_chan, cvt->rotation_in_chan);
+	} else {
+		/* init the destination IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   ctx->rot_mode, false);
+	}
+
+	/* enable the IC */
+	ipu_ic_enable(cvt->ic);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(cvt->in_chan, 0);
+	ipu_idmac_select_buffer(cvt->out_chan, 0);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode))
+		ipu_idmac_select_buffer(cvt->rotation_out_chan, 0);
+	if (ctx->double_buffering) {
+		ipu_idmac_select_buffer(cvt->in_chan, 1);
+		ipu_idmac_select_buffer(cvt->out_chan, 1);
+		if (ipu_rot_mode_is_irt(ctx->rot_mode))
+			ipu_idmac_select_buffer(cvt->rotation_out_chan, 1);
+	}
+
+	/* enable the channels! */
+	ipu_idmac_enable_channel(cvt->in_chan);
+	ipu_idmac_enable_channel(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_enable_channel(cvt->rotation_in_chan);
+		ipu_idmac_enable_channel(cvt->rotation_out_chan);
+	}
+
+	ipu_ic_task_enable(cvt->ic);
+
+	ipu_cpmem_dump(cvt->in_chan);
+	ipu_cpmem_dump(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_cpmem_dump(cvt->rotation_in_chan);
+		ipu_cpmem_dump(cvt->rotation_out_chan);
+	}
+
+	ipu_dump(priv->ipu);
+
+	return 0;
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_run(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+
+	ctx->in.base.phys0 = run->in_phys;
+	ctx->out.base.phys0 = run->out_phys;
+
+	ctx->cur_buf_num = 0;
+	ctx->next_tile = 1;
+
+	/* remove run from pending_q and set as current */
+	list_del(&run->list);
+	cvt->current_run = run;
+
+	return ipu_ic_convert_start(run);
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_run_next(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *tmp;
+	int ret;
+
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		/* skip contexts that are aborting */
+		if (run->ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: skipping aborting ctx %p run %p\n",
+				 __func__, run->ctx, run);
+			continue;
+		}
+
+		ret = ipu_ic_run(run);
+		if (!ret)
+			break;
+
+		/*
+		 * something went wrong with start, add the run
+		 * to done q and continue to the next run in the
+		 * pending q.
+		 */
+		run->status = ret;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+	}
+}
+
+static void ipu_ic_empty_done_q(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	while (!list_empty(&cvt->done_q)) {
+		run = list_entry(cvt->done_q.next,
+				 struct image_converter_run,
+				 list);
+
+		list_del(&run->list);
+
+		dev_dbg(priv->ipu->dev,
+			"%s: completing ctx %p run %p with %d\n",
+			__func__, run->ctx, run, run->status);
+
+		/* call the completion callback and free the run */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		run->ctx->complete(run->ctx->complete_context, run,
+				   run->status);
+		kfree(run);
+		spin_lock_irqsave(&cvt->irqlock, flags);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+}
+
+/*
+ * the bottom half thread clears out the done_q, calling the
+ * completion handler for each.
+ */
+static irqreturn_t ipu_ic_bh(int irq, void *dev_id)
+{
+	struct image_converter *cvt = dev_id;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+
+	dev_dbg(priv->ipu->dev, "%s: enter\n", __func__);
+
+	ipu_ic_empty_done_q(cvt);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/*
+	 * the done_q is cleared out, signal any contexts
+	 * that are aborting that abort can complete.
+	 */
+	list_for_each_entry(ctx, &cvt->ctx_list, list) {
+		if (ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: signaling abort for ctx %p\n",
+				 __func__, ctx);
+			complete(&ctx->aborted);
+		}
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	dev_dbg(priv->ipu->dev, "%s: exit\n", __func__);
+	return IRQ_HANDLED;
+}
+
+/* hold irqlock when calling */
+static irqreturn_t ipu_ic_doirq(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_tile_off *src_off, *dst_off;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	struct ipuv3_channel *outch;
+	unsigned int dst_idx;
+
+	outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
+		cvt->rotation_out_chan : cvt->out_chan;
+
+	/*
+	 * It is difficult to stop the channel DMA before the channels
+	 * enter the paused state. Without double-buffering the channels
+	 * are always in a paused state when the EOF irq occurs, so it
+	 * is safe to stop the channels now. For double-buffering we
+	 * just ignore the abort until the operation completes, when it
+	 * is safe to shut down.
+	 */
+	if (ctx->aborting && !ctx->double_buffering) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		goto done;
+	}
+
+	if (ctx->next_tile == ctx->num_tiles) {
+		/*
+		 * the conversion is complete
+		 */
+		ipu_ic_convert_stop(run);
+		run->status = 0;
+		goto done;
+	}
+
+	/*
+	 * not done, place the next tile buffers.
+	 */
+	if (!ctx->double_buffering) {
+
+		src_off = &s_image->tile_off[ctx->next_tile];
+		dst_idx = ctx->out_tile_map[ctx->next_tile];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, 0,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, 0,
+				     d_image->base.phys0 + dst_off->offset);
+		if (s_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(cvt->in_chan,
+						src_off->u_off,
+						src_off->v_off);
+		if (d_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(outch,
+						dst_off->u_off,
+						dst_off->v_off);
+
+		ipu_idmac_select_buffer(cvt->in_chan, 0);
+		ipu_idmac_select_buffer(outch, 0);
+
+	} else if (ctx->next_tile < ctx->num_tiles - 1) {
+
+		src_off = &s_image->tile_off[ctx->next_tile + 1];
+		dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, ctx->cur_buf_num,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
+				     d_image->base.phys0 + dst_off->offset);
+
+		ipu_idmac_select_buffer(cvt->in_chan, ctx->cur_buf_num);
+		ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
+
+		ctx->cur_buf_num ^= 1;
+	}
+
+	ctx->next_tile++;
+	return IRQ_HANDLED;
+done:
+	list_add_tail(&run->list, &cvt->done_q);
+	cvt->current_run = NULL;
+	ipu_ic_run_next(cvt);
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ipu_ic_norotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this is a rotation operation, just ignore */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+static irqreturn_t ipu_ic_rotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this was NOT a rotation operation, shouldn't happen */
+		dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+/*
+ * try to force the completion of runs for this ctx. Called when
+ * abort wait times out in ipu_image_convert_abort().
+ */
+static void ipu_ic_force_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	run = cvt->current_run;
+	if (run && run->ctx == ctx) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+		ipu_ic_run_next(cvt);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	ipu_ic_empty_done_q(cvt);
+}
+
+static void ipu_ic_release_ipu_resources(struct image_converter *cvt)
+{
+	if (cvt->out_eof_irq >= 0)
+		free_irq(cvt->out_eof_irq, cvt);
+	if (cvt->rot_out_eof_irq >= 0)
+		free_irq(cvt->rot_out_eof_irq, cvt);
+
+	if (!IS_ERR_OR_NULL(cvt->in_chan))
+		ipu_idmac_put(cvt->in_chan);
+	if (!IS_ERR_OR_NULL(cvt->out_chan))
+		ipu_idmac_put(cvt->out_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_in_chan))
+		ipu_idmac_put(cvt->rotation_in_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_out_chan))
+		ipu_idmac_put(cvt->rotation_out_chan);
+
+	cvt->in_chan = cvt->out_chan = cvt->rotation_in_chan =
+		cvt->rotation_out_chan = NULL;
+	cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
+}
+
+static int ipu_ic_get_ipu_resources(struct image_converter *cvt)
+{
+	const struct ic_task_channels *chan = cvt->ic->ch;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	int ret;
+
+	/* get IDMAC channels */
+	cvt->in_chan = ipu_idmac_get(priv->ipu, chan->in);
+	cvt->out_chan = ipu_idmac_get(priv->ipu, chan->out);
+	if (IS_ERR(cvt->in_chan) || IS_ERR(cvt->out_chan)) {
+		dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	cvt->rotation_in_chan = ipu_idmac_get(priv->ipu, chan->rot_in);
+	cvt->rotation_out_chan = ipu_idmac_get(priv->ipu, chan->rot_out);
+	if (IS_ERR(cvt->rotation_in_chan) || IS_ERR(cvt->rotation_out_chan)) {
+		dev_err(priv->ipu->dev,
+			"could not acquire idmac rotation channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	/* acquire the EOF interrupts */
+	cvt->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						cvt->out_chan,
+						IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->out_eof_irq,
+				   ipu_ic_norotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			 cvt->out_eof_irq);
+		cvt->out_eof_irq = -1;
+		goto err;
+	}
+
+	cvt->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						     cvt->rotation_out_chan,
+						     IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->rot_out_eof_irq,
+				   ipu_ic_rotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			cvt->rot_out_eof_irq);
+		cvt->rot_out_eof_irq = -1;
+		goto err;
+	}
+
+	return 0;
+err:
+	ipu_ic_release_ipu_resources(cvt);
+	return ret;
+}
+
+static int ipu_ic_fill_image(struct image_converter_ctx *ctx,
+			     struct ipu_ic_image *ic_image,
+			     struct ipu_image *image,
+			     enum image_convert_type type)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	ic_image->base = *image;
+	ic_image->type = type;
+
+	ic_image->fmt = ipu_ic_get_format(image->pix.pixelformat);
+	if (!ic_image->fmt) {
+		dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
+			type == IMAGE_CONVERT_OUT ? "Output" : "Input");
+		return -EINVAL;
+	}
+
+	if (ic_image->fmt->y_depth)
+		ic_image->stride = (ic_image->fmt->y_depth *
+				    ic_image->base.pix.width) >> 3;
+	else
+		ic_image->stride  = ic_image->base.pix.bytesperline;
+
+	ipu_ic_calc_tile_dimensions(ctx, ic_image);
+	ipu_ic_calc_tile_offsets(ctx, ic_image);
+
+	return 0;
+}
+
+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+				unsigned int max, unsigned int align)
+{
+	/* Bits that must be zero to be aligned */
+	unsigned int mask = ~((1 << align) - 1);
+
+	/* Clamp to aligned min and max */
+	x = clamp(x, (min + ~mask) & mask, max & mask);
+
+	/* Round to nearest aligned value */
+	if (align)
+		x = (x + (1 << (align - 1))) & mask;
+
+	return x;
+}
+
+/*
+ * We have to adjust the tile width such that the tile physaddrs and
+ * U and V plane offsets are multiples of 8 bytes as required by
+ * the IPU DMA Controller. For the planar formats, this corresponds
+ * to a pixel alignment of 16 (but use a more formal equation since
+ * the variables are available). For all the packed formats, 8 is
+ * good enough.
+ */
+static inline u32 tile_width_align(const struct ipu_ic_pixfmt *fmt)
+{
+	return fmt->y_depth ? (64 * fmt->uv_width_dec) / fmt->y_depth : 8;
+}
+
+/*
+ * For tile height alignment, we have to ensure that the output tile
+ * heights are multiples of 8 lines if the IRT is required by the
+ * given rotation mode (the IRT performs rotations on 8x8 blocks
+ * at a time). If the IRT is not used, or for input image tiles,
+ * 2 lines are good enough.
+ */
+static inline u32 tile_height_align(enum image_convert_type type,
+				    enum ipu_rotate_mode rot_mode)
+{
+	return (type == IMAGE_CONVERT_OUT &&
+		ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
+}
+
+/* Adjusts input/output images to IPU restrictions */
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	const struct ipu_ic_pixfmt *infmt, *outfmt;
+	unsigned int num_in_rows, num_in_cols;
+	unsigned int num_out_rows, num_out_cols;
+	u32 w_align, h_align;
+
+	infmt = ipu_ic_get_format(in->pix.pixelformat);
+	outfmt = ipu_ic_get_format(out->pix.pixelformat);
+
+	/* set some defaults if needed */
+	if (!infmt) {
+		in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		infmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+	if (!outfmt) {
+		out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		outfmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+
+	if (!in->pix.width || !in->pix.height) {
+		in->pix.width = 640;
+		in->pix.height = 480;
+	}
+	if (!out->pix.width || !out->pix.height) {
+		out->pix.width = 640;
+		out->pix.height = 480;
+	}
+
+	/* image converter does not handle fields */
+	in->pix.field = out->pix.field = V4L2_FIELD_NONE;
+
+	/* resizer cannot downsize more than 4:1 */
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.width / 4);
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.height / 4);
+	} else {
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.width / 4);
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.height / 4);
+	}
+
+	/* get tiling rows/cols from output format */
+	num_out_rows = ipu_ic_num_stripes(out->pix.height);
+	num_out_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		num_in_rows = num_out_cols;
+		num_in_cols = num_out_rows;
+	} else {
+		num_in_rows = num_out_rows;
+		num_in_cols = num_out_cols;
+	}
+
+	/* align input width/height */
+	w_align = ilog2(tile_width_align(infmt) * num_in_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
+			num_in_rows);
+	in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
+	in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
+
+	/* align output width/height */
+	w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
+			num_out_rows);
+	out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
+	out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
+
+	/* set input/output strides and image sizes */
+	in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
+	in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
+	out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
+	out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
+
+/*
+ * this is used by ipu_image_convert_prepare() to verify set input and
+ * output images are valid before starting the conversion. Clients can
+ * also call it before calling ipu_image_convert_prepare().
+ */
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	struct ipu_image testin, testout;
+	int ret;
+
+	testin = *in;
+	testout = *out;
+
+	ret = ipu_image_convert_adjust(&testin, &testout, rot_mode);
+	if (ret)
+		return ret;
+
+	if (testin.pix.width != in->pix.width ||
+	    testin.pix.height != in->pix.height ||
+	    testout.pix.width != out->pix.width ||
+	    testout.pix.height != out->pix.height)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
+
+/*
+ * Call ipu_image_convert_prepare() to prepare for the conversion of
+ * given images and rotation mode. Returns a new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+	struct image_converter *cvt = &ic->cvt;
+	struct ipu_ic_image *s_image, *d_image;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+	bool get_res;
+	int ret;
+
+	if (!ic || !in || !out || !complete)
+		return ERR_PTR(-EINVAL);
+
+	/* verify the in/out images before continuing */
+	ret = ipu_image_convert_verify(in, out, rot_mode);
+	if (ret) {
+		dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
+			__func__);
+		return ERR_PTR(ret);
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p\n", __func__, ctx);
+
+	ctx->cvt = cvt;
+	init_completion(&ctx->aborted);
+
+	s_image = &ctx->in;
+	d_image = &ctx->out;
+
+	/* set tiling and rotation */
+	d_image->num_rows = ipu_ic_num_stripes(out->pix.height);
+	d_image->num_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		s_image->num_rows = d_image->num_cols;
+		s_image->num_cols = d_image->num_rows;
+	} else {
+		s_image->num_rows = d_image->num_rows;
+		s_image->num_cols = d_image->num_cols;
+	}
+
+	ctx->num_tiles = d_image->num_cols * d_image->num_rows;
+	ctx->rot_mode = rot_mode;
+
+	ret = ipu_ic_fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
+	if (ret)
+		goto out_free;
+	ret = ipu_ic_fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
+	if (ret)
+		goto out_free;
+
+	ipu_ic_calc_out_tile_map(ctx);
+
+	ipu_ic_dump_format(ctx, s_image);
+	ipu_ic_dump_format(ctx, d_image);
+
+	ctx->complete = complete;
+	ctx->complete_context = complete_context;
+
+	/*
+	 * Can we use double-buffering for this operation? If there is
+	 * only one tile (the whole image can be converted in a single
+	 * operation) there's no point in using double-buffering. Also,
+	 * the IPU's IDMAC channels allow only a single U and V plane
+	 * offset shared between both buffers, but these offsets change
+	 * for every tile, and therefore would have to be updated for
+	 * each buffer which is not possible. So double-buffering is
+	 * impossible when either the source or destination images are
+	 * a planar format (YUV420, YUV422P, etc.).
+	 */
+	ctx->double_buffering = (ctx->num_tiles > 1 &&
+				 !s_image->fmt->y_depth &&
+				 !d_image->fmt->y_depth);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ret = ipu_ic_alloc_dma_buf(priv, &ctx->rot_intermediate[0],
+					   d_image->tile.size);
+		if (ret)
+			goto out_free;
+		if (ctx->double_buffering) {
+			ret = ipu_ic_alloc_dma_buf(priv,
+						   &ctx->rot_intermediate[1],
+						   d_image->tile.size);
+			if (ret)
+				goto out_free_dmabuf0;
+		}
+	}
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	get_res = list_empty(&cvt->ctx_list);
+
+	list_add_tail(&ctx->list, &cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (get_res) {
+		ret = ipu_ic_get_ipu_resources(cvt);
+		if (ret)
+			goto out_free_dmabuf1;
+	}
+
+	return ctx;
+
+out_free_dmabuf1:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	spin_lock_irqsave(&cvt->irqlock, flags);
+	list_del(&ctx->list);
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+out_free_dmabuf0:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+out_free:
+	kfree(ctx);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+/*
+ * Carry out a single image conversion. Only the physaddr's of the input
+ * and output image buffers are needed. The conversion context must have
+ * been created previously with ipu_image_convert_prepare(). Returns the
+ * new run object.
+ */
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+	int ret = 0;
+
+	run = kzalloc(sizeof(*run), GFP_KERNEL);
+	if (!run)
+		return ERR_PTR(-ENOMEM);
+
+	run->ctx = ctx;
+	run->in_phys = in_phys;
+	run->out_phys = out_phys;
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p run %p\n", __func__,
+		ctx, run);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	if (ctx->aborting) {
+		ret = -EIO;
+		goto unlock;
+	}
+
+	list_add_tail(&run->list, &cvt->pending_q);
+
+	if (!cvt->current_run) {
+		ret = ipu_ic_run(run);
+		if (ret)
+			cvt->current_run = NULL;
+	}
+unlock:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (ret) {
+		kfree(run);
+		run = ERR_PTR(ret);
+	}
+
+	return run;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_run);
+
+/* Abort any active or pending conversions for this context */
+void ipu_image_convert_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *active_run, *tmp;
+	unsigned long flags;
+	int run_count, ret;
+	bool need_abort;
+
+	reinit_completion(&ctx->aborted);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* move all remaining pending runs in this context to done_q */
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		if (run->ctx != ctx)
+			continue;
+		run->status = -EIO;
+		list_move_tail(&run->list, &cvt->done_q);
+	}
+
+	run_count = ipu_ic_get_run_count(ctx, &cvt->done_q);
+	active_run = (cvt->current_run && cvt->current_run->ctx == ctx) ?
+		cvt->current_run : NULL;
+
+	need_abort = (run_count || active_run);
+
+	ctx->aborting = need_abort;
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (!need_abort) {
+		dev_dbg(priv->ipu->dev, "%s: no abort needed for ctx %p\n",
+			__func__, ctx);
+		return;
+	}
+
+	dev_dbg(priv->ipu->dev,
+		 "%s: wait for completion: %d runs, active run %p\n",
+		 __func__, run_count, active_run);
+
+	ret = wait_for_completion_timeout(&ctx->aborted,
+					  msecs_to_jiffies(10000));
+	if (ret == 0) {
+		dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
+		ipu_ic_force_abort(ctx);
+	}
+
+	ctx->aborting = false;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
+
+/* Unprepare image conversion context */
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	unsigned long flags;
+	bool put_res;
+
+	/* make sure no runs are hanging around */
+	ipu_image_convert_abort(ctx);
+
+	dev_dbg(priv->ipu->dev, "%s: removing ctx %p\n", __func__, ctx);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	list_del(&ctx->list);
+
+	put_res = list_empty(&cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (put_res)
+		ipu_ic_release_ipu_resources(cvt);
+
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+
+	kfree(ctx);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
+
+/*
+ * "Canned" asynchronous single image conversion. On successful return
+ * caller must call ipu_image_convert_unprepare() after conversion completes.
+ * Returns the new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context)
+{
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+
+	ctx = ipu_image_convert_prepare(ic, in, out, rot_mode,
+					complete, complete_context);
+	if (IS_ERR(ctx))
+		return ctx;
+
+	run = ipu_image_convert_run(ctx, in->phys0, out->phys0);
+	if (IS_ERR(run)) {
+		ipu_image_convert_unprepare(ctx);
+		return ERR_PTR(PTR_ERR(run));
+	}
+
+	return ctx;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert);
+
+/* "Canned" synchronous single image conversion */
+static void image_convert_sync_complete(void *data,
+					struct image_converter_run *run,
+					int err)
+{
+	struct completion *comp = data;
+
+	complete(comp);
+}
+
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode)
+{
+	struct image_converter_ctx *ctx;
+	struct completion comp;
+	int ret;
+
+	init_completion(&comp);
+
+	ctx = ipu_image_convert(ic, in, out, rot_mode,
+				image_convert_sync_complete, &comp);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
+	ret = (ret == 0) ? -ETIMEDOUT : 0;
+
+	ipu_image_convert_unprepare(ctx);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
@@ -759,6 +2428,7 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	ipu->ic_priv = priv;
 
 	spin_lock_init(&priv->lock);
+
 	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
 	if (!priv->base)
 		return -ENOMEM;
@@ -771,10 +2441,21 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	priv->ipu = ipu;
 
 	for (i = 0; i < IC_NUM_TASKS; i++) {
-		priv->task[i].task = i;
-		priv->task[i].priv = priv;
-		priv->task[i].reg = &ic_task_reg[i];
-		priv->task[i].bit = &ic_task_bit[i];
+		struct ipu_ic *ic = &priv->task[i];
+		struct image_converter *cvt = &ic->cvt;
+
+		ic->task = i;
+		ic->priv = priv;
+		ic->reg = &ic_task_reg[i];
+		ic->bit = &ic_task_bit[i];
+		ic->ch = &ic_task_ch[i];
+
+		cvt->ic = ic;
+		spin_lock_init(&cvt->irqlock);
+		INIT_LIST_HEAD(&cvt->ctx_list);
+		INIT_LIST_HEAD(&cvt->pending_q);
+		INIT_LIST_HEAD(&cvt->done_q);
+		cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
 	}
 
 	return 0;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 8f77ddb..5938a69 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -63,17 +63,25 @@ enum ipu_csi_dest {
 /*
  * Enumeration of IPU rotation modes
  */
+#define IPU_ROT_BIT_VFLIP (1 << 0)
+#define IPU_ROT_BIT_HFLIP (1 << 1)
+#define IPU_ROT_BIT_90    (1 << 2)
+
 enum ipu_rotate_mode {
 	IPU_ROTATE_NONE = 0,
-	IPU_ROTATE_VERT_FLIP,
-	IPU_ROTATE_HORIZ_FLIP,
-	IPU_ROTATE_180,
-	IPU_ROTATE_90_RIGHT,
-	IPU_ROTATE_90_RIGHT_VFLIP,
-	IPU_ROTATE_90_RIGHT_HFLIP,
-	IPU_ROTATE_90_LEFT,
+	IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
+	IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
+	IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
+	IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
+	IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
+			      IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
 };
 
+/* 90-degree rotations require the IRT unit */
+#define ipu_rot_mode_is_irt(m) ((m) >= IPU_ROTATE_90_RIGHT)
+
 enum ipu_color_space {
 	IPUV3_COLORSPACE_RGB,
 	IPUV3_COLORSPACE_YUV,
@@ -320,6 +328,7 @@ enum ipu_ic_task {
 };
 
 struct ipu_ic;
+
 int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
@@ -335,6 +344,40 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
 int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
+
+struct image_converter_ctx;
+struct image_converter_run;
+
+typedef void (*image_converter_cb_t)(void *ctx,
+				     struct image_converter_run *run,
+				     int err);
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc);
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context);
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys);
+void ipu_image_convert_abort(struct image_converter_ctx *ctx);
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context);
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode);
+
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1


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

* [PATCH 15/38] gpu: ipu-ic: allow multiple handles to ic
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (13 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 14/38] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 16/38] gpu: ipu-v3: rename CSI client device Steve Longerbeam
                   ` (25 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

The image converter kernel API supports conversion contexts and
job queues, so we should allow more than one handle to the IC, so
that multiple users can add jobs to the queue.

Note however that users that control the IC manually (that do not
use the image converter APIs but setup the IC task by hand via calls
to ipu_ic_task_enable(), ipu_ic_enable(), etc.) must still be careful not
to share the IC handle with other threads. At this point, the only user
that still controls the IC manually is the i.mx capture driver. In that
case the capture driver only allows one open context to get a handle
to the IC at a time, so we should be ok there.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 25 +------------------------
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index f6a1125..51e34a1 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -342,7 +342,6 @@ struct ipu_ic {
 	enum ipu_color_space out_cs;
 	bool graphics;
 	bool rotation;
-	bool in_use;
 
 	struct image_converter cvt;
 
@@ -2380,38 +2379,16 @@ EXPORT_SYMBOL_GPL(ipu_ic_disable);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
 {
 	struct ipu_ic_priv *priv = ipu->ic_priv;
-	unsigned long flags;
-	struct ipu_ic *ic, *ret;
 
 	if (task >= IC_NUM_TASKS)
 		return ERR_PTR(-EINVAL);
 
-	ic = &priv->task[task];
-
-	spin_lock_irqsave(&priv->lock, flags);
-
-	if (ic->in_use) {
-		ret = ERR_PTR(-EBUSY);
-		goto unlock;
-	}
-
-	ic->in_use = true;
-	ret = ic;
-
-unlock:
-	spin_unlock_irqrestore(&priv->lock, flags);
-	return ret;
+	return &priv->task[task];
 }
 EXPORT_SYMBOL_GPL(ipu_ic_get);
 
 void ipu_ic_put(struct ipu_ic *ic)
 {
-	struct ipu_ic_priv *priv = ic->priv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ic->in_use = false;
-	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_put);
 
-- 
1.9.1


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

* [PATCH 16/38] gpu: ipu-v3: rename CSI client device
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (14 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 15/38] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 17/38] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
                   ` (24 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Rename the CSI client device in the client_reg[] table to
"imx-ipuv3-csi".

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 374100e..bd6771b 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1153,14 +1153,14 @@ static struct ipu_platform_reg client_reg[] = {
 			.dma[0] = IPUV3_CHANNEL_CSI0,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.csi = 1,
 			.dma[0] = IPUV3_CHANNEL_CSI1,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.di = 0,
-- 
1.9.1


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

* [PATCH 17/38] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (15 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 16/38] gpu: ipu-v3: rename CSI client device Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 18/38] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
                   ` (23 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Add to the MIPI CSI2 receiver node: compatible string, interrupt sources,
clocks.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6qdl.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index ed613eb..50499eb 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -1119,7 +1119,14 @@
 			};
 
 			mipi_csi: mipi@021dc000 {
+				compatible = "fsl,imx-mipi-csi2";
 				reg = <0x021dc000 0x4000>;
+				interrupts = <0 100 0x04>, <0 101 0x04>;
+				clocks = <&clks IMX6QDL_CLK_HSI_TX>,
+					 <&clks IMX6QDL_CLK_VIDEO_27M>,
+					 <&clks IMX6QDL_CLK_EIM_SEL>;
+				clock-names = "dphy_clk", "cfg_clk", "pix_clk";
+				status = "disabled";
 			};
 
 			mipi_dsi: mipi@021e0000 {
-- 
1.9.1


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

* [PATCH 18/38] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (16 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 17/38] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections Steve Longerbeam
                   ` (22 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Philipp Zabel, Steve Longerbeam

From: Philipp Zabel <p.zabel@pengutronix.de>

This patch adds the device tree graph connecting the input multiplexers
to the IPU CSIs and the MIPI-CSI2 gasket on i.MX6.

On i.MX6Q/D two two-input multiplexers in front of IPU1 CSI0 and IPU2 CSI1
allow to select between CSI0/1 parallel input pads and the MIPI CSI-2 virtual
channels 0/3.

On i.MX6DL/S two five-input multiplexers in front of IPU1 CSI0 and IPU1 CSI1
allow to select between CSI0/1 parallel input pads and any of the four MIPI
CSI-2 virtual channels.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6dl.dtsi  | 183 +++++++++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/imx6q.dtsi   | 120 +++++++++++++++++++++++++++
 arch/arm/boot/dts/imx6qdl.dtsi |   6 ++
 3 files changed, 309 insertions(+)

diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index 9a4c22c..8813df3 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -109,6 +109,118 @@
 		compatible = "fsl,imx-gpu-subsystem";
 		cores = <&gpu_2d>, <&gpu_3d>;
 	};
+
+	ipu1_csi0_mux: videomux@0 {
+		compatible = "imx-video-mux";
+		reg = <0x34 0x07>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu1_csi0_mux_from_mipi_vc0: endpoint {
+				remote-endpoint = <&mipi_vc0_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu1_csi0_mux_from_mipi_vc1: endpoint {
+				remote-endpoint = <&mipi_vc1_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu1_csi0_mux_from_mipi_vc2: endpoint {
+				remote-endpoint = <&mipi_vc2_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@3 {
+			reg = <3>;
+
+			ipu1_csi0_mux_from_mipi_vc3: endpoint {
+				remote-endpoint = <&mipi_vc3_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@4 {
+			reg = <4>;
+
+			ipu1_csi0_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@5 {
+			reg = <5>;
+
+			ipu1_csi0_mux_to_ipu1_csi0: endpoint {
+				remote-endpoint = <&ipu1_csi0_from_ipu1_csi0_mux>;
+			};
+		};
+	};
+
+	ipu1_csi1_mux: videomux@1 {
+		compatible = "imx-video-mux";
+		reg = <0x34 0x38>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu1_csi1_mux_from_mipi_vc0: endpoint {
+				remote-endpoint = <&mipi_vc0_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu1_csi1_mux_from_mipi_vc1: endpoint {
+				remote-endpoint = <&mipi_vc1_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu1_csi1_mux_from_mipi_vc2: endpoint {
+				remote-endpoint = <&mipi_vc2_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@3 {
+			reg = <3>;
+
+			ipu1_csi1_mux_from_mipi_vc3: endpoint {
+				remote-endpoint = <&mipi_vc3_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@4 {
+			reg = <4>;
+
+			ipu1_csi1_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@5 {
+			reg = <5>;
+
+			ipu1_csi1_mux_to_ipu1_csi1: endpoint {
+				remote-endpoint = <&ipu1_csi1_from_ipu1_csi1_mux>;
+			};
+		};
+	};
 };
 
 &gpt {
@@ -131,3 +243,74 @@
 &vpu {
 	compatible = "fsl,imx6dl-vpu", "cnm,coda960";
 };
+
+&ipu1_csi1 {
+	ipu1_csi1_from_ipu1_csi1_mux: endpoint {
+		remote-endpoint = <&ipu1_csi1_mux_to_ipu1_csi1>;
+	};
+};
+
+&mipi_csi {
+	port@0 {
+		reg = <0>;
+
+		mipi_csi_from_mipi_sensor: endpoint {
+		};
+	};
+
+	port@1 {
+		reg = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc0_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc0>;
+		};
+
+		mipi_vc0_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc0>;
+		};
+	};
+
+	port@2 {
+		reg = <2>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc1_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc1>;
+		};
+
+		mipi_vc1_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc1>;
+		};
+	};
+
+	port@3 {
+		reg = <3>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc2_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc2>;
+		};
+
+		mipi_vc2_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc2>;
+		};
+	};
+
+	port@4 {
+		reg = <4>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc3_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc3>;
+		};
+
+		mipi_vc3_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc3>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index c30c836..a487658 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -143,10 +143,18 @@
 
 			ipu2_csi0: port@0 {
 				reg = <0>;
+
+				ipu2_csi0_from_mipi_vc2: endpoint {
+					remote-endpoint = <&mipi_vc2_to_ipu2_csi0>;
+				};
 			};
 
 			ipu2_csi1: port@1 {
 				reg = <1>;
+
+				ipu2_csi1_from_ipu2_csi1_mux: endpoint {
+					remote-endpoint = <&ipu2_csi1_mux_to_ipu2_csi1>;
+				};
 			};
 
 			ipu2_di0: port@2 {
@@ -207,6 +215,71 @@
 		compatible = "fsl,imx-gpu-subsystem";
 		cores = <&gpu_2d>, <&gpu_3d>, <&gpu_vg>;
 	};
+
+
+	ipu1_csi0_mux: videomux@0 {
+		compatible = "imx-video-mux";
+		reg = <0x04 0x80000>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu1_csi0_mux_from_mipi_vc0: endpoint {
+				remote-endpoint = <&mipi_vc0_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu1_csi0_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu1_csi0_mux_to_ipu1_csi0: endpoint {
+				remote-endpoint = <&ipu1_csi0_from_ipu1_csi0_mux>;
+			};
+		};
+	};
+
+	ipu2_csi1_mux: videomux@1 {
+		compatible = "imx-video-mux";
+		reg = <0x04 0x100000>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu2_csi1_mux_from_mipi_vc3: endpoint {
+				remote-endpoint = <&mipi_vc3_to_ipu2_csi1_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu2_csi1_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu2_csi1_mux_to_ipu2_csi1: endpoint {
+				remote-endpoint = <&ipu2_csi1_from_ipu2_csi1_mux>;
+			};
+		};
+	};
 };
 
 &hdmi {
@@ -229,6 +302,12 @@
 	};
 };
 
+&ipu1_csi1 {
+	ipu1_csi1_from_mipi_vc1: endpoint {
+		remote-endpoint = <&mipi_vc1_to_ipu1_csi1>;
+	};
+};
+
 &ldb {
 	clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>, <&clks IMX6QDL_CLK_LDB_DI1_SEL>,
 		 <&clks IMX6QDL_CLK_IPU1_DI0_SEL>, <&clks IMX6QDL_CLK_IPU1_DI1_SEL>,
@@ -275,6 +354,47 @@
 	};
 };
 
+&mipi_csi {
+	port@0 {
+		reg = <0>;
+
+		mipi_csi_from_mipi_sensor: endpoint {
+		};
+	};
+
+	port@1 {
+		reg = <1>;
+
+		mipi_vc0_to_ipu1_csi0_mux: endpoint {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc0>;
+		};
+	};
+
+	port@2 {
+		reg = <2>;
+
+		mipi_vc1_to_ipu1_csi1: endpoint {
+			remote-endpoint = <&ipu1_csi1_from_mipi_vc1>;
+		};
+	};
+
+	port@3 {
+		reg = <3>;
+
+		mipi_vc2_to_ipu2_csi0: endpoint {
+			remote-endpoint = <&ipu2_csi0_from_mipi_vc2>;
+		};
+	};
+
+	port@4 {
+		reg = <4>;
+
+		mipi_vc3_to_ipu2_csi1_mux: endpoint {
+			remote-endpoint = <&ipu2_csi1_mux_from_mipi_vc3>;
+		};
+	};
+};
+
 &mipi_dsi {
 	ports {
 		port@2 {
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 50499eb..838d1d5 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -1121,6 +1121,8 @@
 			mipi_csi: mipi@021dc000 {
 				compatible = "fsl,imx-mipi-csi2";
 				reg = <0x021dc000 0x4000>;
+				#address-cells = <1>;
+				#size-cells = <0>;
 				interrupts = <0 100 0x04>, <0 101 0x04>;
 				clocks = <&clks IMX6QDL_CLK_HSI_TX>,
 					 <&clks IMX6QDL_CLK_VIDEO_27M>,
@@ -1226,6 +1228,10 @@
 
 			ipu1_csi0: port@0 {
 				reg = <0>;
+
+				ipu1_csi0_from_ipu1_csi0_mux: endpoint {
+					remote-endpoint = <&ipu1_csi0_mux_to_ipu1_csi0>;
+				};
 			};
 
 			ipu1_csi1: port@1 {
-- 
1.9.1


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

* [PATCH 19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (17 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 18/38] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-16  8:32   ` [19/38] " Gary Bisson
  2016-06-14 22:49 ` [PATCH 20/38] ARM: dts: imx6-sabresd: " Steve Longerbeam
                   ` (21 subsequent siblings)
  40 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Defines the host video capture device node and an OV5642 camera sensor
node on i2c2. The host capture device connects to the OV5642 via the
parallel-bus mux input on the ipu1_csi0_mux.

Note there is a pin conflict with GPIO6. This pin functions as a power
input pin to the OV5642, but ENET requires it to wake-up the ARM cores
on normal RX and TX packet done events (see 6261c4c8). So by default,
capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
Ethernet will still work just not quite as well.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6qdl-sabrelite.dtsi | 95 ++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
index c47fe6c..9709183 100644
--- a/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabrelite.dtsi
@@ -39,9 +39,20 @@
  *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  *     OTHER DEALINGS IN THE SOFTWARE.
  */
+
+#include <dt-bindings/clock/imx6qdl-clock.h>
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/input/input.h>
 
+/*
+ * Uncomment the following macro to enable OV5642 video capture
+ * support. There is a pin conflict for GPIO6 between ENET wake-up
+ * interrupt function and power-down pin function for the OV5642.
+ * ENET will still work when enabling OV5642 capture, just not
+ * quite as well.
+ */
+/* #define __OV5642_CAPTURE__ */
+
 / {
 	chosen {
 		stdout-path = &uart2;
@@ -218,8 +229,37 @@
 			};
 		};
 	};
+
+#ifdef __OV5642_CAPTURE__
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>;
+		status = "okay";
+	};
+#endif
+};
+
+#ifdef __OV5642_CAPTURE__
+&ipu1_csi0_from_ipu1_csi0_mux {
+	bus-width = <8>;
+	data-shift = <12>; /* Lines 19:12 used */
+	hsync-active = <1>;
+	vync-active = <1>;
 };
 
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&ov5642_to_ipu1_csi0_mux>;
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+#endif
+	
 &audmux {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_audmux>;
@@ -271,8 +311,11 @@
 	txd1-skew-ps = <0>;
 	txd2-skew-ps = <0>;
 	txd3-skew-ps = <0>;
+#ifndef __OV5642_CAPTURE__
 	interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
 			      <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
+
+#endif
 	status = "okay";
 };
 
@@ -301,6 +344,30 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_i2c2>;
 	status = "okay";
+
+#ifdef __OV5642_CAPTURE__
+	camera: ov5642@3c {
+		compatible = "ovti,ov5642";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ov5642>;
+		clocks = <&clks IMX6QDL_CLK_CKO2>;
+		clock-names = "xclk";
+		reg = <0x3c>;
+		xclk = <24000000>;
+		reset-gpios = <&gpio1 8 0>;
+		pwdn-gpios = <&gpio1 6 0>;
+		gp-gpios = <&gpio1 16 0>;
+
+		port {
+			ov5642_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <8>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+			};
+		};
+	};
+#endif
 };
 
 &i2c3 {
@@ -373,7 +440,9 @@
 				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL	0x1b0b0
 				/* Phy reset */
 				MX6QDL_PAD_EIM_D23__GPIO3_IO23		0x000b0
+#ifndef __OV5642_CAPTURE__
 				MX6QDL_PAD_GPIO_6__ENET_IRQ		0x000b1
+#endif
 			>;
 		};
 
@@ -448,6 +517,32 @@
 			>;
 		};
 
+		pinctrl_ipu1_csi0: ipu1grp-csi0 {
+			fsl,pins = <
+				MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12    0x80000000
+				MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13    0x80000000
+				MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14    0x80000000
+				MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15    0x80000000
+				MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16    0x80000000
+				MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17    0x80000000
+				MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18    0x80000000
+				MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19    0x80000000
+				MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK   0x80000000
+				MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC      0x80000000
+				MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC     0x80000000
+				MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000
+			>;
+		};
+
+		pinctrl_ov5642: ov5642grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000
+				MX6QDL_PAD_GPIO_6__GPIO1_IO06   0x80000000
+				MX6QDL_PAD_GPIO_8__GPIO1_IO08   0x80000000
+				MX6QDL_PAD_GPIO_3__CCM_CLKO2    0x80000000
+			>;
+		};
+
 		pinctrl_pwm1: pwm1grp {
 			fsl,pins = <
 				MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1
-- 
1.9.1


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

* [PATCH 20/38] ARM: dts: imx6-sabresd: add video capture ports and connections
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (18 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 21/38] ARM: dts: imx6-sabreauto: create i2cmux for i2c3 Steve Longerbeam
                   ` (20 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Defines the host ipu-capture device node and two camera sensors:
parallel-bus OV5642 and MIPI CSI-2 OV5640.

The host capture device connects to the OV5642 via the parallel-bus
mux input on the ipu1_csi0_mux.

The host capture device connects to the OV5640 via the MIPI CSI-2
receiver (directly on virtual channel 1 to ipu1_csi1 on imx6q, and
indirectly via the ipu1_csi1_mux on imx6dl).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6dl-sabresd.dts   |  44 +++++++++++
 arch/arm/boot/dts/imx6q-sabresd.dts    |  16 ++++
 arch/arm/boot/dts/imx6qdl-sabresd.dtsi | 139 ++++++++++++++++++++++++++++++++-
 3 files changed, 198 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx6dl-sabresd.dts b/arch/arm/boot/dts/imx6dl-sabresd.dts
index 1e45f2f..0a4bfc2 100644
--- a/arch/arm/boot/dts/imx6dl-sabresd.dts
+++ b/arch/arm/boot/dts/imx6dl-sabresd.dts
@@ -15,3 +15,47 @@
 	model = "Freescale i.MX6 DualLite SABRE Smart Device Board";
 	compatible = "fsl,imx6dl-sabresd", "fsl,imx6dl";
 };
+
+&ipu1_csi1_from_ipu1_csi1_mux {
+	data-lanes = <0 1>;
+	clock-lanes = <2>;
+};
+
+&ipu1_csi1_mux {
+	status = "okay";
+};
+
+/*
+ * if the OV5642 sensor is enabled, the ipu1_csi0_mux is also enabled,
+ * but we don't want to find the OV5640 through ipu1_csi0_mux path to the
+ * mipi-csi2 receiver, so shutdown the link to the mipi-csi2 receiver at
+ * all virtual channels.
+*/
+#ifdef __ENABLE_OV5642__
+&ipu1_csi0_mux_from_mipi_vc0 {
+	remote-endpoint = <>;
+};
+&ipu1_csi0_mux_from_mipi_vc1 {
+	remote-endpoint = <>;
+};
+&ipu1_csi0_mux_from_mipi_vc2 {
+	remote-endpoint = <>;
+};
+&ipu1_csi0_mux_from_mipi_vc3 {
+	remote-endpoint = <>;
+};
+#endif
+
+/*
+ * shutdown links to mipi-csi2 channels 0,2,3 through ipu1_csi1_mux. The
+ * OV5640 is on VC1, so it must be found only on that ipu1_csi1_mux input.
+ */
+&ipu1_csi1_mux_from_mipi_vc0 {
+	remote-endpoint = <>;
+};
+&ipu1_csi1_mux_from_mipi_vc2 {
+	remote-endpoint = <>;
+};
+&ipu1_csi1_mux_from_mipi_vc3 {
+	remote-endpoint = <>;
+};
diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts
index 9cbdfe7..ade6305 100644
--- a/arch/arm/boot/dts/imx6q-sabresd.dts
+++ b/arch/arm/boot/dts/imx6q-sabresd.dts
@@ -23,3 +23,19 @@
 &sata {
 	status = "okay";
 };
+
+&ipu1_csi1_from_mipi_vc1 {
+	data-lanes = <0 1>;
+	clock-lanes = <2>;
+};
+
+/*
+ * if the OV5642 sensor is enabled, the ipu1_csi0_mux is also enabled,
+ * but we don't want to find the OV5640 through ipu1_csi0_mux path, so
+ * shutdown the link to the mipi-csi2 receiver.
+*/
+#ifdef __ENABLE_OV5642__
+&ipu1_csi0_mux_from_mipi_vc0 {
+	remote-endpoint = <>;
+};
+#endif
diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
index 5248e7b..ce575e6 100644
--- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
@@ -10,6 +10,10 @@
  * http://www.gnu.org/copyleft/gpl.html
  */
 
+/* Uncomment to enable parallel interface OV5642 on i2c1 and port csi0 */
+/* #define __ENABLE_OV5642__ */
+
+#include <dt-bindings/clock/imx6qdl-clock.h>
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/input/input.h>
 
@@ -144,6 +148,54 @@
 			};
 		};
 	};
+
+#ifdef __ENABLE_OV5642__
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>;
+		status = "okay";
+	};
+#endif
+
+	ipucap1: ipucap@1 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ports = <&ipu1_csi1>;
+		status = "okay";
+	};
+};
+
+#ifdef __ENABLE_OV5642__
+&ipu1_csi0_from_ipu1_csi0_mux {
+	bus-width = <8>;
+	data-shift = <12>; /* Lines 19:12 used */
+	hsync-active = <1>;
+	vsync-active = <1>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&ov5642_to_ipu1_csi0_mux>;
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+#endif
+
+&mipi_csi {
+	status = "okay";
+};
+
+/* Incoming port from sensor */
+&mipi_csi_from_mipi_sensor {
+	remote-endpoint = <&ov5640_to_mipi_csi>;
+	data-lanes = <0 1>;
+	clock-lanes = <2>;
 };
 
 &audmux {
@@ -214,7 +266,34 @@
 			0x8014 /* 4:FN_DMICCDAT */
 			0x0000 /* 5:Default */
 		>;
-       };
+	};
+
+#ifdef __ENABLE_OV5642__
+	camera: ov5642@3c {
+		compatible = "ovti,ov5642";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ov5642>;
+		clocks = <&clks IMX6QDL_CLK_CKO>;
+		clock-names = "xclk";
+		reg = <0x3c>;
+		xclk = <24000000>;
+		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
+		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
+			      		     	rev B board is VGEN5 */
+		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
+		pwdn-gpios = <&gpio1 16 1>;   /* SD1_DAT0 */
+		reset-gpios = <&gpio1 17 0>; /* SD1_DAT1 */
+
+		port {
+			ov5642_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <8>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+			};
+		};
+	};
+#endif
 };
 
 &i2c2 {
@@ -322,6 +401,34 @@
 			};
 		};
 	};
+
+	mipi_camera: ov5640@3c {
+		compatible = "ovti,ov5640_mipi";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ov5640>;
+		reg = <0x3c>;
+		clocks = <&clks IMX6QDL_CLK_CKO>;
+		clock-names = "xclk";
+		xclk = <24000000>;
+		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
+		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
+			      		     	rev B board is VGEN5 */
+		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
+		pwdn-gpios = <&gpio1 19 0>; /* SD1_DAT2 */
+		reset-gpios = <&gpio1 20 1>; /* SD1_CLK */
+
+		port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ov5640_to_mipi_csi: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&mipi_csi_from_mipi_sensor>;
+				data-lanes = <0 1>;
+				clock-lanes = <2>;
+			};
+		};
+	};
 };
 
 &i2c3 {
@@ -426,6 +533,36 @@
 			>;
 		};
 
+		pinctrl_ov5640: ov5640grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD1_DAT2__GPIO1_IO19 0x80000000
+				MX6QDL_PAD_SD1_CLK__GPIO1_IO20  0x80000000
+			>;
+		};
+
+		pinctrl_ov5642: ov5642grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000
+				MX6QDL_PAD_SD1_DAT1__GPIO1_IO17 0x80000000
+			>;
+		};
+
+		pinctrl_ipu1_csi0: ipu1grp-csi0 {
+			fsl,pins = <
+				MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12    0x80000000
+				MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13    0x80000000
+				MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14    0x80000000
+				MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15    0x80000000
+				MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16    0x80000000
+				MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17    0x80000000
+				MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18    0x80000000
+				MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19    0x80000000
+				MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK   0x80000000
+				MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC      0x80000000
+				MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC     0x80000000
+			>;
+		};
+
 		pinctrl_pcie: pciegrp {
 			fsl,pins = <
 				MX6QDL_PAD_GPIO_17__GPIO7_IO12	0x1b0b0
-- 
1.9.1


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

* [PATCH 21/38] ARM: dts: imx6-sabreauto: create i2cmux for i2c3
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (19 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 20/38] ARM: dts: imx6-sabresd: " Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 22/38] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310 Steve Longerbeam
                   ` (19 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

The sabreauto uses a steering pin to select between the SDA signal on
i2c3 bus, and a data-in pin for an SPI NOR chip. Use i2cmux to control
this steering pin. Idle state of the i2cmux selects SPI NOR. This is not
a classic way to use i2cmux, since one side of the mux selects something
other than an i2c bus, but it works and is probably the cleanest
solution. Note that if one thread is attempting to access SPI NOR while
another thread is accessing i2c3, the SPI NOR access will fail since the
i2cmux has selected the SDA pin rather than SPI NOR data-in. This couldn't
be avoided in any case, the board is not designed to allow concurrent
i2c3 and SPI NOR functions (and the default device-tree does not enable
SPI NOR anyway).

Devices hanging off i2c3 should now be defined under i2cmux, so
that the steering pin can be properly controlled to access those
devices. The port expanders (MAX7310) are thus moved into i2cmux.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 65 +++++++++++++++++++++-----------
 1 file changed, 44 insertions(+), 21 deletions(-)

diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index d354d40..13f50e8 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -108,6 +108,44 @@
 		default-brightness-level = <7>;
 		status = "okay";
 	};
+
+	i2cmux {
+		compatible = "i2c-mux-gpio";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_i2c3mux>;
+		mux-gpios = <&gpio5 4 0>;
+		i2c-parent = <&i2c3>;
+		idle-state = <0>;
+
+		i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+
+			max7310_a: gpio@30 {
+				compatible = "maxim,max7310";
+				reg = <0x30>;
+				gpio-controller;
+				#gpio-cells = <2>;
+			};
+
+			max7310_b: gpio@32 {
+				compatible = "maxim,max7310";
+				reg = <0x32>;
+				gpio-controller;
+				#gpio-cells = <2>;
+			};
+
+			max7310_c: gpio@34 {
+				compatible = "maxim,max7310";
+				reg = <0x34>;
+				gpio-controller;
+				#gpio-cells = <2>;
+			};
+		};
+	};
 };
 
 &clks {
@@ -290,27 +328,6 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_i2c3>;
 	status = "okay";
-
-	max7310_a: gpio@30 {
-		compatible = "maxim,max7310";
-		reg = <0x30>;
-		gpio-controller;
-		#gpio-cells = <2>;
-	};
-
-	max7310_b: gpio@32 {
-		compatible = "maxim,max7310";
-		reg = <0x32>;
-		gpio-controller;
-		#gpio-cells = <2>;
-	};
-
-	max7310_c: gpio@34 {
-		compatible = "maxim,max7310";
-		reg = <0x34>;
-		gpio-controller;
-		#gpio-cells = <2>;
-	};
 };
 
 &iomuxc {
@@ -418,6 +435,12 @@
 			>;
 		};
 
+		pinctrl_i2c3mux: i2c3muxgrp {
+			fsl,pins = <
+				MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x80000000
+			>;
+		};
+
 		pinctrl_pwm3: pwm1grp {
 			fsl,pins = <
 				MX6QDL_PAD_SD4_DAT1__PWM3_OUT		0x1b0b1
-- 
1.9.1


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

* [PATCH 22/38] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (20 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 21/38] ARM: dts: imx6-sabreauto: create i2cmux for i2c3 Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 23/38] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture Steve Longerbeam
                   ` (18 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

The reset pin to the port expander chip (MAX7310) is controlled by a gpio,
so define a reset-gpios property to control it.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 13f50e8..81e3ab7 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -129,6 +129,9 @@
 				reg = <0x30>;
 				gpio-controller;
 				#gpio-cells = <2>;
+				pinctrl-names = "default";	
+				pinctrl-0 = <&pinctrl_max7310>;
+				reset-gpios = <&gpio1 15 1>;
 			};
 
 			max7310_b: gpio@32 {
@@ -136,6 +139,9 @@
 				reg = <0x32>;
 				gpio-controller;
 				#gpio-cells = <2>;
+				pinctrl-names = "default";	
+				pinctrl-0 = <&pinctrl_max7310>;
+				reset-gpios = <&gpio1 15 1>;
 			};
 
 			max7310_c: gpio@34 {
@@ -143,6 +149,9 @@
 				reg = <0x34>;
 				gpio-controller;
 				#gpio-cells = <2>;
+				pinctrl-names = "default";	
+				pinctrl-0 = <&pinctrl_max7310>;
+				reset-gpios = <&gpio1 15 1>;
 			};
 		};
 	};
@@ -441,6 +450,12 @@
 			>;
 		};
 
+		pinctrl_max7310: max7310grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x80000000
+			>;
+		};
+
 		pinctrl_pwm3: pwm1grp {
 			fsl,pins = <
 				MX6QDL_PAD_SD4_DAT1__PWM3_OUT		0x1b0b1
-- 
1.9.1


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

* [PATCH 23/38] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (21 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 22/38] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310 Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 24/38] ARM: dts: imx6-sabreauto: add video capture ports and connections Steve Longerbeam
                   ` (17 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Add pinctrl groups for both GPT input capture channels.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 81e3ab7..21af432 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -462,6 +462,18 @@
 			>;
 		};
 
+		pinctrl_gpt_input_capture0: gptinputcapture0grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD1_DAT0__GPT_CAPTURE1	0x80000000
+			>;
+		};
+
+		pinctrl_gpt_input_capture1: gptinputcapture1grp {
+			fsl,pins = <
+				MX6QDL_PAD_SD1_DAT1__GPT_CAPTURE2	0x80000000
+			>;
+		};
+
 		pinctrl_spdif: spdifgrp {
 			fsl,pins = <
 				MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0
-- 
1.9.1


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

* [PATCH 24/38] ARM: dts: imx6-sabreauto: add video capture ports and connections
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (22 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 23/38] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 25/38] ARM: dts: imx6qdl: add mem2mem device for sabre* boards Steve Longerbeam
                   ` (16 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Defines the host video capture device node and the ADV7180 decoder
sensor. The host capture device connects to the ADV7180 via the
parallel-bus mux input on the ipu1_csi0_mux.

On the sabreauto, two analog video inputs are routed to the ADV7180,
composite on Ain1, and composite on Ain3. Those inputs are defined
via inputs and input-names under the endpoint node
ipu1_csi0_from_ipu1_csi0_mux. The ADV7180 power pin is via max7310_b
port expander.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 68 ++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 21af432..f962f51 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -153,10 +153,54 @@
 				pinctrl-0 = <&pinctrl_max7310>;
 				reset-gpios = <&gpio1 15 1>;
 			};
+
+			camera: adv7180@21 {
+				compatible = "adi,adv7180";
+				reg = <0x21>;
+				pwdn-gpio = <&max7310_b 2 0>;
+				interrupt-parent = <&gpio1>;
+				interrupts = <27 0x8>;
+
+				port {
+					adv7180_to_ipu1_csi0_mux: endpoint {
+						remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+						bus-width = <8>;
+					};
+				};
+			};
+		};
+	};
+
+	ipucap0: ipucap0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>;
+		status = "okay";
+
+		fim {
+			enable = <1>;
 		};
 	};
 };
 
+&ipu1_csi0_from_ipu1_csi0_mux {
+	bus-width = <8>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&adv7180_to_ipu1_csi0_mux>;
+	inputs = <0x00 0x02>;
+	input-names = "ADV7180 Composite on Ain1",
+			"ADV7180 Composite on Ain3";
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+
 &clks {
 	assigned-clocks = <&clks IMX6QDL_PLL4_BYPASS_SRC>,
 			  <&clks IMX6QDL_PLL4_BYPASS>,
@@ -456,6 +500,30 @@
 			>;
 		};
 
+		pinctrl_ipu1_csi0: ipu1grp-csi0 {
+			fsl,pins = <
+				MX6QDL_PAD_CSI0_DAT4__IPU1_CSI0_DATA04   0x80000000
+				MX6QDL_PAD_CSI0_DAT5__IPU1_CSI0_DATA05   0x80000000
+				MX6QDL_PAD_CSI0_DAT6__IPU1_CSI0_DATA06   0x80000000
+				MX6QDL_PAD_CSI0_DAT7__IPU1_CSI0_DATA07   0x80000000
+				MX6QDL_PAD_CSI0_DAT8__IPU1_CSI0_DATA08   0x80000000
+				MX6QDL_PAD_CSI0_DAT9__IPU1_CSI0_DATA09   0x80000000
+				MX6QDL_PAD_CSI0_DAT10__IPU1_CSI0_DATA10  0x80000000
+				MX6QDL_PAD_CSI0_DAT11__IPU1_CSI0_DATA11  0x80000000
+				MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12  0x80000000
+				MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13  0x80000000
+				MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14  0x80000000
+				MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15  0x80000000
+				MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16  0x80000000
+				MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17  0x80000000
+				MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18  0x80000000
+				MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19  0x80000000
+				MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000
+				MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC    0x80000000
+				MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC   0x80000000
+			>;
+		};
+
 		pinctrl_pwm3: pwm1grp {
 			fsl,pins = <
 				MX6QDL_PAD_SD4_DAT1__PWM3_OUT		0x1b0b1
-- 
1.9.1


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

* [PATCH 25/38] ARM: dts: imx6qdl: add mem2mem device for sabre* boards
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (23 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 24/38] ARM: dts: imx6-sabreauto: add video capture ports and connections Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 26/38] gpio: pca953x: Add reset-gpios property Steve Longerbeam
                   ` (15 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Enables ipu-mem2mem device on SabreAuto, SabreSD, and SabreLite
(also on ipu2 for quad).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6q-sabreauto.dts    | 7 +++++++
 arch/arm/boot/dts/imx6q-sabrelite.dts    | 6 ++++++
 arch/arm/boot/dts/imx6q-sabresd.dts      | 6 ++++++
 arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 6 ++++++
 arch/arm/boot/dts/imx6qdl-sabresd.dtsi   | 6 ++++++
 5 files changed, 31 insertions(+)

diff --git a/arch/arm/boot/dts/imx6q-sabreauto.dts b/arch/arm/boot/dts/imx6q-sabreauto.dts
index 334b924..6e79396 100644
--- a/arch/arm/boot/dts/imx6q-sabreauto.dts
+++ b/arch/arm/boot/dts/imx6q-sabreauto.dts
@@ -18,8 +18,15 @@
 / {
 	model = "Freescale i.MX6 Quad SABRE Automotive Board";
 	compatible = "fsl,imx6q-sabreauto", "fsl,imx6q";
+
+	ipum2m1: ipum2m@ipu2 {
+		compatible = "fsl,imx-video-mem2mem";
+		ipu = <&ipu2>;
+		status = "okay";
+	};
 };
 
+
 &sata {
 	status = "okay";
 };
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index 66d10d8..95fe618 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -47,6 +47,12 @@
 / {
 	model = "Freescale i.MX6 Quad SABRE Lite Board";
 	compatible = "fsl,imx6q-sabrelite", "fsl,imx6q";
+
+	ipum2m1: ipum2m@ipu2 {
+		compatible = "fsl,imx-video-mem2mem";
+		ipu = <&ipu2>;
+		status = "okay";
+	};
 };
 
 &sata {
diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts
index ade6305..1156919 100644
--- a/arch/arm/boot/dts/imx6q-sabresd.dts
+++ b/arch/arm/boot/dts/imx6q-sabresd.dts
@@ -18,6 +18,12 @@
 / {
 	model = "Freescale i.MX6 Quad SABRE Smart Device Board";
 	compatible = "fsl,imx6q-sabresd", "fsl,imx6q";
+
+	ipum2m1: ipum2m@ipu2 {
+		compatible = "fsl,imx-video-mem2mem";
+		ipu = <&ipu2>;
+		status = "okay";
+	};
 };
 
 &sata {
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index f962f51..811059f 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -184,6 +184,12 @@
 			enable = <1>;
 		};
 	};
+
+	ipum2m0: ipum2m@ipu1 {
+		compatible = "fsl,imx-video-mem2mem";
+		ipu = <&ipu1>;
+		status = "okay";
+	};
 };
 
 &ipu1_csi0_from_ipu1_csi0_mux {
diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
index ce575e6..a67ad02 100644
--- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi
@@ -168,6 +168,12 @@
 		ports = <&ipu1_csi1>;
 		status = "okay";
 	};
+
+	ipum2m0: ipum2m@ipu1 {
+		compatible = "fsl,imx-video-mem2mem";
+		ipu = <&ipu1>;
+		status = "okay";
+	};
 };
 
 #ifdef __ENABLE_OV5642__
-- 
1.9.1


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

* [PATCH 26/38] gpio: pca953x: Add reset-gpios property
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (24 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 25/38] ARM: dts: imx6qdl: add mem2mem device for sabre* boards Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 27/38] clocksource/drivers/imx: add input capture support Steve Longerbeam
                   ` (14 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Add optional reset-gpios property. If present, de-assert the
specified reset gpio pin to bring the chip out of reset.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpio/gpio-pca953x.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 5e3be32..475fa56 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -21,6 +21,7 @@
 #include <asm/unaligned.h>
 #include <linux/of_platform.h>
 #include <linux/acpi.h>
+#include <linux/of_gpio.h>
 
 #define PCA953X_INPUT		0
 #define PCA953X_OUTPUT		1
@@ -111,6 +112,11 @@ struct pca953x_chip {
 	const char *const *names;
 	int	chip_type;
 	unsigned long driver_data;
+
+#ifdef CONFIG_OF_GPIO
+	enum of_gpio_flags reset_gpio_flags;
+	int reset_gpio;
+#endif
 };
 
 static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
@@ -759,6 +765,28 @@ static int pca953x_probe(struct i2c_client *client,
 	} else {
 		chip->gpio_start = -1;
 		irq_base = 0;
+
+#ifdef CONFIG_OF_GPIO
+		/* see if we need to de-assert a reset pin */
+		ret = of_get_named_gpio_flags(client->dev.of_node,
+					      "reset-gpios", 0,
+					      &chip->reset_gpio_flags);
+		if (gpio_is_valid(ret)) {
+			chip->reset_gpio = ret;
+			ret = devm_gpio_request_one(&client->dev,
+						    chip->reset_gpio,
+						    GPIOF_DIR_OUT,
+						    "pca953x_reset");
+			if (ret == 0) {
+				/* bring chip out of reset */
+				dev_info(&client->dev, "releasing reset\n");
+				gpio_set_value(chip->reset_gpio,
+					       (chip->reset_gpio_flags ==
+						OF_GPIO_ACTIVE_LOW) ? 1 : 0);
+			}
+		} else if (ret == -EPROBE_DEFER)
+			return ret;
+#endif
 	}
 
 	chip->client = client;
-- 
1.9.1


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

* [PATCH 27/38] clocksource/drivers/imx: add input capture support
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (25 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 26/38] gpio: pca953x: Add reset-gpios property Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 28/38] v4l: Add signal lock status to source change events Steve Longerbeam
                   ` (13 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This patch adds support for the input capture function in the
i.MX GPT. Output compare and input capture functions are mixed
in the same register block, so we need to modify the irq ack/enable/
disable primitives to not stomp on the other function.

The input capture API is modelled after request/free irq:

typedef void (*mxc_icap_handler_t)(int, void *, struct timespec *);

int mxc_request_input_capture(unsigned int chan,
			      mxc_icap_handler_t handler,
			      unsigned long capflags, void *dev_id);

    - chan: the channel number being requested (0 or 1).

    - handler: a callback when there is an input capture event. The
      handler is given the channel number, the dev_id, and a timespec
      marking the input capture event. The timespec is always reset at
      request time, that is, the first event after request will always
      have a timespec of 0, and will increase thereafter.

    - capflags: IRQF_TRIGGER_RISING and/or IRQF_TRIGGER_FALLING. If
      both are specified, events will be triggered on both rising and
      falling edges of the input capture signal.

    - dev_id: a context pointer given back to the handler.

void mxc_free_input_capture(unsigned int chan, void *dev_id);

    This disables the given input capture channel in the GPT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/clocksource/timer-imx-gpt.c | 463 ++++++++++++++++++++++++++++++++----
 include/linux/mxc_icap.h            |  20 ++
 2 files changed, 437 insertions(+), 46 deletions(-)
 create mode 100644 include/linux/mxc_icap.h

diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
index 99ec967..1f7f871 100644
--- a/drivers/clocksource/timer-imx-gpt.c
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -21,6 +21,7 @@
  * MA 02110-1301, USA.
  */
 
+#include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/clockchips.h>
@@ -32,6 +33,8 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/mxc_icap.h>
 #include <soc/imx/timer.h>
 
 /*
@@ -65,16 +68,53 @@
 #define V2_TCTL_CLK_PER		(2 << 6)
 #define V2_TCTL_CLK_OSC_DIV8	(5 << 6)
 #define V2_TCTL_FRR		(1 << 9)
+#define V2_TCTL_IM1_BIT		16
+#define V2_TCTL_IM2_BIT		18
+#define V2_IM_DISABLE		0
+#define V2_IM_RISING		1
+#define V2_IM_FALLING		2
+#define V2_IM_BOTH		3
 #define V2_TCTL_24MEN		(1 << 10)
 #define V2_TPRER_PRE24M		12
 #define V2_IR			0x0c
+#define V2_IR_OF1		(1 << 0)
+#define V2_IR_IF1		(1 << 3)
+#define V2_IR_IF2		(1 << 4)
 #define V2_TSTAT		0x08
 #define V2_TSTAT_OF1		(1 << 0)
+#define V2_TSTAT_IF1		(1 << 3)
+#define V2_TSTAT_IF2		(1 << 4)
 #define V2_TCN			0x24
 #define V2_TCMP			0x10
+#define V2_TCAP1		0x1c
+#define V2_TCAP2		0x20
 
 #define V2_TIMER_RATE_OSC_DIV8	3000000
 
+struct imx_timer;
+
+struct icap_channel {
+	struct imx_timer *imxtm;
+
+	int chan;
+
+	u32 cnt_reg;
+	u32 irqen_bit;
+	u32 status_bit;
+	u32 mode_bit;
+
+	mxc_icap_handler_t handler;
+	void *dev_id;
+
+	struct timespec ts;
+	cycles_t last_cycles;
+	bool first_event;
+};
+
+/* FIXME, for now can't find icap unless it's statically allocated */
+static struct icap_channel icap_channel[2];
+static DEFINE_SPINLOCK(icap_lock);
+
 struct imx_timer {
 	enum imx_gpt_type type;
 	void __iomem *base;
@@ -90,12 +130,20 @@ struct imx_gpt_data {
 	int reg_tstat;
 	int reg_tcn;
 	int reg_tcmp;
-	void (*gpt_setup_tctl)(struct imx_timer *imxtm);
-	void (*gpt_irq_enable)(struct imx_timer *imxtm);
-	void (*gpt_irq_disable)(struct imx_timer *imxtm);
-	void (*gpt_irq_acknowledge)(struct imx_timer *imxtm);
+	void (*gpt_oc_setup_tctl)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_enable)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_disable)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_acknowledge)(struct imx_timer *imxtm);
+	bool (*gpt_is_oc_irq)(unsigned int tstat);
 	int (*set_next_event)(unsigned long evt,
 			      struct clock_event_device *ced);
+
+	void (*gpt_ic_irq_enable)(struct icap_channel *ic);
+	void (*gpt_ic_irq_disable)(struct icap_channel *ic);
+	void (*gpt_ic_irq_acknowledge)(struct icap_channel *ic);
+	bool (*gpt_is_ic_irq)(unsigned int tstat);
+	void (*gpt_ic_enable)(struct icap_channel *ic, unsigned int mode);
+	void (*gpt_ic_disable)(struct icap_channel *ic);
 };
 
 static inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
@@ -103,52 +151,144 @@ static inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
 	return container_of(ced, struct imx_timer, ced);
 }
 
-static void imx1_gpt_irq_disable(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_disable(struct imx_timer *imxtm)
 {
 	unsigned int tmp;
 
 	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
 	writel_relaxed(tmp & ~MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_irq_disable imx1_gpt_irq_disable
+#define imx21_gpt_oc_irq_disable imx1_gpt_oc_irq_disable
 
-static void imx31_gpt_irq_disable(struct imx_timer *imxtm)
+static void imx31_gpt_oc_irq_disable(struct imx_timer *imxtm)
 {
-	writel_relaxed(0, imxtm->base + V2_IR);
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	writel_relaxed(tmp & ~V2_IR_OF1, imxtm->base + V2_IR);
 }
-#define imx6dl_gpt_irq_disable imx31_gpt_irq_disable
+#define imx6dl_gpt_oc_irq_disable imx31_gpt_oc_irq_disable
 
-static void imx1_gpt_irq_enable(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_enable(struct imx_timer *imxtm)
 {
 	unsigned int tmp;
 
 	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
 	writel_relaxed(tmp | MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_irq_enable imx1_gpt_irq_enable
+#define imx21_gpt_oc_irq_enable imx1_gpt_oc_irq_enable
 
-static void imx31_gpt_irq_enable(struct imx_timer *imxtm)
+static void imx31_gpt_oc_irq_enable(struct imx_timer *imxtm)
 {
-	writel_relaxed(1<<0, imxtm->base + V2_IR);
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	writel_relaxed(tmp | V2_IR_OF1, imxtm->base + V2_IR);
 }
-#define imx6dl_gpt_irq_enable imx31_gpt_irq_enable
+#define imx6dl_gpt_oc_irq_enable imx31_gpt_oc_irq_enable
 
-static void imx1_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(0, imxtm->base + MX1_2_TSTAT);
 }
 
-static void imx21_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static void imx21_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(MX2_TSTAT_CAPT | MX2_TSTAT_COMP,
 				imxtm->base + MX1_2_TSTAT);
 }
 
-static void imx31_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static bool imx1_gpt_is_oc_irq(unsigned int tstat)
+{
+	return true;
+}
+
+static bool imx21_gpt_is_oc_irq(unsigned int tstat)
+{
+	return (tstat & MX2_TSTAT_COMP) != 0;
+}
+
+static bool imx31_gpt_is_oc_irq(unsigned int tstat)
+{
+	return (tstat & V2_TSTAT_OF1) != 0;
+}
+#define imx6dl_gpt_is_oc_irq imx31_gpt_is_oc_irq
+
+static void imx31_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(V2_TSTAT_OF1, imxtm->base + V2_TSTAT);
 }
-#define imx6dl_gpt_irq_acknowledge imx31_gpt_irq_acknowledge
+#define imx6dl_gpt_oc_irq_acknowledge imx31_gpt_oc_irq_acknowledge
+
+static void imx31_gpt_ic_irq_disable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	tmp &= ~ic->irqen_bit;
+	writel_relaxed(tmp, imxtm->base + V2_IR);
+}
+#define imx6dl_gpt_ic_irq_disable imx31_gpt_ic_irq_disable
+
+static void imx31_gpt_ic_irq_enable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	tmp |= ic->irqen_bit;
+	writel_relaxed(tmp, imxtm->base + V2_IR);
+}
+#define imx6dl_gpt_ic_irq_enable imx31_gpt_ic_irq_enable
+
+static void imx31_gpt_ic_irq_acknowledge(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+
+	writel_relaxed(ic->status_bit, imxtm->base + V2_TSTAT);
+}
+#define imx6dl_gpt_ic_irq_acknowledge imx31_gpt_ic_irq_acknowledge
+
+static bool imx1_gpt_is_ic_irq(unsigned int tstat)
+{
+	return false;
+}
+#define imx21_gpt_is_ic_irq imx1_gpt_is_ic_irq
+
+static bool imx31_gpt_is_ic_irq(unsigned int tstat)
+{
+	return (tstat & (V2_TSTAT_IF1 | V2_TSTAT_IF2)) != 0;
+}
+#define imx6dl_gpt_is_ic_irq imx31_gpt_is_ic_irq
+
+static void imx31_gpt_ic_enable(struct icap_channel *ic, unsigned int mode)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tctl, mask;
+
+	mask = 0x3 << ic->mode_bit;
+	mode <<= ic->mode_bit;
+
+	tctl = readl_relaxed(imxtm->base + MXC_TCTL);
+	tctl &= ~mask;
+	tctl |= mode;
+	writel_relaxed(tctl, imxtm->base + MXC_TCTL);
+}
+#define imx6dl_gpt_ic_enable imx31_gpt_ic_enable
+
+static void imx31_gpt_ic_disable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tctl, mask;
+
+	mask = 0x3 << ic->mode_bit;
+
+	tctl = readl_relaxed(imxtm->base + MXC_TCTL);
+	tctl &= ~mask;
+	writel_relaxed(tctl, imxtm->base + MXC_TCTL);
+}
+#define imx6dl_gpt_ic_disable imx31_gpt_ic_disable
 
 static void __iomem *sched_clock_reg;
 
@@ -164,6 +304,32 @@ static unsigned long imx_read_current_timer(void)
 	return readl_relaxed(sched_clock_reg);
 }
 
+static void mxc_update_icap_ts(struct icap_channel *ic, struct timespec *ts)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	cycles_t cycles;
+	u64 diff;
+	u32 rem;
+
+	cycles = readl_relaxed(imxtm->base + ic->cnt_reg);
+	if (!ic->first_event) {
+		if (cycles >= ic->last_cycles)
+			diff = cycles - ic->last_cycles;
+		else
+			diff = ((u64)1 << 32) - ic->last_cycles + cycles;
+
+		diff *= NSEC_PER_SEC;
+		diff += imx_delay_timer.freq / 2;
+
+		rem = do_div(diff, imx_delay_timer.freq);
+
+		timespec_add_ns(&ic->ts, diff);
+	}
+
+	*ts = ic->ts;
+	ic->last_cycles = cycles;
+}
+
 static int __init mxc_clocksource_init(struct imx_timer *imxtm)
 {
 	unsigned int c = clk_get_rate(imxtm->clk_per);
@@ -224,14 +390,14 @@ static int mxc_shutdown(struct clock_event_device *ced)
 	local_irq_save(flags);
 
 	/* Disable interrupt in GPT module */
-	imxtm->gpt->gpt_irq_disable(imxtm);
+	imxtm->gpt->gpt_oc_irq_disable(imxtm);
 
 	tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
 	/* Set event time into far-far future */
 	writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
 
 	/* Clear pending interrupt */
-	imxtm->gpt->gpt_irq_acknowledge(imxtm);
+	imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
 
 #ifdef DEBUG
 	printk(KERN_INFO "%s: changing mode\n", __func__);
@@ -254,7 +420,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 	local_irq_save(flags);
 
 	/* Disable interrupt in GPT module */
-	imxtm->gpt->gpt_irq_disable(imxtm);
+	imxtm->gpt->gpt_oc_irq_disable(imxtm);
 
 	if (!clockevent_state_oneshot(ced)) {
 		u32 tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
@@ -262,7 +428,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 		writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
 
 		/* Clear pending interrupt */
-		imxtm->gpt->gpt_irq_acknowledge(imxtm);
+		imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
 	}
 
 #ifdef DEBUG
@@ -275,7 +441,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 	 * to call mxc_set_next_event() or shutdown clock after
 	 * mode switching
 	 */
-	imxtm->gpt->gpt_irq_enable(imxtm);
+	imxtm->gpt->gpt_oc_irq_enable(imxtm);
 	local_irq_restore(flags);
 
 	return 0;
@@ -292,9 +458,31 @@ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
 
 	tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat);
 
-	imxtm->gpt->gpt_irq_acknowledge(imxtm);
+	if (imxtm->gpt->gpt_is_ic_irq(tstat)) {
+		struct icap_channel *ic;
+		struct timespec ts;
+		int i;
+
+		for (i = 0; i < 2; i++) {
+			ic = &icap_channel[i];
+
+			if (!(tstat & ic->status_bit))
+				continue;
 
-	ced->event_handler(ced);
+			imxtm->gpt->gpt_ic_irq_acknowledge(ic);
+
+			mxc_update_icap_ts(ic, &ts);
+			ic->first_event = false;
+
+			if (ic->handler)
+				ic->handler(ic->chan, ic->dev_id, &ts);
+		}
+	}
+
+	if (imxtm->gpt->gpt_is_oc_irq(tstat)) {
+		imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
+		ced->event_handler(ced);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -324,16 +512,16 @@ static int __init mxc_clockevent_init(struct imx_timer *imxtm)
 	return setup_irq(imxtm->irq, act);
 }
 
-static void imx1_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx1_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
 	tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
 	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_setup_tctl imx1_gpt_setup_tctl
+#define imx21_gpt_oc_setup_tctl imx1_gpt_oc_setup_tctl
 
-static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx31_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
@@ -346,7 +534,7 @@ static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
 	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
 }
 
-static void imx6dl_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx6dl_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
@@ -367,10 +555,12 @@ static const struct imx_gpt_data imx1_gpt_data = {
 	.reg_tstat = MX1_2_TSTAT,
 	.reg_tcn = MX1_2_TCN,
 	.reg_tcmp = MX1_2_TCMP,
-	.gpt_irq_enable = imx1_gpt_irq_enable,
-	.gpt_irq_disable = imx1_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx1_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx1_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx1_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx1_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx1_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx1_gpt_is_oc_irq,
+	.gpt_is_ic_irq = imx1_gpt_is_ic_irq,
+	.gpt_oc_setup_tctl = imx1_gpt_oc_setup_tctl,
 	.set_next_event = mx1_2_set_next_event,
 };
 
@@ -378,10 +568,12 @@ static const struct imx_gpt_data imx21_gpt_data = {
 	.reg_tstat = MX1_2_TSTAT,
 	.reg_tcn = MX1_2_TCN,
 	.reg_tcmp = MX1_2_TCMP,
-	.gpt_irq_enable = imx21_gpt_irq_enable,
-	.gpt_irq_disable = imx21_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx21_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx21_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx21_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx21_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx21_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx21_gpt_is_oc_irq,
+	.gpt_is_ic_irq = imx21_gpt_is_ic_irq,
+	.gpt_oc_setup_tctl = imx21_gpt_oc_setup_tctl,
 	.set_next_event = mx1_2_set_next_event,
 };
 
@@ -389,26 +581,136 @@ static const struct imx_gpt_data imx31_gpt_data = {
 	.reg_tstat = V2_TSTAT,
 	.reg_tcn = V2_TCN,
 	.reg_tcmp = V2_TCMP,
-	.gpt_irq_enable = imx31_gpt_irq_enable,
-	.gpt_irq_disable = imx31_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx31_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx31_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx31_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx31_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx31_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx31_gpt_is_oc_irq,
+	.gpt_oc_setup_tctl = imx31_gpt_oc_setup_tctl,
 	.set_next_event = v2_set_next_event,
+
+	/* input capture methods */
+	.gpt_ic_irq_enable = imx31_gpt_ic_irq_enable,
+	.gpt_ic_irq_disable = imx31_gpt_ic_irq_disable,
+	.gpt_ic_irq_acknowledge = imx31_gpt_ic_irq_acknowledge,
+	.gpt_is_ic_irq = imx31_gpt_is_ic_irq,
+	.gpt_ic_enable = imx31_gpt_ic_enable,
+	.gpt_ic_disable = imx31_gpt_ic_disable,
 };
 
 static const struct imx_gpt_data imx6dl_gpt_data = {
 	.reg_tstat = V2_TSTAT,
 	.reg_tcn = V2_TCN,
 	.reg_tcmp = V2_TCMP,
-	.gpt_irq_enable = imx6dl_gpt_irq_enable,
-	.gpt_irq_disable = imx6dl_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx6dl_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx6dl_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx6dl_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx6dl_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx6dl_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx6dl_gpt_is_oc_irq,
+	.gpt_oc_setup_tctl = imx6dl_gpt_oc_setup_tctl,
 	.set_next_event = v2_set_next_event,
+
+	/* input capture methods */
+	.gpt_ic_irq_enable = imx6dl_gpt_ic_irq_enable,
+	.gpt_ic_irq_disable = imx6dl_gpt_ic_irq_disable,
+	.gpt_ic_irq_acknowledge = imx6dl_gpt_ic_irq_acknowledge,
+	.gpt_is_ic_irq = imx6dl_gpt_is_ic_irq,
+	.gpt_ic_enable = imx6dl_gpt_ic_enable,
+	.gpt_ic_disable = imx6dl_gpt_ic_disable,
 };
 
+int mxc_request_input_capture(unsigned int chan, mxc_icap_handler_t handler,
+			      unsigned long capflags, void *dev_id)
+{
+	struct imx_timer *imxtm;
+	struct icap_channel *ic;
+	unsigned long flags;
+	int ret = 0;
+	u32 mode;
+
+	/* we only care about rising and falling flags */
+	capflags &= (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING);
+
+	if (chan > 1 || !handler || !capflags)
+		return -EINVAL;
+
+	ic = &icap_channel[chan];
+	imxtm = ic->imxtm;
+
+	if (!imxtm->gpt->gpt_ic_enable)
+		return -ENODEV;
+
+	spin_lock_irqsave(&icap_lock, flags);
+
+	if (ic->handler) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ic->handler = handler;
+	ic->dev_id = dev_id;
+
+	switch (capflags) {
+	case IRQF_TRIGGER_RISING:
+		mode = V2_IM_RISING;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		mode = V2_IM_FALLING;
+		break;
+	default:
+		mode = V2_IM_BOTH;
+		break;
+	}
+
+	/* ack any pending input capture interrupt before enabling */
+	imxtm->gpt->gpt_ic_irq_acknowledge(ic);
+
+	/* initialize timespec */
+	ic->ts.tv_sec = ic->ts.tv_nsec = 0;
+	ic->first_event = true;
+
+	imxtm->gpt->gpt_ic_enable(ic, mode);
+	imxtm->gpt->gpt_ic_irq_enable(ic);
+
+out:
+	spin_unlock_irqrestore(&icap_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mxc_request_input_capture);
+
+void mxc_free_input_capture(unsigned int chan, void *dev_id)
+{
+	struct imx_timer *imxtm;
+	struct icap_channel *ic;
+	unsigned long flags;
+
+	if (chan > 1)
+		return;
+
+	ic = &icap_channel[chan];
+	imxtm = ic->imxtm;
+
+	if (!imxtm->gpt->gpt_ic_disable)
+		return;
+
+	spin_lock_irqsave(&icap_lock, flags);
+
+	if (!ic->handler || dev_id != ic->dev_id)
+		goto out;
+
+	imxtm->gpt->gpt_ic_irq_disable(ic);
+	imxtm->gpt->gpt_ic_disable(ic);
+
+	ic->handler = NULL;
+	ic->dev_id = NULL;
+out:
+	spin_unlock_irqrestore(&icap_lock, flags);
+}
+EXPORT_SYMBOL_GPL(mxc_free_input_capture);
+
 static void __init _mxc_timer_init(struct imx_timer *imxtm)
 {
+	struct icap_channel *ic;
+	int i;
+
 	switch (imxtm->type) {
 	case GPT_TYPE_IMX1:
 		imxtm->gpt = &imx1_gpt_data;
@@ -443,11 +745,16 @@ static void __init _mxc_timer_init(struct imx_timer *imxtm)
 	writel_relaxed(0, imxtm->base + MXC_TCTL);
 	writel_relaxed(0, imxtm->base + MXC_TPRER); /* see datasheet note */
 
-	imxtm->gpt->gpt_setup_tctl(imxtm);
+	imxtm->gpt->gpt_oc_setup_tctl(imxtm);
 
 	/* init and register the timer to the framework */
 	mxc_clocksource_init(imxtm);
 	mxc_clockevent_init(imxtm);
+
+	for (i = 0; i < 2; i++) {
+		ic = &icap_channel[i];
+		ic->imxtm = imxtm;
+	}
 }
 
 void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
@@ -469,6 +776,70 @@ void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
 	_mxc_timer_init(imxtm);
 }
 
+/*
+ * a platform driver is needed in order to acquire pinmux
+ * for input capture pins. The probe call is also useful
+ * for setting up the input capture channel structures.
+ */
+static int mxc_timer_probe(struct platform_device *pdev)
+{
+	struct icap_channel *ic;
+	int i;
+
+	/* setup the input capture channels */
+	for (i = 0; i < 2; i++) {
+		ic = &icap_channel[i];
+		ic->chan = i;
+		if (i == 0) {
+			ic->cnt_reg = V2_TCAP1;
+			ic->irqen_bit = V2_IR_IF1;
+			ic->status_bit = V2_TSTAT_IF1;
+			ic->mode_bit = V2_TCTL_IM1_BIT;
+		} else {
+			ic->cnt_reg = V2_TCAP2;
+			ic->irqen_bit = V2_IR_IF2;
+			ic->status_bit = V2_TSTAT_IF2;
+			ic->mode_bit = V2_TCTL_IM2_BIT;
+		}
+	}
+
+	return 0;
+}
+
+static int mxc_timer_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id timer_of_match[] = {
+	{ .compatible = "fsl,imx1-gpt" },
+	{ .compatible = "fsl,imx21-gpt" },
+	{ .compatible = "fsl,imx27-gpt" },
+	{ .compatible = "fsl,imx31-gpt" },
+	{ .compatible = "fsl,imx25-gpt" },
+	{ .compatible = "fsl,imx50-gpt" },
+	{ .compatible = "fsl,imx51-gpt" },
+	{ .compatible = "fsl,imx53-gpt" },
+	{ .compatible = "fsl,imx6q-gpt" },
+	{ .compatible = "fsl,imx6dl-gpt" },
+	{ .compatible = "fsl,imx6sl-gpt" },
+	{ .compatible = "fsl,imx6sx-gpt" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, timer_of_match);
+
+static struct platform_driver mxc_timer_pdrv = {
+	.probe		= mxc_timer_probe,
+	.remove		= mxc_timer_remove,
+	.driver		= {
+		.name	= "mxc-timer",
+		.owner	= THIS_MODULE,
+		.of_match_table	= timer_of_match,
+	},
+};
+
+module_platform_driver(mxc_timer_pdrv);
+
 static void __init mxc_timer_init_dt(struct device_node *np,  enum imx_gpt_type type)
 {
 	struct imx_timer *imxtm;
diff --git a/include/linux/mxc_icap.h b/include/linux/mxc_icap.h
new file mode 100644
index 0000000..a829f11
--- /dev/null
+++ b/include/linux/mxc_icap.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 Mentor Graphics, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MXC_ICAP_H__
+#define __MXC_ICAP_H__
+
+typedef void (*mxc_icap_handler_t)(int, void *, struct timespec *);
+
+int mxc_request_input_capture(unsigned int chan, mxc_icap_handler_t handler,
+			      unsigned long capflags, void *dev_id);
+void mxc_free_input_capture(unsigned int chan, void *dev_id);
+
+#endif
-- 
1.9.1


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

* [PATCH 28/38] v4l: Add signal lock status to source change events
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (26 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 27/38] clocksource/drivers/imx: add input capture support Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-07-01  7:24   ` Hans Verkuil
  2016-06-14 22:49 ` [PATCH 29/38] media: Add camera interface driver for i.MX5/6 Steve Longerbeam
                   ` (12 subsequent siblings)
  40 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Add a signal lock status change to the source changes bitmask.
This indicates there was a signal lock or unlock event detected
at the input of a video decoder.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 Documentation/DocBook/media/v4l/vidioc-dqevent.xml | 12 ++++++++++--
 include/uapi/linux/videodev2.h                     |  1 +
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
index c9c3c77..7758ad7 100644
--- a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
@@ -233,8 +233,9 @@
 	    <entry>
 	      <para>This event is triggered when a source parameter change is
 	       detected during runtime by the video device. It can be a
-	       runtime resolution change triggered by a video decoder or the
-	       format change happening on an input connector.
+	       runtime resolution change or signal lock status change
+	       triggered by a video decoder, or the format change happening
+	       on an input connector.
 	       This event requires that the <structfield>id</structfield>
 	       matches the input index (when used with a video device node)
 	       or the pad index (when used with a subdevice node) from which
@@ -461,6 +462,13 @@
 	    from a video decoder.
 	    </entry>
 	  </row>
+	  <row>
+	    <entry><constant>V4L2_EVENT_SRC_CH_LOCK_STATUS</constant></entry>
+	    <entry>0x0002</entry>
+	    <entry>This event gets triggered when there is a signal lock or
+	    unlock detected at the input of a video decoder.
+	    </entry>
+	  </row>
 	</tbody>
       </tgroup>
     </table>
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 8f95191..2eba5da 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -2076,6 +2076,7 @@ struct v4l2_event_frame_sync {
 };
 
 #define V4L2_EVENT_SRC_CH_RESOLUTION		(1 << 0)
+#define V4L2_EVENT_SRC_CH_LOCK_STATUS		(1 << 1)
 
 struct v4l2_event_src_change {
 	__u32 changes;
-- 
1.9.1


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

* [PATCH 29/38] media: Add camera interface driver for i.MX5/6
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (27 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 28/38] v4l: Add signal lock status to source change events Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 30/38] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
                   ` (11 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media
  Cc: Steve Longerbeam, Dmitry Eremin-Solenikov, Jiada Wang,
	Vladimir Zapolskiy

This is a V4L2 camera interface driver for i.MX5/6. See
Documentation/video4linux/imx_camera.txt and device tree binding
documentation at Documentation/devicetree/bindings/media/imx.txt.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
Signed-off-by: Dmitry Eremin-Solenikov <dmitry_eremin@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
---
 Documentation/devicetree/bindings/media/imx.txt   |  426 ++++
 Documentation/video4linux/imx_camera.txt          |  243 ++
 drivers/staging/media/Kconfig                     |    2 +
 drivers/staging/media/Makefile                    |    1 +
 drivers/staging/media/imx/Kconfig                 |   23 +
 drivers/staging/media/imx/Makefile                |    1 +
 drivers/staging/media/imx/capture/Kconfig         |    3 +
 drivers/staging/media/imx/capture/Makefile        |    5 +
 drivers/staging/media/imx/capture/imx-camif.c     | 2496 +++++++++++++++++++++
 drivers/staging/media/imx/capture/imx-camif.h     |  281 +++
 drivers/staging/media/imx/capture/imx-csi.c       |  195 ++
 drivers/staging/media/imx/capture/imx-ic-prpenc.c |  660 ++++++
 drivers/staging/media/imx/capture/imx-of.c        |  354 +++
 drivers/staging/media/imx/capture/imx-of.h        |   18 +
 drivers/staging/media/imx/capture/imx-smfc.c      |  505 +++++
 drivers/staging/media/imx/capture/imx-vdic.c      |  994 ++++++++
 include/media/imx.h                               |   15 +
 include/uapi/Kbuild                               |    1 +
 include/uapi/linux/v4l2-controls.h                |    4 +
 include/uapi/media/Kbuild                         |    2 +
 include/uapi/media/imx.h                          |   22 +
 21 files changed, 6251 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/imx.txt
 create mode 100644 Documentation/video4linux/imx_camera.txt
 create mode 100644 drivers/staging/media/imx/Kconfig
 create mode 100644 drivers/staging/media/imx/Makefile
 create mode 100644 drivers/staging/media/imx/capture/Kconfig
 create mode 100644 drivers/staging/media/imx/capture/Makefile
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.c
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.h
 create mode 100644 drivers/staging/media/imx/capture/imx-csi.c
 create mode 100644 drivers/staging/media/imx/capture/imx-ic-prpenc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.h
 create mode 100644 drivers/staging/media/imx/capture/imx-smfc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-vdic.c
 create mode 100644 include/media/imx.h
 create mode 100644 include/uapi/media/Kbuild
 create mode 100644 include/uapi/media/imx.h

diff --git a/Documentation/devicetree/bindings/media/imx.txt b/Documentation/devicetree/bindings/media/imx.txt
new file mode 100644
index 0000000..1f2ca4b
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/imx.txt
@@ -0,0 +1,426 @@
+Freescale i.MX Video Capture
+
+Video Capture node
+------------------
+
+This is the imx video capture host interface node. The host node is an IPU
+client and uses the register-level primitives of the IPU, so it does
+not require reg or interrupt properties. Only a compatible property
+and a list of IPU CSI port phandles is required.
+
+Required properties:
+- compatible	: "fsl,imx-video-capture";
+- ports         : a list of CSI port phandles this device will control
+
+Optional properties:
+- fim           : child node that sets boot-time behavior of the
+		  Frame Interval Monitor;
+
+fim child node
+--------------
+
+This is an optional child node of the video capture node. It can
+be used to modify the default control values for the video capture
+Frame Interval Monitor. Refer to Documentation/video4linux/imx_camera.txt
+for more info on the Frame Interval Monitor.
+
+Optional properties:
+- enable          : enable (1) or disable (0) the FIM;
+- num-avg         : how many frame intervals the FIM will average;
+- num-skip        : how many frames the FIM will skip after a video
+		    capture restart before beginning to sample frame
+		    intervals;
+- tolerance-range : a range of tolerances for the averaged frame
+		    interval error, specified as <min max>, in usec.
+		    The FIM will signal a frame interval error if
+		    min < error < max. If the max is <= min, then
+		    tolerance range is disabled (interval error if
+		    error > min).
+- input-capture-channel: an input capture channel and channel flags,
+			 specified as <chan flags>. The channel number
+			 must be 0 or 1. The flags can be
+			 IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, or
+			 IRQ_TYPE_EDGE_BOTH, and specify which input
+			 capture signal edge will trigger the event. If
+			 an input capture channel is specified, the FIM
+			 will use this method to measure frame intervals
+			 instead of via the EOF interrupt. The input capture
+			 method is much preferred over EOF as it is not
+			 subject to interrupt latency errors. However it
+			 requires routing the VSYNC or FIELD output
+			 signals of the camera sensor to one of the
+			 i.MX input capture pads (SD1_DAT0, SD1_DAT1),
+			 which also gives up support for SD1.
+
+
+mipi_csi2 node
+--------------
+
+This is the device node for the MIPI CSI-2 Receiver, required for MIPI
+CSI-2 sensors.
+
+Required properties:
+- compatible	: "fsl,imx-mipi-csi2";
+- reg           : physical base address and length of the register set;
+- clocks	: the MIPI CSI-2 receiver requires three clocks: hsi_tx
+                  (the DPHY clock), video_27m, and eim_sel;
+- clock-names	: must contain "dphy_clk", "cfg_clk", "pix_clk";
+
+Optional properties:
+- interrupts	: must contain two level-triggered interrupts,
+                  in order: 100 and 101;
+
+
+Device tree nodes of the image sensors' controlled directly by the imx
+camera host interface driver must be child nodes of their corresponding
+I2C bus controller node. The data link of these image sensors must be
+specified using the common video interfaces bindings, defined in
+video-interfaces.txt.
+
+Video capture is supported with the following imx-based reference
+platforms:
+
+
+SabreLite with OV5642
+---------------------
+
+The OV5642 module is connected to the parallel bus input on the internal
+video mux to IPU1 CSI0. It's i2c bus connects to i2c bus 2, so the ov5642
+sensor node must be a child of i2c2.
+
+OV5642 Required properties:
+- compatible	: "ovti,ov5642";
+- clocks        : the OV5642 system clock (cko2, 200);
+- clock-names	: must be "xclk";
+- reg           : must be 0x3c;
+- xclk          : the system clock frequency, must be 24000000;
+- reset-gpios   : must be <&gpio1 8 0>;
+- pwdn-gpios    : must be <&gpio1 6 0>;
+
+OV5642 Endpoint Required properties:
+- remote-endpoint : must connect to parallel sensor interface input endpoint 
+  		    on ipu1_csi0 video mux (ipu1_csi0_mux_from_parallel_sensor).
+- bus-width       : must be 8;
+- hsync-active    : must be 1;
+- vsync-active    : must be 1;
+
+The following is an example devicetree video capture configuration for
+SabreLite:
+
+/ {
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>;
+		status = "okay";
+	};
+};
+
+&ipu1_csi0_from_ipu1_csi0_mux {
+	bus-width = <8>;
+	data-shift = <12>; /* Lines 19:12 used */
+	hsync-active = <1>;
+	vync-active = <1>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&ov5642_to_ipu1_csi0_mux>;
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+
+&i2c2 {
+	camera: ov5642@3c {
+		compatible = "ovti,ov5642";
+		clocks = <&clks 200>;
+		clock-names = "xclk";
+		reg = <0x3c>;
+		xclk = <24000000>;
+		reset-gpios = <&gpio1 8 0>;
+		pwdn-gpios = <&gpio1 6 0>;
+		gp-gpios = <&gpio1 16 0>;
+
+		port {
+			ov5642_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <8>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+			};
+		};
+	};
+};
+
+
+SabreAuto with ADV7180
+----------------------
+
+On the SabreAuto, an on-board ADV7180 SD decoder is connected to the
+parallel bus input on the internal video mux to IPU1 CSI0.
+
+Two analog video inputs are routed to the ADV7180 on the SabreAuto,
+composite on Ain1, and composite on Ain3. Those inputs are defined
+via inputs and input-names properties under the ipu1_csi0_mux parallel
+sensor input endpoint (ipu1_csi0_mux_from_parallel_sensor).
+
+Regulators and port expanders are required for the ADV7180 (power pin
+is via port expander gpio on i2c3). The reset pin to the port expander
+chip (MAX7310) is controlled by a gpio, so a reset-gpios property must
+be defined under the port expander node to control it.
+
+The sabreauto uses a steering pin to select between the SDA signal on
+i2c3 bus, and a data-in pin for an SPI NOR chip. i2cmux can be used to
+control this steering pin. Idle state of the i2cmux selects SPI NOR.
+This is not classic way to use i2cmux, since one side of the mux selects
+something other than an i2c bus, but it works and is probably the cleanest
+solution. Note that if one thread is attempting to access SPI NOR while
+another thread is accessing i2c3, the SPI NOR access will fail since the
+i2cmux has selected the SDA pin rather than SPI NOR data-in. This couldn't
+be avoided in any case, the board is not designed to allow concurrent
+i2c3 and SPI NOR functions (and the default device-tree does not enable
+SPI NOR anyway).
+
+Endpoint ipu1_csi0_mux_from_parallel_sensor Optional Properties:
+- inputs        : list of input mux values, must be 0x00 followed by
+                  0x02 on SabreAuto;
+- input-names   : names of the inputs;
+
+ADV7180 Required properties:
+- compatible    : "adi,adv7180";
+- reg           : must be 0x21;
+
+ADV7180 Optional properties:
+- DOVDD-supply  : DOVDD regulator supply;
+- AVDD-supply   : AVDD regulator supply;
+- DVDD-supply   : DVDD regulator supply;
+- PVDD-supply   : PVDD regulator supply;
+- pwdn-gpio     : gpio to control ADV7180 power pin, must be
+                  <&port_exp_b 2 0> on SabreAuto;
+- interrupts    : interrupt from ADV7180, must be <27 0x8> on SabreAuto;
+- interrupt-parent : must be <&gpio1> on SabreAuto;
+
+ADV7180 Endpoint Required properties:
+- remote-endpoint : must connect to parallel sensor interface input endpoint 
+  		    on ipu1_csi0 video mux (ipu1_csi0_mux_from_parallel_sensor).
+- bus-width       : must be 8;
+
+
+The following is an example devicetree video capture configuration for
+SabreAuto:
+
+/ {
+	i2cmux {
+		i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+
+			camera: adv7180@21 {
+				compatible = "adi,adv7180";
+				reg = <0x21>;
+				pwdn-gpio = <&port_exp_b 2 0>;
+				interrupt-parent = <&gpio1>;
+				interrupts = <27 0x8>;
+
+				port {
+					adv7180_to_ipu1_csi0_mux: endpoint {
+						remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+						bus-width = <8>;
+					};
+				};
+			};
+
+			port_exp_b: gpio_pca953x@32 {
+				compatible = "maxim,max7310";
+				gpio-controller;
+				#gpio-cells = <2>;
+				reg = <0x32>;
+				reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
+			};
+
+		};
+	};
+
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>;
+		status = "okay";
+
+		fim {
+			enable = <1>;
+			tolerance-range = <20 0>;
+			num-avg = <1>;
+			input-capture-channel = <0 IRQ_TYPE_EDGE_RISING>;
+		};
+	};
+};
+
+&ipu1_csi0_from_ipu1_csi0_mux {
+        bus-width = <8>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&adv7180_to_ipu1_csi0_mux>;
+	inputs = <0x00 0x02>;
+	input-names = "ADV7180 Composite on Ain1", "ADV7180 Composite on Ain3";
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+
+/* input capture requires the input capture pin */
+&gpt {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_gpt_input_capture0>;
+};
+
+/* enabling input capture requires disabling SDHC1 */
+&usdhc1 {
+	status = "disabled";
+};
+
+
+
+SabreSD Quad with OV5642 and MIPI CSI-2 OV5640
+----------------------------------------------
+
+On the imx6q SabreSD, two camera sensors are supported: a parallel interface
+OV5642 on IPU1 CSI0, and a MIPI CSI-2 OV5640 on IPU1 CSI1 on MIPI virtual
+channel 1. The OV5642 connects to i2c bus 1 (i2c1) and the OV5640 to i2c
+bus 2 (i2c2).
+
+The mipi_csi2 receiver node must be enabled and its input endpoint connected
+via remote-endpoint to the OV5640 MIPI CSI-2 endpoint.
+
+OV5642 properties are as described above on SabreLite.
+
+OV5640 Required properties:
+- compatible	: "ovti,ov5640_mipi";
+- clocks        : the OV5640 system clock (cko, 201);
+- clock-names	: must be "xclk";
+- reg           : must be 0x3c;
+- xclk          : the system clock frequency, must be 24000000;
+- reset-gpios   : must be <&gpio1 20 1>;
+- pwdn-gpios    : must be <&gpio1 19 0>;
+
+OV5640 Optional properties:
+- DOVDD-supply  : DOVDD regulator supply;
+- AVDD-supply   : AVDD regulator supply;
+- DVDD-supply   : DVDD regulator supply;
+
+OV5640 MIPI CSI-2 Endpoint Required properties:
+- remote-endpoint : must connect to mipi_csi receiver input endpoint
+  		    (mipi_csi_from_mipi_sensor).
+- reg             : must be 1; /* virtual channel 1 */
+- data-lanes      : must be <0 1>;
+- clock-lanes     : must be <2>;
+
+
+The following is an example devicetree video capture configuration for
+SabreSD:
+
+/ {
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>, <&ipu1_csi1>;
+		status = "okay";
+	};
+};
+
+&i2c1 {
+	camera: ov5642@3c {
+		compatible = "ovti,ov5642";
+		clocks = <&clks 201>;
+		clock-names = "xclk";
+		reg = <0x3c>;
+		xclk = <24000000>;
+		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
+		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
+						rev B board is VGEN5 */
+		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
+		pwdn-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;   /* SD1_DAT0 */
+		reset-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; /* SD1_DAT1 */
+
+		port {
+			ov5642_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <8>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+			};
+		};
+	};
+};
+
+&i2c2 {
+	mipi_camera: ov5640@3c {
+		compatible = "ovti,ov5640_mipi";
+		reg = <0x3c>;
+		clocks = <&clks 201>;
+		clock-names = "xclk";
+		xclk = <24000000>;
+		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
+		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
+						rev B board is VGEN5 */
+		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
+		pwdn-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; /* SD1_DAT2 */
+		reset-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; /* SD1_CLK */
+
+		port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ov5640_to_mipi_csi: endpoint@1 {
+				reg = <1>; /* virtual channel 1 */
+				remote-endpoint = <&mipi_csi_from_mipi_sensor>;
+				data-lanes = <0 1>;
+				clock-lanes = <2>;
+			};
+		};
+	};
+};
+
+ipu1_csi0_from_ipu1_csi0_mux {
+	bus-width = <8>;
+	data-shift = <12>; /* Lines 19:12 used */
+	hsync-active = <1>;
+	vsync-active = <1>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&ov5642_to_ipu1_csi0_mux>;
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+
+&mipi_csi {
+	status = "okay";
+};
+
+/* Incoming port from sensor */
+&mipi_csi_from_mipi_sensor {
+	remote-endpoint = <&ov5640_to_mipi_csi>;
+	data-lanes = <0 1>;
+	clock-lanes = <2>;
+};
+
+&ipu1_csi1_from_mipi_vc1 {
+	data-lanes = <0 1>;
+	clock-lanes = <2>;
+};
diff --git a/Documentation/video4linux/imx_camera.txt b/Documentation/video4linux/imx_camera.txt
new file mode 100644
index 0000000..1d391c2
--- /dev/null
+++ b/Documentation/video4linux/imx_camera.txt
@@ -0,0 +1,243 @@
+                         i.MX Video Capture Driver
+                         ==========================
+
+Introduction
+------------
+
+The Freescale i.MX5/6 contains an Image Processing Unit (IPU), which
+handles the flow of image frames to and from capture devices and
+display devices.
+
+For image capture, the IPU contains the following subunits:
+
+- Image DMA Controller (IDMAC)
+- Camera Serial Interface (CSI)
+- Image Converter (IC)
+- Sensor Multi-FIFO Controller (SMFC)
+- Image Rotator (IRT)
+- Video De-Interlace Controller (VDIC)
+
+The IDMAC is the DMA controller for transfer of image frames to and from
+memory. Various dedicated DMA channels exist for both video capture and
+display paths.
+
+The CSI is the frontend capture unit that interfaces directly with
+capture devices over Parallel, BT.656, and MIPI CSI-2 busses.
+
+The IC handles color-space conversion, resizing, and rotation
+operations.
+
+The SMFC is used to send image frames directly to memory, bypassing the
+IC. The SMFC is used when no color-space conversion or resizing is
+required, i.e. the requested V4L2 formats and color-space are identical
+to raw frames from the capture device.
+
+The IRT carries out 90 and 270 degree image rotation operations.
+
+Finally, the VDIC handles the conversion of interlaced video to
+progressive, with support for different motion compensation modes (low
+and high).
+
+For more info, refer to the latest versions of the i.MX5/6 reference
+manuals listed under References.
+
+
+Features
+--------
+
+Some of the features of this driver include:
+
+- Supports parallel, BT.565, and MIPI CSI-2 interfaces.
+
+- Multiple subdev sensors can be registered and controlled by a single
+  interface driver instance. Input enumeration will list every registered
+  sensor's inputs and input names, and setting an input will switch to
+  a different sensor if the input index is handled by a different sensor.
+
+- Simultaneous streaming from two separate sensors is possible with two
+  interface driver instances, each instance controlling a different
+  sensor. This is currently possible with the SabreSD reference board
+  with OV5642 and MIPI CSI-2 OV5640 sensors.
+
+- Scaling, color-space conversion, and image rotation.
+
+- Many pixel formats supported (RGB, packed and planar YUV, partial
+  planar YUV).
+
+- Full device-tree support using OF graph bindings.
+
+- Analog decoder input video source hot-swap support (during streaming)
+  via decoder status change subdev notification.
+
+- MMAP, USERPTR, and DMABUF importer/exporter buffers supported.
+
+- Motion compensated de-interlacing using the VDIC, with three
+  motion compensation modes: low, medium, and high motion. The mode is
+  specified with a custom control.
+
+- Includes a Frame Interval Monitor (FIM) that can correct vertical sync
+  problems with the ADV718x video decoders. See below for a description
+  of the FIM.
+
+
+Usage Notes
+-----------
+
+The i.MX capture driver is a standardized driver that supports the
+following community V4L2 tools:
+
+- v4l2-ctl
+- v4l2-cap
+- v4l2src gstreamer plugin
+
+
+The following platforms have been tested:
+
+
+SabreLite with parallel-interface OV5642
+----------------------------------------
+
+This platform requires the OmniVision OV5642 module with a parallel
+camera interface from Boundary Devices for the SabreLite
+(http://boundarydevices.com/products/nit6x_5mp/).
+
+There is a pin conflict between OV5642 and ethernet devices on this
+platform, so by default video capture is disabled in the device tree. To
+enable video capture, edit arch/arm/boot/dts/imx6qdl-sabrelite.dtsi and
+uncomment the macro __OV5642_CAPTURE__.
+
+
+SabreAuto with ADV7180 decoder
+------------------------------
+
+This platform accepts Composite Video analog inputs on Ain1 (connector
+J42) and Ain3 (connector J43).
+
+To switch to Ain1:
+
+# v4l2-ctl -i0
+
+To switch to Ain3:
+
+# v4l2-ctl -i1
+
+
+Frame Interval Monitor
+----------------------
+
+The adv718x decoders can occasionally send corrupt fields during
+NTSC/PAL signal re-sync (too little or too many video lines). When
+this happens, the IPU triggers a mechanism to re-establish vertical
+sync by adding 1 dummy line every frame, which causes a rolling effect
+from image to image, and can last a long time before a stable image is
+recovered. Or sometimes the mechanism doesn't work at all, causing a
+permanent split image (one frame contains lines from two consecutive
+captured images).
+
+From experiment it was found that during image rolling, the frame
+intervals (elapsed time between two EOF's) drop below the nominal
+value for the current standard, by about one frame time (60 usec),
+and remain at that value until rolling stops.
+
+While the reason for this observation isn't known (the IPU dummy
+line mechanism should show an increase in the intervals by 1 line
+time every frame, not a fixed value), we can use it to detect the
+corrupt fields using a frame interval monitor. If the FIM detects a
+bad frame interval, the camera interface driver restarts IPU capture
+which corrects the rolling/split image.
+
+Custom controls exist to tweak some dials for FIM. If one of these
+controls is changed during streaming, the FIM will be reset and will
+continue at the new settings.
+
+- V4L2_CID_IMX_FIM_ENABLE
+
+Enable/disable the FIM.
+
+- V4L2_CID_IMX_FIM_NUM
+
+How many frame interval errors to average before comparing against the nominal
+frame interval reported by the sensor. This can reduce noise from interrupt
+latency.
+
+- V4L2_CID_IMX_FIM_TOLERANCE_MIN
+
+If the averaged intervals fall outside nominal by this amount, in
+microseconds, streaming will be restarted.
+
+- V4L2_CID_IMX_FIM_TOLERANCE_MAX
+
+If any interval errors are higher than this value, those error samples
+are discarded and do not enter into the average. This can be used to
+discard really high interval errors that might be due to very high
+system load, causing excessive interrupt latencies.
+
+- V4L2_CID_IMX_FIM_NUM_SKIP
+
+How many frames to skip after a FIM reset or stream restart before
+FIM begins to average intervals. It has been found that there are
+always a few bad frame intervals after stream restart, so this is
+used to skip those frames to prevent endless restarts.
+
+Finally, all the defaults for these controls can be modified via a
+device tree child node of the capture node, see
+Documentation/devicetree/bindings/media/imx.txt.
+
+
+SabreSD with MIPI CSI-2 OV5640
+------------------------------
+
+The default device tree for SabreSD includes endpoints for both the
+parallel OV5642 and the MIPI CSI-2 OV5640, but as of this writing only
+the MIPI CSI-2 OV5640 has been tested. The OV5640 module connects to
+MIPI connector J5 (sorry I don't have the compatible module part number
+or URL).
+
+Inputs are registered for both the OV5642 and OV5640, and by default the
+OV5642 is selected. To switch to the OV5640:
+
+# v4l2-ctl -i1
+
+
+Known Issues
+------------
+
+1. When using 90 or 270 degree rotation control at capture resolutions
+   near the IC resizer limit of 1024x1024, and combined with planar
+   pixel formats (YUV420, YUV422p), frame capture will often fail with
+   no end-of-frame interrupts from the IDMAC channel. To work around
+   this, use lower resolution and/or packed formats (YUYV, RGB3, etc.)
+   when 90 or 270 rotations are needed.
+
+2. Simple IDMAC interleaving using the ILO field in the IDMAC cpmem
+   doesn't work when combined with the 16-bit planar pixel formats
+   (YUV422P and NV16). This looks like a silicon bug, and there is
+   no satisfactory replies to queries about it from Freescale. So
+   the driver works around the issue by forcing the format to the
+   12-bit planar versions (YUV420 and NV12) when simple interleaving
+   is used and the sensor sends interlaced fields (ADV718x). Another
+   option to workaround the issue is to use motion compensation when
+   combined with YUV422P or NV16.
+
+File list
+---------
+
+drivers/staging/media/imx/capture/
+include/media/imx.h
+include/uapi/media/imx.h
+
+References
+----------
+
+[1] "i.MX 6Dual/6Quad Applications Processor Reference Manual"
+[2] "i.MX 6Solo/6DualLite Applications Processor Reference Manual"
+
+
+Authors
+-------
+Steve Longerbeam <steve_longerbeam@mentor.com>
+Dmitry Eremin-Solenikov <dmitry_eremin@mentor.com>
+Jiada Wang <jiada_wang@mentor.com>
+Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
+
+Copyright (C) 2012-2016 Mentor Graphics Inc.
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index ee91868..1ebcd1d 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -25,6 +25,8 @@ source "drivers/staging/media/cxd2099/Kconfig"
 
 source "drivers/staging/media/davinci_vpfe/Kconfig"
 
+source "drivers/staging/media/imx/Kconfig"
+
 source "drivers/staging/media/omap4iss/Kconfig"
 
 source "drivers/staging/media/tw686x-kh/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 8c05d0a..9f00aea 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_I2C_BCM2048)	+= bcm2048/
 obj-$(CONFIG_DVB_CXD2099)	+= cxd2099/
+obj-$(CONFIG_VIDEO_IMX)		+= imx/
 obj-$(CONFIG_LIRC_STAGING)	+= lirc/
 obj-$(CONFIG_VIDEO_DM365_VPFE)	+= davinci_vpfe/
 obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
new file mode 100644
index 0000000..65e1645
--- /dev/null
+++ b/drivers/staging/media/imx/Kconfig
@@ -0,0 +1,23 @@
+config VIDEO_IMX
+	tristate "i.MX5/6 V4L2 devices"
+	depends on VIDEO_V4L2 && ARCH_MXC && IMX_IPUV3_CORE
+	default y
+	---help---
+	  Say yes here to enable support for video4linux drivers for
+	  the i.MX5/6 SOC.
+
+config VIDEO_IMX_CAMERA
+	tristate "i.MX5/6 Camera Interface driver"
+	depends on VIDEO_IMX && VIDEO_DEV && I2C
+	select VIDEOBUF2_DMA_CONTIG
+	default y
+	---help---
+	  A video4linux capture driver for i.MX5/6 SOC. Some of the
+	  features of this driver include MIPI CSI-2 sensor support,
+	  hardware scaling, colorspace conversion, and rotation,
+	  simultaneous capture from separate sensors, dmabuf
+	  importer/exporter, and full devicetree support.
+
+if VIDEO_IMX_CAMERA
+source "drivers/staging/media/imx/capture/Kconfig"
+endif
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
new file mode 100644
index 0000000..7c97629
--- /dev/null
+++ b/drivers/staging/media/imx/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_IMX_CAMERA) += capture/
diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
new file mode 100644
index 0000000..ee2cbab
--- /dev/null
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -0,0 +1,3 @@
+menu "i.MX5/6 Camera Sub devices"
+
+endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
new file mode 100644
index 0000000..5c965f9
--- /dev/null
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -0,0 +1,5 @@
+imx-camera-objs := imx-camif.o imx-ic-prpenc.o imx-of.o \
+		imx-smfc.o imx-vdic.o
+
+obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
+obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
diff --git a/drivers/staging/media/imx/capture/imx-camif.c b/drivers/staging/media/imx/capture/imx-camif.c
new file mode 100644
index 0000000..0276426
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-camif.c
@@ -0,0 +1,2496 @@
+/*
+ * Video Camera Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/mxc_icap.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+#include "imx-of.h"
+
+/*
+ * Min/Max supported width and heights.
+ */
+#define MIN_W       176
+#define MIN_H       144
+#define MAX_W      8192
+#define MAX_H      4096
+#define MAX_W_IC   1024
+#define MAX_H_IC   1024
+#define MAX_W_VDIC  968
+#define MAX_H_VDIC 2048
+
+#define H_ALIGN    3 /* multiple of 8 */
+#define S_ALIGN    1 /* multiple of 2 */
+
+#define DEVICE_NAME "imx-camera"
+
+/* In bytes, per queue */
+#define VID_MEM_LIMIT	SZ_64M
+
+static struct vb2_ops imxcam_qops;
+
+static inline struct imxcam_dev *sd2dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd->v4l2_dev, struct imxcam_dev, v4l2_dev);
+}
+
+static inline struct imxcam_dev *notifier2dev(struct v4l2_async_notifier *n)
+{
+	return container_of(n, struct imxcam_dev, subdev_notifier);
+}
+
+static inline struct imxcam_dev *fim2dev(struct imxcam_fim *fim)
+{
+	return container_of(fim, struct imxcam_dev, fim);
+}
+
+static inline struct imxcam_ctx *file2ctx(struct file *file)
+{
+	return container_of(file->private_data, struct imxcam_ctx, fh);
+}
+
+static inline bool is_io_ctx(struct imxcam_ctx *ctx)
+{
+	return ctx == ctx->dev->io_ctx;
+}
+
+/* forward references */
+static void imxcam_bump_restart_timer(struct imxcam_ctx *ctx);
+
+/* Supported user and sensor pixel formats */
+static struct imxcam_pixfmt imxcam_pixformats[] = {
+	{
+		.name	= "RGB565",
+		.fourcc	= V4L2_PIX_FMT_RGB565,
+		.codes  = {MEDIA_BUS_FMT_RGB565_2X8_LE},
+		.bpp    = 16,
+	}, {
+		.name	= "RGB24",
+		.fourcc	= V4L2_PIX_FMT_RGB24,
+		.codes  = {MEDIA_BUS_FMT_RGB888_1X24,
+			   MEDIA_BUS_FMT_RGB888_2X12_LE},
+		.bpp    = 24,
+	}, {
+		.name	= "BGR24",
+		.fourcc	= V4L2_PIX_FMT_BGR24,
+		.bpp    = 24,
+	}, {
+		.name	= "RGB32",
+		.fourcc	= V4L2_PIX_FMT_RGB32,
+		.codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
+		.bpp   = 32,
+	}, {
+		.name	= "BGR32",
+		.fourcc	= V4L2_PIX_FMT_BGR32,
+		.bpp    = 32,
+	}, {
+		.name	= "4:2:2 packed, YUYV",
+		.fourcc	= V4L2_PIX_FMT_YUYV,
+		.codes = {MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_1X16},
+		.bpp   = 16,
+	}, {
+		.name	= "4:2:2 packed, UYVY",
+		.fourcc	= V4L2_PIX_FMT_UYVY,
+		.codes = {MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_1X16},
+		.bpp   = 16,
+	}, {
+		.name	= "4:2:0 planar, YUV",
+		.fourcc	= V4L2_PIX_FMT_YUV420,
+		.bpp    = 12,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:0 planar, YVU",
+		.fourcc = V4L2_PIX_FMT_YVU420,
+		.bpp    = 12,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:2 planar, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.bpp    = 16,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:0 planar, Y/CbCr",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.bpp    = 12,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:2 planar, Y/CbCr",
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.bpp    = 16,
+		.y_depth = 8,
+	},
+};
+
+#define NUM_FORMATS ARRAY_SIZE(imxcam_pixformats)
+
+static struct imxcam_pixfmt *imxcam_get_format(u32 fourcc, u32 code)
+{
+	struct imxcam_pixfmt *fmt, *ret = NULL;
+	int i, j;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		fmt = &imxcam_pixformats[i];
+
+		if (fourcc && fmt->fourcc == fourcc) {
+			ret = fmt;
+			goto out;
+		}
+
+		for (j = 0; fmt->codes[j]; j++) {
+			if (fmt->codes[j] == code) {
+				ret = fmt;
+				goto out;
+			}
+		}
+	}
+out:
+	return ret;
+}
+
+/* Support functions */
+
+/* find the sensor that is handling this input index */
+static struct imxcam_sensor *
+find_sensor_by_input_index(struct imxcam_dev *dev, int input_idx)
+{
+	struct imxcam_sensor *sensor;
+	int i;
+
+	for (i = 0; i < dev->num_sensors; i++) {
+		sensor = &dev->sensor_list[i];
+		if (!sensor->sd)
+			continue;
+
+		if (input_idx >= sensor->input.first &&
+		    input_idx <= sensor->input.last)
+			break;
+	}
+
+	return (i < dev->num_sensors) ? sensor : NULL;
+}
+
+/*
+ * Set all the video muxes required to receive data from the
+ * current sensor.
+ */
+static int imxcam_set_video_muxes(struct imxcam_dev *dev)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+	int i, ret;
+
+	for (i = 0; i < IMXCAM_MAX_VIDEOMUX; i++) {
+		if (sensor->vidmux_input[i] < 0)
+			continue;
+		dev_dbg(dev->dev, "%s: vidmux %d, input %d\n",
+			sensor->sd->name, i, sensor->vidmux_input[i]);
+		ret = v4l2_subdev_call(dev->vidmux_list[i], video, s_routing,
+				       sensor->vidmux_input[i], 0, 0);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Query sensor and update signal lock status. Returns true if lock
+ * status has changed.
+ */
+static bool update_signal_lock_status(struct imxcam_dev *dev)
+{
+	bool locked, changed;
+	u32 status;
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor->sd, video, g_input_status, &status);
+	if (ret)
+		return false;
+
+	locked = ((status & (V4L2_IN_ST_NO_SIGNAL | V4L2_IN_ST_NO_SYNC)) == 0);
+	changed = (dev->signal_locked != locked);
+	dev->signal_locked = locked;
+
+	return changed;
+}
+
+/*
+ * Return true if the VDIC deinterlacer is needed. We need the VDIC
+ * if the sensor is transmitting fields, and userland is requesting
+ * motion compensation (rather than simple weaving).
+ */
+static bool need_vdic(struct imxcam_dev *dev,
+		      struct v4l2_mbus_framefmt *sf)
+{
+	return dev->motion != MOTION_NONE && V4L2_FIELD_HAS_BOTH(sf->field);
+}
+
+/*
+ * Return true if sensor format currently meets the VDIC
+ * restrictions:
+ *     o the full-frame resolution to the VDIC must be at or below 968x2048.
+ *     o the pixel format to the VDIC must be YUV422
+ */
+static bool can_use_vdic(struct imxcam_dev *dev,
+			 struct v4l2_mbus_framefmt *sf)
+{
+	return sf->width <= MAX_W_VDIC &&
+		sf->height <= MAX_H_VDIC &&
+		(sf->code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+		 sf->code == MEDIA_BUS_FMT_UYVY8_1X16 ||
+		 sf->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+		 sf->code == MEDIA_BUS_FMT_YUYV8_1X16);
+}
+
+/*
+ * Return true if the current capture parameters require the use of
+ * the Image Converter. We need the IC for scaling, colorspace conversion,
+ * and rotation.
+ */
+static bool need_ic(struct imxcam_dev *dev,
+		    struct v4l2_mbus_framefmt *sf,
+		    struct v4l2_format *uf,
+		    struct v4l2_rect *crop)
+{
+	struct v4l2_pix_format *user_fmt = &uf->fmt.pix;
+	enum ipu_color_space sensor_cs, user_cs;
+	bool ret;
+
+	sensor_cs = ipu_mbus_code_to_colorspace(sf->code);
+	user_cs = ipu_pixelformat_to_colorspace(user_fmt->pixelformat);
+
+	ret = (user_fmt->width != crop->width ||
+	       user_fmt->height != crop->height ||
+	       user_cs != sensor_cs ||
+	       dev->rot_mode != IPU_ROTATE_NONE);
+
+	return ret;
+}
+
+/*
+ * Return true if user and sensor formats currently meet the IC
+ * restrictions:
+ *     o the parallel CSI bus cannot be 16-bit wide.
+ *     o the endpoint id of the CSI this sensor connects to must be 0
+ *       (for MIPI CSI2, the endpoint id is the virtual channel number,
+ *        and only VC0 can pass through the IC).
+ *     o the resizer output size must be at or below 1024x1024.
+ */
+static bool can_use_ic(struct imxcam_dev *dev,
+		       struct v4l2_mbus_framefmt *sf,
+		       struct v4l2_format *uf)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+
+	return (sensor->ep.bus_type == V4L2_MBUS_CSI2 ||
+		sensor->ep.bus.parallel.bus_width < 16) &&
+		sensor->csi_ep.base.id == 0 &&
+		uf->fmt.pix.width <= MAX_W_IC &&
+		uf->fmt.pix.height <= MAX_H_IC;
+}
+
+/*
+ * Adjusts passed width and height to meet IC resizer limits.
+ */
+static void adjust_to_resizer_limits(struct imxcam_dev *dev,
+				     struct v4l2_format *uf,
+				     struct v4l2_rect *crop)
+{
+	u32 *width, *height;
+
+	if (uf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		width = &uf->fmt.pix.width;
+		height = &uf->fmt.pix.height;
+	} else {
+		width = &uf->fmt.win.w.width;
+		height = &uf->fmt.win.w.height;
+	}
+
+	/* output of resizer can't be above 1024x1024 */
+	*width = min_t(__u32, *width, MAX_W_IC);
+	*height = min_t(__u32, *height, MAX_H_IC);
+
+	/* resizer cannot downsize more than 4:1 */
+	if (ipu_rot_mode_is_irt(dev->rot_mode)) {
+		*height = max_t(__u32, *height, crop->width / 4);
+		*width = max_t(__u32, *width, crop->height / 4);
+	} else {
+		*width = max_t(__u32, *width, crop->width / 4);
+		*height = max_t(__u32, *height, crop->height / 4);
+	}
+}
+
+static void adjust_user_fmt(struct imxcam_dev *dev,
+			    struct v4l2_mbus_framefmt *sf,
+			    struct v4l2_format *uf,
+			    struct v4l2_rect *crop)
+{
+	struct imxcam_pixfmt *fmt;
+
+	/*
+	 * Make sure resolution is within IC resizer limits
+	 * if we need the Image Converter.
+	 */
+	if (need_ic(dev, sf, uf, crop))
+		adjust_to_resizer_limits(dev, uf, crop);
+
+	/*
+	 * Force the resolution to match crop window if
+	 * we can't use the Image Converter.
+	 */
+	if (!can_use_ic(dev, sf, uf)) {
+		uf->fmt.pix.width = crop->width;
+		uf->fmt.pix.height = crop->height;
+	}
+
+	fmt = imxcam_get_format(uf->fmt.pix.pixelformat, 0);
+
+	uf->fmt.pix.bytesperline = (uf->fmt.pix.width * fmt->bpp) >> 3;
+	uf->fmt.pix.sizeimage = uf->fmt.pix.height * uf->fmt.pix.bytesperline;
+}
+
+/*
+ * calculte the default active crop window, given a sensor frame and
+ * video standard. This crop window will be stored to dev->crop_defrect.
+ */
+static void calc_default_crop(struct imxcam_dev *dev,
+			      struct v4l2_rect *rect,
+			      struct v4l2_mbus_framefmt *sf,
+			      v4l2_std_id std)
+{
+	rect->width = sf->width;
+	rect->height = sf->height;
+	rect->top = 0;
+	rect->left = 0;
+
+	/*
+	 * FIXME: For NTSC standards, top must be set to an
+	 * offset of 13 lines to match fixed CCIR programming
+	 * in the IPU.
+	 */
+	if (std != V4L2_STD_UNKNOWN && (std & V4L2_STD_525_60))
+		rect->top = 13;
+
+	/* adjust crop window to h/w alignment restrictions */
+	rect->width &= ~0x7;
+}
+
+static int update_sensor_std(struct imxcam_dev *dev)
+{
+	return v4l2_subdev_call(dev->sensor->sd, video, querystd,
+				&dev->current_std);
+}
+
+static void update_fim(struct imxcam_dev *dev)
+{
+	struct imxcam_fim *fim = &dev->fim;
+
+	if (dev->sensor_tpf.denominator == 0) {
+		fim->enabled = false;
+		return;
+	}
+
+	fim->nominal = DIV_ROUND_CLOSEST(
+		1000 * 1000 * dev->sensor_tpf.numerator,
+		dev->sensor_tpf.denominator);
+}
+
+static int update_sensor_fmt(struct imxcam_dev *dev)
+{
+	struct v4l2_subdev_format fmt;
+	struct v4l2_streamparm parm;
+	struct v4l2_rect crop;
+	int ret;
+
+	update_sensor_std(dev);
+
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt.pad = 0;
+
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, get_fmt, NULL, &fmt);
+	if (ret)
+		return ret;
+
+	dev->sensor_fmt = fmt.format;
+
+	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	ret = v4l2_subdev_call(dev->sensor->sd, video, g_parm, &parm);
+	if (ret)
+		memset(&dev->sensor_tpf, 0, sizeof(dev->sensor_tpf));
+	else
+		dev->sensor_tpf = parm.parm.capture.timeperframe;
+	update_fim(dev);
+
+	ret = v4l2_subdev_call(dev->sensor->sd, video, g_mbus_config,
+			       &dev->mbus_cfg);
+	if (ret)
+		return ret;
+
+	dev->sensor_pixfmt = imxcam_get_format(0, dev->sensor_fmt.code);
+
+	/* get new sensor default crop window */
+	calc_default_crop(dev, &crop, &dev->sensor_fmt, dev->current_std);
+
+	/* and update crop bounds */
+	dev->crop_bounds.top = dev->crop_bounds.left = 0;
+	dev->crop_bounds.width = crop.width + (u32)crop.left;
+	dev->crop_bounds.height = crop.height + (u32)crop.top;
+
+	/*
+	 * reset the user crop window to defrect if defrect has changed,
+	 * or if user crop is not initialized yet.
+	 */
+	if (dev->crop_defrect.width != crop.width ||
+	    dev->crop_defrect.left != crop.left ||
+	    dev->crop_defrect.height != crop.height ||
+	    dev->crop_defrect.top != crop.top ||
+	    !dev->crop.width || !dev->crop.height) {
+		dev->crop_defrect = crop;
+		dev->crop = dev->crop_defrect;
+	}
+
+	return 0;
+}
+
+/*
+ * Turn current sensor power on/off according to power_count.
+ */
+static int sensor_set_power(struct imxcam_dev *dev, int on)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+	struct v4l2_subdev *sd = sensor->sd;
+	int ret;
+
+	if (on && sensor->power_count++ > 0)
+		return 0;
+	else if (!on && (sensor->power_count == 0 ||
+			 --sensor->power_count > 0))
+		return 0;
+
+	if (on) {
+		/* power-on the csi2 receiver */
+		if (sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd) {
+			ret = v4l2_subdev_call(dev->csi2_sd, core, s_power,
+					       true);
+			if (ret)
+				goto out;
+		}
+
+		ret = v4l2_subdev_call(sd, core, s_power, true);
+		if (ret && ret != -ENOIOCTLCMD)
+			goto csi2_off;
+	} else {
+		v4l2_subdev_call(sd, core, s_power, false);
+		if (sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+			v4l2_subdev_call(dev->csi2_sd, core, s_power, false);
+	}
+
+	return 0;
+
+csi2_off:
+	if (sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+		v4l2_subdev_call(dev->csi2_sd, core, s_power, false);
+out:
+	sensor->power_count--;
+	return ret;
+}
+
+static void reset_fim(struct imxcam_dev *dev, bool curval)
+{
+	struct imxcam_fim *fim = &dev->fim;
+	struct v4l2_ctrl *en = fim->ctrl[FIM_CL_ENABLE];
+	struct v4l2_ctrl *num = fim->ctrl[FIM_CL_NUM];
+	struct v4l2_ctrl *skip = fim->ctrl[FIM_CL_NUM_SKIP];
+	struct v4l2_ctrl *tol_min = fim->ctrl[FIM_CL_TOLERANCE_MIN];
+	struct v4l2_ctrl *tol_max = fim->ctrl[FIM_CL_TOLERANCE_MAX];
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	if (curval) {
+		fim->enabled = en->cur.val;
+		fim->num_avg = num->cur.val;
+		fim->num_skip = skip->cur.val;
+		fim->tolerance_min = tol_min->cur.val;
+		fim->tolerance_max = tol_max->cur.val;
+	} else {
+		fim->enabled = en->val;
+		fim->num_avg = num->val;
+		fim->num_skip = skip->val;
+		fim->tolerance_min = tol_min->val;
+		fim->tolerance_max = tol_max->val;
+	}
+
+	/* disable tolerance range if max <= min */
+	if (fim->tolerance_max <= fim->tolerance_min)
+		fim->tolerance_max = 0;
+
+	fim->counter = -fim->num_skip;
+	fim->sum = 0;
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Monitor an averaged frame interval. If the average deviates too much
+ * from the sensor's nominal frame rate, return -EIO. The frame intervals
+ * are averaged in order to quiet noise from (presumably random) interrupt
+ * latency.
+ */
+static int frame_interval_monitor(struct imxcam_fim *fim, struct timespec *ts)
+{
+	unsigned long interval, error, error_avg;
+	struct imxcam_dev *dev = fim2dev(fim);
+	struct timespec diff;
+	int ret = 0;
+
+	if (++fim->counter <= 0)
+		goto out_update_ts;
+
+	diff = timespec_sub(*ts, fim->last_ts);
+	interval = diff.tv_sec * 1000 * 1000 + diff.tv_nsec / 1000;
+	error = abs(interval - fim->nominal);
+
+	if (fim->tolerance_max && error >= fim->tolerance_max) {
+		dev_dbg(dev->dev,
+			"FIM: %lu ignored, out of tolerance bounds\n",
+			error);
+		fim->counter--;
+		goto out_update_ts;
+	}
+
+	fim->sum += error;
+
+	if (fim->counter == fim->num_avg) {
+		error_avg = DIV_ROUND_CLOSEST(fim->sum, fim->num_avg);
+
+		if (error_avg > fim->tolerance_min)
+			ret = -EIO;
+
+		dev_dbg(dev->dev, "FIM: error: %lu usec%s\n",
+			error_avg, ret ? " (!!!)" : "");
+
+		fim->counter = 0;
+		fim->sum = 0;
+	}
+
+out_update_ts:
+	fim->last_ts = *ts;
+	return ret;
+}
+
+/*
+ * Called by the encode and vdic subdevs in their EOF interrupt
+ * handlers with the irqlock held. This way of measuring frame
+ * intervals is subject to errors introduced by interrupt latency.
+ */
+static int fim_eof_handler(struct imxcam_dev *dev, struct timeval *now)
+{
+	struct imxcam_fim *fim = &dev->fim;
+	struct timespec ts;
+
+	if (!fim->enabled)
+		return 0;
+
+	ts.tv_sec = now->tv_sec;
+	ts.tv_nsec = now->tv_usec * 1000;
+
+	return frame_interval_monitor(fim, &ts);
+}
+
+/*
+ * Input Capture method of measuring frame intervals. Not subject
+ * to interrupt latency.
+ */
+static void fim_input_capture_handler(int channel, void *dev_id,
+				      struct timespec *now)
+{
+	struct imxcam_fim *fim = dev_id;
+	struct imxcam_dev *dev = fim2dev(fim);
+	struct imxcam_ctx *ctx;
+	unsigned long flags;
+
+	if (!fim->enabled)
+		return;
+
+	if (!frame_interval_monitor(fim, now))
+		return;
+
+	spin_lock_irqsave(&dev->notify_lock, flags);
+	ctx = dev->io_ctx;
+	if (ctx && !ctx->stop && !atomic_read(&dev->pending_restart))
+		imxcam_bump_restart_timer(ctx);
+	spin_unlock_irqrestore(&dev->notify_lock, flags);
+}
+
+static int fim_request_input_capture(struct imxcam_dev *dev)
+{
+	struct imxcam_fim *fim = &dev->fim;
+
+	if (fim->icap_channel < 0)
+		return 0;
+
+	return mxc_request_input_capture(fim->icap_channel,
+					 fim_input_capture_handler,
+					 fim->icap_flags, fim);
+}
+
+static void fim_free_input_capture(struct imxcam_dev *dev)
+{
+	struct imxcam_fim *fim = &dev->fim;
+
+	if (fim->icap_channel < 0)
+		return;
+
+	mxc_free_input_capture(fim->icap_channel, fim);
+}
+
+/*
+ * Turn current sensor and CSI streaming on/off according to stream_count.
+ */
+static int sensor_set_stream(struct imxcam_dev *dev, int on)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+	int ret;
+
+	if (on && sensor->stream_count++ > 0)
+		return 0;
+	else if (!on && (sensor->stream_count == 0 ||
+			 --sensor->stream_count > 0))
+		return 0;
+
+	if (on) {
+		ret = v4l2_subdev_call(sensor->sd, video, s_stream, true);
+		if (ret && ret != -ENOIOCTLCMD)
+			goto out;
+
+		if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd) {
+			ret = v4l2_subdev_call(dev->csi2_sd, video, s_stream,
+					       true);
+			if (ret)
+				goto sensor_off;
+		}
+
+		ret = v4l2_subdev_call(sensor->csi_sd, video, s_stream, true);
+		if (ret)
+			goto csi2_off;
+
+		ret = fim_request_input_capture(dev);
+		if (ret)
+			goto csi_off;
+	} else {
+		fim_free_input_capture(dev);
+		v4l2_subdev_call(sensor->csi_sd, video, s_stream, false);
+		if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+			v4l2_subdev_call(dev->csi2_sd, video, s_stream, false);
+		v4l2_subdev_call(sensor->sd, video, s_stream, false);
+	}
+
+	return 0;
+
+csi_off:
+	v4l2_subdev_call(sensor->csi_sd, video, s_stream, false);
+csi2_off:
+	if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+		v4l2_subdev_call(dev->csi2_sd, video, s_stream, false);
+sensor_off:
+	v4l2_subdev_call(sensor->sd, video, s_stream, false);
+out:
+	sensor->stream_count--;
+	return ret;
+}
+
+/*
+ * Start the encoder for buffer streaming. There must be at least two
+ * frames in the vb2 queue.
+ */
+static int start_encoder(struct imxcam_dev *dev)
+{
+	struct v4l2_subdev *streaming_sd;
+	int ret;
+
+	if (dev->encoder_on)
+		return 0;
+
+	if (dev->using_vdic)
+		streaming_sd = dev->vdic_sd;
+	else if (dev->using_ic)
+		streaming_sd = dev->prpenc_sd;
+	else
+		streaming_sd = dev->smfc_sd;
+
+	ret = v4l2_subdev_call(streaming_sd, video, s_stream, 1);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "encoder stream on failed\n");
+		return ret;
+	}
+
+	dev->encoder_on = true;
+	return 0;
+}
+
+/*
+ * Stop the encoder.
+ */
+static int stop_encoder(struct imxcam_dev *dev)
+{
+	struct v4l2_subdev *streaming_sd;
+	int ret;
+
+	if (!dev->encoder_on)
+		return 0;
+
+	if (dev->using_vdic)
+		streaming_sd = dev->vdic_sd;
+	else if (dev->using_ic)
+		streaming_sd = dev->prpenc_sd;
+	else
+		streaming_sd = dev->smfc_sd;
+
+	/* encoder/vdic off */
+	ret = v4l2_subdev_call(streaming_sd, video, s_stream, 0);
+	if (ret)
+		v4l2_err(&dev->v4l2_dev, "encoder stream off failed\n");
+
+	dev->encoder_on = false;
+	return ret;
+}
+
+/*
+ * Start/Stop streaming.
+ */
+static int set_stream(struct imxcam_ctx *ctx, bool on)
+{
+	struct imxcam_dev *dev = ctx->dev;
+	int ret = 0;
+
+	if (on) {
+		if (atomic_read(&dev->status_change)) {
+			update_signal_lock_status(dev);
+			update_sensor_fmt(dev);
+			atomic_set(&dev->status_change, 0);
+			v4l2_info(&dev->v4l2_dev, "at stream on: %s, %s\n",
+				  v4l2_norm_to_name(dev->current_std),
+				  dev->signal_locked ?
+				  "signal locked" : "no signal");
+		}
+
+		atomic_set(&dev->pending_restart, 0);
+
+		dev->using_ic =
+			(need_ic(dev, &dev->sensor_fmt, &dev->user_fmt,
+				 &dev->crop) &&
+			 can_use_ic(dev, &dev->sensor_fmt, &dev->user_fmt));
+
+		dev->using_vdic = need_vdic(dev, &dev->sensor_fmt) &&
+			can_use_vdic(dev, &dev->sensor_fmt);
+
+		reset_fim(dev, true);
+
+		/*
+		 * If there are two or more frames in the queue, we can start
+		 * the encoder now. Otherwise the encoding will start once
+		 * two frames have been queued.
+		 */
+		if (!list_empty(&ctx->ready_q) &&
+		    !list_is_singular(&ctx->ready_q))
+			ret = start_encoder(dev);
+	} else {
+		ret = stop_encoder(dev);
+	}
+
+	return ret;
+}
+
+/*
+ * Restart work handler. This is called in three cases during active
+ * streaming.
+ *
+ * o NFB4EOF errors
+ * o A decoder's signal lock status or autodetected video standard changes
+ * o End-of-Frame timeouts
+ */
+static void restart_work_handler(struct work_struct *w)
+{
+	struct imxcam_ctx *ctx = container_of(w, struct imxcam_ctx,
+					      restart_work);
+	struct imxcam_dev *dev = ctx->dev;
+
+	mutex_lock(&dev->mutex);
+
+	/* this can happen if we are releasing the io context */
+	if (!is_io_ctx(ctx))
+		goto out_unlock;
+
+	if (!vb2_is_streaming(&dev->buffer_queue))
+		goto out_unlock;
+
+	if (!ctx->stop) {
+		v4l2_warn(&dev->v4l2_dev, "restarting\n");
+		set_stream(ctx, false);
+		set_stream(ctx, true);
+	}
+
+out_unlock:
+	mutex_unlock(&dev->mutex);
+}
+
+/*
+ * Stop work handler. Not currently needed but keep around.
+ */
+static void stop_work_handler(struct work_struct *w)
+{
+	struct imxcam_ctx *ctx = container_of(w, struct imxcam_ctx,
+					      stop_work);
+	struct imxcam_dev *dev = ctx->dev;
+
+	mutex_lock(&dev->mutex);
+
+	if (vb2_is_streaming(&dev->buffer_queue)) {
+		v4l2_err(&dev->v4l2_dev, "stopping\n");
+		vb2_streamoff(&dev->buffer_queue, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	}
+
+	mutex_unlock(&dev->mutex);
+}
+
+/*
+ * Restart timer function. Schedules a restart.
+ */
+static void imxcam_restart_timeout(unsigned long data)
+{
+	struct imxcam_ctx *ctx = (struct imxcam_ctx *)data;
+
+	schedule_work(&ctx->restart_work);
+}
+
+/*
+ * bump the restart timer and set the pending restart flag.
+ * notify_lock must be held when calling.
+ */
+static void imxcam_bump_restart_timer(struct imxcam_ctx *ctx)
+{
+	struct imxcam_dev *dev = ctx->dev;
+
+	mod_timer(&ctx->restart_timer, jiffies +
+		  msecs_to_jiffies(IMXCAM_RESTART_DELAY));
+	atomic_set(&dev->pending_restart, 1);
+}
+
+/* Controls */
+static int imxcam_set_rotation(struct imxcam_dev *dev,
+			       int rotation, bool hflip, bool vflip)
+{
+	enum ipu_rotate_mode rot_mode;
+	int ret;
+
+	ret = ipu_degrees_to_rot_mode(&rot_mode, rotation,
+				      hflip, vflip);
+	if (ret)
+		return ret;
+
+	if (rot_mode != dev->rot_mode) {
+		/* can't change rotation mid-streaming */
+		if (vb2_is_streaming(&dev->buffer_queue)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: not allowed while streaming\n",
+				 __func__);
+			return -EBUSY;
+		}
+
+		if (rot_mode != IPU_ROTATE_NONE &&
+		    !can_use_ic(dev, &dev->sensor_fmt, &dev->user_fmt)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: current format does not allow rotation\n",
+				 __func__);
+			return -EINVAL;
+		}
+	}
+
+	dev->rot_mode = rot_mode;
+	dev->rotation = rotation;
+	dev->hflip = hflip;
+	dev->vflip = vflip;
+
+	return 0;
+}
+
+static int imxcam_set_motion(struct imxcam_dev *dev,
+			     enum ipu_motion_sel motion)
+{
+	if (motion != dev->motion) {
+		/* can't change motion setting mid-streaming */
+		if (vb2_is_streaming(&dev->buffer_queue)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: not allowed while streaming\n",
+				 __func__);
+			return -EBUSY;
+		}
+
+		if (motion != MOTION_NONE &&
+		    !can_use_vdic(dev, &dev->sensor_fmt)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "sensor format does not allow deinterlace\n");
+			return -EINVAL;
+		}
+	}
+
+	dev->motion = motion;
+	return 0;
+}
+
+static int imxcam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct imxcam_dev *dev = container_of(ctrl->handler,
+					      struct imxcam_dev, ctrl_hdlr);
+	enum ipu_motion_sel motion;
+	bool hflip, vflip;
+	int rotation;
+
+	rotation = dev->rotation;
+	hflip = dev->hflip;
+	vflip = dev->vflip;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		hflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_VFLIP:
+		vflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_ROTATE:
+		rotation = ctrl->val;
+		break;
+	case V4L2_CID_IMX_MOTION:
+		motion = ctrl->val;
+		return imxcam_set_motion(dev, motion);
+	case V4L2_CID_IMX_FIM_ENABLE:
+		reset_fim(dev, false);
+		return 0;
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+
+	return imxcam_set_rotation(dev, rotation, hflip, vflip);
+}
+
+static const struct v4l2_ctrl_ops imxcam_ctrl_ops = {
+	.s_ctrl = imxcam_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config imxcam_std_ctrl[] = {
+	{
+		.id = V4L2_CID_HFLIP,
+		.name = "Horizontal Flip",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.def =  0,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.id = V4L2_CID_VFLIP,
+		.name = "Vertical Flip",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.def =  0,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.id = V4L2_CID_ROTATE,
+		.name = "Rotation",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def =   0,
+		.min =   0,
+		.max = 270,
+		.step = 90,
+	},
+};
+
+#define IMXCAM_NUM_STD_CONTROLS ARRAY_SIZE(imxcam_std_ctrl)
+
+static const struct v4l2_ctrl_config imxcam_custom_ctrl[] = {
+	{
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_MOTION,
+		.name = "Motion Compensation",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = MOTION_NONE,
+		.min = MOTION_NONE,
+		.max = HIGH_MOTION,
+		.step = 1,
+	},
+};
+
+#define IMXCAM_NUM_CUSTOM_CONTROLS ARRAY_SIZE(imxcam_custom_ctrl)
+
+static const struct v4l2_ctrl_config imxcam_fim_ctrl[] = {
+	[FIM_CL_ENABLE] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_ENABLE,
+		.name = "FIM Enable",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.def = FIM_CL_ENABLE_DEF,
+		.min = 0,
+		.max = 1,
+		.step = 1,
+	},
+	[FIM_CL_NUM] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_NUM,
+		.name = "FIM Num Average",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_NUM_DEF,
+		.min =  1, /* no averaging */
+		.max = 64, /* average 64 frames */
+		.step = 1,
+	},
+	[FIM_CL_TOLERANCE_MIN] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_TOLERANCE_MIN,
+		.name = "FIM Tolerance Min",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_TOLERANCE_MIN_DEF,
+		.min =    2,
+		.max =  200,
+		.step =   1,
+	},
+	[FIM_CL_TOLERANCE_MAX] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_TOLERANCE_MAX,
+		.name = "FIM Tolerance Max",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_TOLERANCE_MAX_DEF,
+		.min =    0,
+		.max =  500,
+		.step =   1,
+	},
+	[FIM_CL_NUM_SKIP] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_NUM_SKIP,
+		.name = "FIM Num Skip",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_NUM_SKIP_DEF,
+		.min =   1, /* skip 1 frame */
+		.max = 256, /* skip 256 frames */
+		.step =  1,
+	},
+};
+
+/*
+ * the adv7182 has the most controls with 27, so add 32
+ * on top of our own
+ */
+#define IMXCAM_NUM_CONTROLS (IMXCAM_NUM_STD_CONTROLS    + \
+			     IMXCAM_NUM_CUSTOM_CONTROLS + \
+			     FIM_NUM_CONTROLS + 32)
+
+static int imxcam_init_controls(struct imxcam_dev *dev)
+{
+	struct v4l2_ctrl_handler *hdlr = &dev->ctrl_hdlr;
+	struct imxcam_fim *fim = &dev->fim;
+	const struct v4l2_ctrl_config *c;
+	struct v4l2_ctrl_config fim_c;
+	int i, ret;
+
+	v4l2_ctrl_handler_init(hdlr, IMXCAM_NUM_CONTROLS);
+
+	for (i = 0; i < IMXCAM_NUM_STD_CONTROLS; i++) {
+		c = &imxcam_std_ctrl[i];
+
+		v4l2_ctrl_new_std(hdlr, &imxcam_ctrl_ops,
+				  c->id, c->min, c->max, c->step, c->def);
+	}
+
+	for (i = 0; i < IMXCAM_NUM_CUSTOM_CONTROLS; i++) {
+		c = &imxcam_custom_ctrl[i];
+
+		v4l2_ctrl_new_custom(hdlr, c, NULL);
+	}
+
+	for (i = 0; i < FIM_NUM_CONTROLS; i++) {
+		fim_c = imxcam_fim_ctrl[i];
+		fim_c.def = fim->of_defaults[i];
+		fim->ctrl[i] = v4l2_ctrl_new_custom(hdlr, &fim_c, NULL);
+	}
+
+	if (hdlr->error) {
+		ret = hdlr->error;
+		v4l2_ctrl_handler_free(hdlr);
+		return ret;
+	}
+
+	v4l2_ctrl_cluster(FIM_NUM_CONTROLS, fim->ctrl);
+
+	dev->v4l2_dev.ctrl_handler = hdlr;
+	dev->vfd->ctrl_handler = hdlr;
+
+	return 0;
+}
+
+/*
+ * Video ioctls follow
+ */
+
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, DEVICE_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, DEVICE_NAME, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct imxcam_pixfmt *fmt;
+
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+
+	fmt = &imxcam_pixformats[f->index];
+	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+	f->pixelformat = fmt->fourcc;
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	f->fmt.pix = dev->user_fmt.fmt.pix;
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct v4l2_subdev_pad_config pad_cfg;
+	struct v4l2_subdev_format format;
+	struct imxcam_pixfmt *fmt;
+	unsigned int width_align;
+	struct v4l2_rect crop;
+	int ret;
+
+	fmt = imxcam_get_format(f->fmt.pix.pixelformat, 0);
+	if (!fmt) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Fourcc format (0x%08x) invalid.\n",
+			 f->fmt.pix.pixelformat);
+		return -EINVAL;
+	}
+
+	/*
+	 * simple IDMAC interleaving using ILO field doesn't work
+	 * when combined with the 16-bit planar formats (YUV422P
+	 * and NV16). This looks like a silicon bug, no satisfactory
+	 * replies to queries about it from Freescale. So workaround
+	 * the issue by forcing the formats to the 12-bit planar versions.
+	 */
+	if (V4L2_FIELD_HAS_BOTH(dev->sensor_fmt.field) &&
+	    dev->motion == MOTION_NONE) {
+		switch (fmt->fourcc) {
+		case V4L2_PIX_FMT_YUV422P:
+			v4l2_info(&dev->v4l2_dev,
+				  "ILO workaround: YUV422P forced to YUV420\n");
+			f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+			break;
+		case V4L2_PIX_FMT_NV16:
+			v4l2_info(&dev->v4l2_dev,
+				  "ILO workaround: NV16 forced to NV12\n");
+			f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
+			break;
+		default:
+			break;
+		}
+		fmt = imxcam_get_format(f->fmt.pix.pixelformat, 0);
+	}
+
+	/*
+	 * We have to adjust the width such that the physaddrs and U and
+	 * U and V plane offsets are multiples of 8 bytes as required by
+	 * the IPU DMA Controller. For the planar formats, this corresponds
+	 * to a pixel alignment of 16. For all the packed formats, 8 is
+	 * good enough.
+	 *
+	 * For height alignment, we have to ensure that the heights
+	 * are multiples of 8 lines, to satisfy the requirement of the
+	 * IRT (the IRT performs rotations on 8x8 blocks at a time).
+	 */
+	width_align = ipu_pixelformat_is_planar(fmt->fourcc) ? 4 : 3;
+
+	v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+			      width_align, &f->fmt.pix.height,
+			      MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+
+	format.which = V4L2_SUBDEV_FORMAT_TRY;
+	format.pad = 0;
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, 0);
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, set_fmt, &pad_cfg, &format);
+	if (ret)
+		return ret;
+
+	fmt = imxcam_get_format(0, pad_cfg.try_fmt.code);
+	if (!fmt) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Sensor mbus format (0x%08x) invalid\n",
+			 pad_cfg.try_fmt.code);
+		return -EINVAL;
+	}
+
+	/*
+	 * calculate what the optimal crop window will be for this
+	 * sensor format and make any user format adjustments.
+	 */
+	calc_default_crop(dev, &crop, &pad_cfg.try_fmt, dev->current_std);
+	adjust_user_fmt(dev, &pad_cfg.try_fmt, f, &crop);
+
+	/* this driver only delivers progressive frames to userland */
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct v4l2_subdev_format format;
+	int ret;
+
+	if (vb2_is_busy(&dev->buffer_queue)) {
+		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+		return -EBUSY;
+	}
+
+	ret = vidioc_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	format.pad = 0;
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, 0);
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, set_fmt, NULL, &format);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "%s set_fmt failed\n", __func__);
+		return ret;
+	}
+
+	ret = update_sensor_fmt(dev);
+	if (ret)
+		return ret;
+
+	dev->user_fmt = *f;
+	dev->user_pixfmt = imxcam_get_format(f->fmt.pix.pixelformat, 0);
+
+	return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct imxcam_pixfmt *fmt;
+	struct v4l2_format uf;
+
+	fmt = imxcam_get_format(fsize->pixel_format, 0);
+	if (!fmt)
+		return -EINVAL;
+
+	if (fsize->index)
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+	fsize->stepwise.min_width = MIN_W;
+	fsize->stepwise.step_width =
+		ipu_pixelformat_is_planar(fmt->fourcc) ? 16 : 8;
+	fsize->stepwise.min_height = MIN_H;
+	fsize->stepwise.step_height = 1 << H_ALIGN;
+
+	uf = dev->user_fmt;
+	uf.fmt.pix.pixelformat = fmt->fourcc;
+
+	if (need_ic(dev, &dev->sensor_fmt, &uf, &dev->crop)) {
+		fsize->stepwise.max_width = MAX_W_IC;
+		fsize->stepwise.max_height = MAX_H_IC;
+	} else {
+		fsize->stepwise.max_width = MAX_W;
+		fsize->stepwise.max_height = MAX_H;
+	}
+
+	return 0;
+}
+
+static int vidioc_enum_frameintervals(struct file *file, void *priv,
+				      struct v4l2_frmivalenum *fival)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct imxcam_pixfmt *fmt;
+	struct v4l2_subdev_frame_interval_enum fie = {
+		.index = fival->index,
+		.pad = 0,
+		.width = fival->width,
+		.height = fival->height,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	int ret;
+
+	fmt = imxcam_get_format(fival->pixel_format, 0);
+	if (!fmt)
+		return -EINVAL;
+
+	fie.code = fmt->codes[0];
+
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, enum_frame_interval,
+			       NULL, &fie);
+	if (ret)
+		return ret;
+
+	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	fival->discrete = fie.interval;
+	return 0;
+}
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	int ret;
+
+	ret = update_sensor_std(dev);
+	if (!ret)
+		*std = dev->current_std;
+	return ret;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	*std = dev->current_std;
+	return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	int ret;
+
+	if (vb2_is_busy(&dev->buffer_queue))
+		return -EBUSY;
+
+	ret = v4l2_subdev_call(dev->sensor->sd, video, s_std, std);
+	if (ret < 0)
+		return ret;
+
+	dev->current_std = std;
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+			     struct v4l2_input *input)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct imxcam_sensor_input *sinput;
+	struct imxcam_sensor *sensor;
+	int sensor_input;
+
+	/* find the sensor that is handling this input */
+	sensor = find_sensor_by_input_index(dev, input->index);
+	if (!sensor)
+		return -EINVAL;
+
+	sinput = &sensor->input;
+	sensor_input = input->index - sinput->first;
+
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+	input->capabilities = sinput->caps[sensor_input];
+	strncpy(input->name, sinput->name[sensor_input], sizeof(input->name));
+
+	if (input->index == dev->current_input) {
+		v4l2_subdev_call(sensor->sd, video, g_input_status, &input->status);
+		update_sensor_std(dev);
+		input->std = dev->current_std;
+	} else {
+		input->status = V4L2_IN_ST_NO_SIGNAL;
+		input->std = V4L2_STD_UNKNOWN;
+	}
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *index)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	*index = dev->current_input;
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct imxcam_sensor_input *sinput;
+	struct imxcam_sensor *sensor;
+	int ret, sensor_input, fim_actv;
+
+	if (index == dev->current_input)
+		return 0;
+
+	/* find the sensor that is handling this input */
+	sensor = find_sensor_by_input_index(dev, index);
+	if (!sensor)
+		return -EINVAL;
+
+	if (dev->sensor != sensor) {
+		/*
+		 * don't allow switching sensors if there are queued buffers
+		 * or there are other users of the current sensor besides us.
+		 */
+		if (vb2_is_busy(&dev->buffer_queue) ||
+		    dev->sensor->power_count > 1)
+			return -EBUSY;
+
+		v4l2_info(&dev->v4l2_dev, "switching to sensor %s\n",
+			  sensor->sd->name);
+
+		/* power down current sensor before enabling new one */
+		ret = sensor_set_power(dev, 0);
+		if (ret)
+			v4l2_warn(&dev->v4l2_dev, "sensor power off failed\n");
+
+		/* set new sensor and the video mux(es) in the pipeline to it */
+		dev->sensor = sensor;
+		ret = imxcam_set_video_muxes(dev);
+		if (ret)
+			v4l2_warn(&dev->v4l2_dev, "set video muxes failed\n");
+
+		/*
+		 * turn on FIM if ADV718x is selected else turn off FIM
+		 * for other sensors.
+		 */
+		if (strncasecmp(sensor->sd->name, "adv718", 6) == 0)
+			fim_actv = 1;
+		else
+			fim_actv = 0;
+		v4l2_ctrl_s_ctrl(dev->fim.ctrl[FIM_CL_ENABLE], fim_actv);
+
+		/* power-on the new sensor */
+		ret = sensor_set_power(dev, 1);
+		if (ret)
+			v4l2_warn(&dev->v4l2_dev, "sensor power on failed\n");
+	}
+
+	/* finally select the sensor's input */
+	sinput = &sensor->input;
+	sensor_input = index - sinput->first;
+	ret = v4l2_subdev_call(sensor->sd, video, s_routing,
+			       sinput->value[sensor_input], 0, 0);
+
+	dev->current_input = index;
+
+	/*
+	 * Status update required if there is a change
+	 * of inputs
+	 */
+	atomic_set(&dev->status_change, 1);
+
+	return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+			 struct v4l2_streamparm *a)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return v4l2_subdev_call(dev->sensor->sd, video, g_parm, a);
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+			 struct v4l2_streamparm *a)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return v4l2_subdev_call(dev->sensor->sd, video, s_parm, a);
+}
+
+static int vidioc_g_selection(struct file *file, void *priv,
+			      struct v4l2_selection *sel)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE:
+		/*
+		 * compose windows are not supported in this driver,
+		 * compose window is same as user buffers from s_fmt.
+		 */
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = dev->user_fmt.fmt.pix.width;
+		sel->r.height = dev->user_fmt.fmt.pix.height;
+		break;
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r = dev->crop_bounds;
+		break;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r = dev->crop_defrect;
+		break;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = dev->crop;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int vidioc_s_selection(struct file *file, void *priv,
+			      struct v4l2_selection *sel)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct v4l2_rect *bounds = &dev->crop_bounds;
+
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	if (vb2_is_busy(&dev->buffer_queue))
+		return -EBUSY;
+
+	/* make sure crop window is within bounds */
+	if (sel->r.top < 0 || sel->r.left < 0 ||
+	    sel->r.left + sel->r.width > bounds->width ||
+	    sel->r.top + sel->r.height > bounds->height)
+		return -EINVAL;
+
+	/*
+	 * FIXME: the IPU currently does not setup the CCIR code
+	 * registers properly to handle arbitrary vertical crop
+	 * windows. So return error if the sensor bus is BT.656
+	 * and user is asking to change vertical cropping.
+	 */
+	if (dev->sensor->ep.bus_type == V4L2_MBUS_BT656 &&
+	    (sel->r.top != dev->crop.top ||
+	     sel->r.height != dev->crop.height)) {
+		v4l2_err(&dev->v4l2_dev,
+			 "vertical crop is not supported for this sensor!\n");
+		return -EINVAL;
+	}
+
+	/* adjust crop window to h/w alignment restrictions */
+	sel->r.width &= ~0x7;
+	sel->r.left &= ~0x3;
+
+	dev->crop = sel->r;
+
+	/*
+	 * Crop window has changed, we need to adjust the user
+	 * width/height to meet new IC resizer restrictions or to
+	 * match the new crop window if the IC can't be used.
+	 */
+	adjust_user_fmt(dev, &dev->sensor_fmt, &dev->user_fmt,
+			&dev->crop);
+
+	return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+			  struct v4l2_requestbuffers *reqbufs)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct vb2_queue *vq = &dev->buffer_queue;
+	unsigned long flags;
+	int ret;
+
+	if (vb2_is_busy(vq) || (dev->io_ctx && !is_io_ctx(ctx)))
+		return -EBUSY;
+
+	ctx->alloc_ctx = vb2_dma_contig_init_ctx(dev->dev);
+	if (IS_ERR(ctx->alloc_ctx)) {
+		v4l2_err(&dev->v4l2_dev, "failed to alloc vb2 context\n");
+		return PTR_ERR(ctx->alloc_ctx);
+	}
+
+	INIT_LIST_HEAD(&ctx->ready_q);
+	INIT_WORK(&ctx->restart_work, restart_work_handler);
+	INIT_WORK(&ctx->stop_work, stop_work_handler);
+	__init_timer(&ctx->restart_timer, TIMER_IRQSAFE);
+	ctx->restart_timer.data = (unsigned long)ctx;
+	ctx->restart_timer.function = imxcam_restart_timeout;
+
+	vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	vq->drv_priv = ctx;
+	vq->buf_struct_size = sizeof(struct imxcam_buffer);
+	vq->ops = &imxcam_qops;
+	vq->mem_ops = &vb2_dma_contig_memops;
+	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	ret = vb2_queue_init(vq);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "vb2_queue_init failed\n");
+		goto alloc_ctx_free;
+	}
+
+	ret = vb2_reqbufs(vq, reqbufs);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "vb2_reqbufs failed\n");
+		goto alloc_ctx_free;
+	}
+
+	spin_lock_irqsave(&dev->notify_lock, flags);
+	dev->io_ctx = ctx;
+	spin_unlock_irqrestore(&dev->notify_lock, flags);
+
+	return 0;
+
+alloc_ctx_free:
+	vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx);
+	return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+			   struct v4l2_buffer *buf)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct vb2_queue *vq = &ctx->dev->buffer_queue;
+
+	return vb2_querybuf(vq, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct vb2_queue *vq = &ctx->dev->buffer_queue;
+
+	if (!is_io_ctx(ctx))
+		return -EBUSY;
+
+	return vb2_qbuf(vq, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct vb2_queue *vq = &ctx->dev->buffer_queue;
+
+	if (!is_io_ctx(ctx))
+		return -EBUSY;
+
+	return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_expbuf(struct file *file, void *priv,
+			 struct v4l2_exportbuffer *eb)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct vb2_queue *vq = &ctx->dev->buffer_queue;
+
+	if (!is_io_ctx(ctx))
+		return -EBUSY;
+
+	return vb2_expbuf(vq, eb);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+			   enum v4l2_buf_type type)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct vb2_queue *vq = &ctx->dev->buffer_queue;
+
+	if (!is_io_ctx(ctx))
+		return -EBUSY;
+
+	return vb2_streamon(vq, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+			    enum v4l2_buf_type type)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct vb2_queue *vq = &ctx->dev->buffer_queue;
+
+	if (!is_io_ctx(ctx))
+		return -EBUSY;
+
+	return vb2_streamoff(vq, type);
+}
+
+static const struct v4l2_ioctl_ops imxcam_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap           = vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap         = vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap           = vidioc_s_fmt_vid_cap,
+
+	.vidioc_enum_framesizes         = vidioc_enum_framesizes,
+	.vidioc_enum_frameintervals     = vidioc_enum_frameintervals,
+
+	.vidioc_querystd        = vidioc_querystd,
+	.vidioc_g_std           = vidioc_g_std,
+	.vidioc_s_std           = vidioc_s_std,
+
+	.vidioc_enum_input      = vidioc_enum_input,
+	.vidioc_g_input         = vidioc_g_input,
+	.vidioc_s_input         = vidioc_s_input,
+
+	.vidioc_g_parm          = vidioc_g_parm,
+	.vidioc_s_parm          = vidioc_s_parm,
+
+	.vidioc_g_selection     = vidioc_g_selection,
+	.vidioc_s_selection     = vidioc_s_selection,
+
+	.vidioc_reqbufs		= vidioc_reqbufs,
+	.vidioc_querybuf	= vidioc_querybuf,
+	.vidioc_qbuf		= vidioc_qbuf,
+	.vidioc_dqbuf		= vidioc_dqbuf,
+	.vidioc_expbuf		= vidioc_expbuf,
+
+	.vidioc_streamon	= vidioc_streamon,
+	.vidioc_streamoff	= vidioc_streamoff,
+};
+
+/*
+ * Queue operations
+ */
+
+static int imxcam_queue_setup(struct vb2_queue *vq,
+			      unsigned int *nbuffers, unsigned int *nplanes,
+			      unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct imxcam_ctx *ctx = vb2_get_drv_priv(vq);
+	struct imxcam_dev *dev = ctx->dev;
+	unsigned int count = *nbuffers;
+	u32 sizeimage = dev->user_fmt.fmt.pix.sizeimage;
+
+	if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	while (sizeimage * count > VID_MEM_LIMIT)
+		count--;
+
+	*nplanes = 1;
+	*nbuffers = count;
+	sizes[0] = sizeimage;
+
+	alloc_ctxs[0] = ctx->alloc_ctx;
+
+	dprintk(dev, "get %d buffer(s) of size %d each.\n", count, sizeimage);
+
+	return 0;
+}
+
+static int imxcam_buf_init(struct vb2_buffer *vb)
+{
+	struct imxcam_buffer *buf = to_imxcam_vb(vb);
+
+	INIT_LIST_HEAD(&buf->list);
+	return 0;
+}
+
+static int imxcam_buf_prepare(struct vb2_buffer *vb)
+{
+	struct imxcam_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct imxcam_dev *dev = ctx->dev;
+
+	if (vb2_plane_size(vb, 0) < dev->user_fmt.fmt.pix.sizeimage) {
+		v4l2_err(&dev->v4l2_dev,
+			 "data will not fit into plane (%lu < %lu)\n",
+			 vb2_plane_size(vb, 0),
+			 (long)dev->user_fmt.fmt.pix.sizeimage);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, dev->user_fmt.fmt.pix.sizeimage);
+
+	return 0;
+}
+
+static void imxcam_buf_queue(struct vb2_buffer *vb)
+{
+	struct imxcam_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct imxcam_dev *dev = ctx->dev;
+	struct imxcam_buffer *buf = to_imxcam_vb(vb);
+	unsigned long flags;
+	bool kickstart;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	list_add_tail(&buf->list, &ctx->ready_q);
+
+	/* kickstart DMA chain if we have two frames in active q */
+	kickstart = (vb2_is_streaming(vb->vb2_queue) &&
+		     !(list_empty(&ctx->ready_q) ||
+		       list_is_singular(&ctx->ready_q)));
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	if (kickstart)
+		start_encoder(dev);
+}
+
+static void imxcam_lock(struct vb2_queue *vq)
+{
+	struct imxcam_ctx *ctx = vb2_get_drv_priv(vq);
+	struct imxcam_dev *dev = ctx->dev;
+
+	mutex_lock(&dev->mutex);
+}
+
+static void imxcam_unlock(struct vb2_queue *vq)
+{
+	struct imxcam_ctx *ctx = vb2_get_drv_priv(vq);
+	struct imxcam_dev *dev = ctx->dev;
+
+	mutex_unlock(&dev->mutex);
+}
+
+static int imxcam_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct imxcam_ctx *ctx = vb2_get_drv_priv(vq);
+	struct imxcam_buffer *buf, *tmp;
+	int ret;
+
+	if (vb2_is_streaming(vq))
+		return 0;
+
+	ctx->stop = false;
+
+	ret = set_stream(ctx, true);
+	if (ret)
+		goto return_bufs;
+
+	return 0;
+
+return_bufs:
+	list_for_each_entry_safe(buf, tmp, &ctx->ready_q, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+	}
+	return ret;
+}
+
+static void imxcam_stop_streaming(struct vb2_queue *vq)
+{
+	struct imxcam_ctx *ctx = vb2_get_drv_priv(vq);
+	struct imxcam_dev *dev = ctx->dev;
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+
+	if (!vb2_is_streaming(vq))
+		return;
+
+	/*
+	 * signal that streaming is being stopped, so that the
+	 * restart_work_handler() will skip unnecessary stream
+	 * restarts, and to stop kicking the restart timer.
+	 */
+	ctx->stop = true;
+
+	set_stream(ctx, false);
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	/* release all active buffers */
+	while (!list_empty(&ctx->ready_q)) {
+		frame = list_entry(ctx->ready_q.next,
+				   struct imxcam_buffer, list);
+		list_del(&frame->list);
+		vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+	}
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+static struct vb2_ops imxcam_qops = {
+	.queue_setup	 = imxcam_queue_setup,
+	.buf_init        = imxcam_buf_init,
+	.buf_prepare	 = imxcam_buf_prepare,
+	.buf_queue	 = imxcam_buf_queue,
+	.wait_prepare	 = imxcam_unlock,
+	.wait_finish	 = imxcam_lock,
+	.start_streaming = imxcam_start_streaming,
+	.stop_streaming  = imxcam_stop_streaming,
+};
+
+/*
+ * File operations
+ */
+static int imxcam_open(struct file *file)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct imxcam_ctx *ctx;
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->mutex))
+		return -ERESTARTSYS;
+
+	if (!dev->sensor || !dev->sensor->sd) {
+		v4l2_err(&dev->v4l2_dev, "no subdevice registered\n");
+		ret = -ENODEV;
+		goto unlock;
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = &ctx->fh;
+	ctx->dev = dev;
+	v4l2_fh_add(&ctx->fh);
+
+	ret = sensor_set_power(dev, 1);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "sensor power on failed\n");
+		goto ctx_free;
+	}
+
+	/* update the sensor's current lock status and format */
+	update_signal_lock_status(dev);
+	update_sensor_fmt(dev);
+
+	mutex_unlock(&dev->mutex);
+	return 0;
+
+ctx_free:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	kfree(ctx);
+unlock:
+	mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+static int imxcam_release(struct file *file)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	unsigned long flags;
+	int ret = 0;
+
+	mutex_lock(&dev->mutex);
+
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+
+	if (is_io_ctx(ctx)) {
+		vb2_queue_release(&dev->buffer_queue);
+		vb2_dma_contig_cleanup_ctx(ctx->alloc_ctx);
+
+		spin_lock_irqsave(&dev->notify_lock, flags);
+		/* cancel any pending or scheduled restart timer */
+		del_timer_sync(&ctx->restart_timer);
+		dev->io_ctx = NULL;
+		spin_unlock_irqrestore(&dev->notify_lock, flags);
+
+		/*
+		 * cancel any scheduled restart work, we have to release
+		 * the dev->mutex in case it has already been scheduled.
+		 */
+		mutex_unlock(&dev->mutex);
+		cancel_work_sync(&ctx->restart_work);
+		mutex_lock(&dev->mutex);
+	}
+
+	if (!dev->sensor || !dev->sensor->sd) {
+		v4l2_warn(&dev->v4l2_dev, "lost the slave?\n");
+		goto free_ctx;
+	}
+
+	ret = sensor_set_power(dev, 0);
+	if (ret)
+		v4l2_err(&dev->v4l2_dev, "sensor power off failed\n");
+
+free_ctx:
+	kfree(ctx);
+	mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+static unsigned int imxcam_poll(struct file *file,
+				struct poll_table_struct *wait)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct vb2_queue *vq = &dev->buffer_queue;
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->mutex))
+		return -ERESTARTSYS;
+
+	ret = vb2_poll(vq, file, wait);
+
+	mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+static int imxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+	struct vb2_queue *vq = &dev->buffer_queue;
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->mutex))
+		return -ERESTARTSYS;
+
+	ret = vb2_mmap(vq, vma);
+
+	mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+static const struct v4l2_file_operations imxcam_fops = {
+	.owner		= THIS_MODULE,
+	.open		= imxcam_open,
+	.release	= imxcam_release,
+	.poll		= imxcam_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= imxcam_mmap,
+};
+
+static struct video_device imxcam_videodev = {
+	.name		= DEVICE_NAME,
+	.fops		= &imxcam_fops,
+	.ioctl_ops	= &imxcam_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release,
+	.vfl_dir	= VFL_DIR_RX,
+	.tvnorms	= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
+/*
+ * Handle notifications from the subdevs.
+ */
+static void imxcam_subdev_notification(struct v4l2_subdev *sd,
+				       unsigned int notification,
+				       void *arg)
+{
+	struct imxcam_dev *dev;
+	struct imxcam_ctx *ctx;
+	struct v4l2_event *ev;
+	unsigned long flags;
+
+	if (!sd)
+		return;
+
+	dev = sd2dev(sd);
+
+	spin_lock_irqsave(&dev->notify_lock, flags);
+
+	ctx = dev->io_ctx;
+
+	switch (notification) {
+	case IMXCAM_NFB4EOF_NOTIFY:
+		if (ctx && !ctx->stop)
+			imxcam_bump_restart_timer(ctx);
+		break;
+	case IMXCAM_FRAME_INTERVAL_NOTIFY:
+		if (ctx && !ctx->stop && !atomic_read(&dev->pending_restart))
+			imxcam_bump_restart_timer(ctx);
+		break;
+	case IMXCAM_EOF_TIMEOUT_NOTIFY:
+		if (ctx && !ctx->stop) {
+			/*
+			 * cancel a running restart timer since we are
+			 * restarting now anyway
+			 */
+			del_timer_sync(&ctx->restart_timer);
+			/* and restart now */
+			schedule_work(&ctx->restart_work);
+		}
+		break;
+	case V4L2_DEVICE_NOTIFY_EVENT:
+		ev = (struct v4l2_event *)arg;
+		if (ev && ev->type == V4L2_EVENT_SOURCE_CHANGE) {
+			atomic_set(&dev->status_change, 1);
+			if (ctx && !ctx->stop) {
+				v4l2_warn(&dev->v4l2_dev,
+					  "decoder status change\n");
+				imxcam_bump_restart_timer(ctx);
+			}
+			/* send decoder status events to userspace */
+			v4l2_event_queue(dev->vfd, ev);
+		}
+		break;
+	}
+
+	spin_unlock_irqrestore(&dev->notify_lock, flags);
+}
+
+
+static void imxcam_unregister_sync_subdevs(struct imxcam_dev *dev)
+{
+	if (!IS_ERR_OR_NULL(dev->smfc_sd))
+		v4l2_device_unregister_subdev(dev->smfc_sd);
+
+	if (!IS_ERR_OR_NULL(dev->prpenc_sd))
+		v4l2_device_unregister_subdev(dev->prpenc_sd);
+
+	if (!IS_ERR_OR_NULL(dev->vdic_sd))
+		v4l2_device_unregister_subdev(dev->vdic_sd);
+}
+
+static int imxcam_register_sync_subdevs(struct imxcam_dev *dev)
+{
+	int ret;
+
+	dev->smfc_sd = imxcam_smfc_init(dev);
+	if (IS_ERR(dev->smfc_sd))
+		return PTR_ERR(dev->smfc_sd);
+
+	dev->prpenc_sd = imxcam_ic_prpenc_init(dev);
+	if (IS_ERR(dev->prpenc_sd))
+		return PTR_ERR(dev->prpenc_sd);
+
+	dev->vdic_sd = imxcam_vdic_init(dev);
+	if (IS_ERR(dev->vdic_sd))
+		return PTR_ERR(dev->vdic_sd);
+
+	ret = v4l2_device_register_subdev(&dev->v4l2_dev, dev->smfc_sd);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "failed to register subdev %s\n",
+			 dev->smfc_sd->name);
+		goto unreg;
+	}
+	v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", dev->smfc_sd->name);
+
+	ret = v4l2_device_register_subdev(&dev->v4l2_dev, dev->prpenc_sd);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "failed to register subdev %s\n",
+			 dev->prpenc_sd->name);
+		goto unreg;
+	}
+	v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", dev->prpenc_sd->name);
+
+	ret = v4l2_device_register_subdev(&dev->v4l2_dev, dev->vdic_sd);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "failed to register subdev %s\n",
+			 dev->vdic_sd->name);
+		goto unreg;
+	}
+	v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", dev->vdic_sd->name);
+
+	return 0;
+
+unreg:
+	imxcam_unregister_sync_subdevs(dev);
+	return ret;
+}
+
+/* async subdev bound notifier */
+static int imxcam_subdev_bound(struct v4l2_async_notifier *notifier,
+			       struct v4l2_subdev *sd,
+			       struct v4l2_async_subdev *asd)
+{
+	struct imxcam_dev *dev = notifier2dev(notifier);
+	struct imxcam_sensor_input *sinput;
+	struct imxcam_sensor *sensor;
+	int i, ret = -EINVAL;
+
+	if (dev->csi2_asd &&
+	    sd->dev->of_node == dev->csi2_asd->match.of.node) {
+		dev->csi2_sd = sd;
+		ret = 0;
+		goto out;
+	}
+
+	for (i = 0; i < dev->num_csi; i++) {
+		if (dev->csi_asd[i] &&
+		    sd->dev->of_node == dev->csi_asd[i]->match.of.node) {
+			dev->csi_list[i] = sd;
+			ret = 0;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < dev->num_vidmux; i++) {
+		if (dev->vidmux_asd[i] &&
+		    sd->dev->of_node == dev->vidmux_asd[i]->match.of.node) {
+			dev->vidmux_list[i] = sd;
+			ret = 0;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < dev->num_sensors; i++) {
+		sensor = &dev->sensor_list[i];
+		if (sensor->asd &&
+		    sd->dev->of_node == sensor->asd->match.of.node) {
+			sensor->sd = sd;
+
+			/* set sensor input names if needed */
+			sinput = &sensor->input;
+			for (i = 0; i < sinput->num; i++) {
+				if (strlen(sinput->name[i]))
+					continue;
+				snprintf(sinput->name[i],
+					 sizeof(sinput->name[i]),
+					 "%s-%d", sd->name, i);
+			}
+
+			ret = 0;
+			break;
+		}
+	}
+
+out:
+	if (ret)
+		v4l2_warn(&dev->v4l2_dev, "Received unknown subdev %s\n",
+			  sd->name);
+	else
+		v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", sd->name);
+
+	return ret;
+}
+
+/* async subdev complete notifier */
+static int imxcam_probe_complete(struct v4l2_async_notifier *notifier)
+{
+	struct imxcam_dev *dev = notifier2dev(notifier);
+	struct imxcam_sensor *sensor;
+	int i, j, ret;
+
+	/* assign CSI subdevs to every sensor */
+	for (i = 0; i < dev->num_sensors; i++) {
+		sensor = &dev->sensor_list[i];
+		for (j = 0; j < dev->num_csi; j++) {
+			if (sensor->csi_np == dev->csi_asd[j]->match.of.node) {
+				sensor->csi_sd = dev->csi_list[j];
+				break;
+			}
+		}
+		if (j >= dev->num_csi) {
+			v4l2_err(&dev->v4l2_dev,
+				 "Failed to find a CSI for sensor %s\n",
+				 sensor->sd->name);
+			return -ENODEV;
+		}
+	}
+
+	/* make default sensor the first in list */
+	dev->sensor = &dev->sensor_list[0];
+
+	/* setup our controls */
+	ret = v4l2_ctrl_handler_setup(&dev->ctrl_hdlr);
+	if (ret)
+		goto free_ctrls;
+
+	ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		goto free_ctrls;
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
+	if (ret)
+		goto unreg;
+
+	/* set video mux(es) in the pipeline to this sensor */
+	ret = imxcam_set_video_muxes(dev);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to set video muxes\n");
+		goto unreg;
+	}
+
+	dev->v4l2_dev.notify = imxcam_subdev_notification;
+
+	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
+		  dev->vfd->num);
+
+	return 0;
+
+unreg:
+	video_unregister_device(dev->vfd);
+free_ctrls:
+	v4l2_ctrl_handler_free(&dev->ctrl_hdlr);
+	return ret;
+}
+
+static int imxcam_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct imxcam_dev *dev;
+	struct video_device *vfd;
+	struct pinctrl *pinctrl;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->dev = &pdev->dev;
+	mutex_init(&dev->mutex);
+	spin_lock_init(&dev->irqlock);
+	spin_lock_init(&dev->notify_lock);
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		return ret;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto unreg_dev;
+	}
+
+	*vfd = imxcam_videodev;
+	vfd->lock = &dev->mutex;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+
+	video_set_drvdata(vfd, dev);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", imxcam_videodev.name);
+	dev->vfd = vfd;
+
+	platform_set_drvdata(pdev, dev);
+
+	/* Get any pins needed */
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+
+	/* setup some defaults */
+	dev->user_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dev->user_fmt.fmt.pix.width = 640;
+	dev->user_fmt.fmt.pix.height = 480;
+	dev->user_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+	dev->user_fmt.fmt.pix.bytesperline = (640 * 12) >> 3;
+	dev->user_fmt.fmt.pix.sizeimage =
+		(480 * dev->user_fmt.fmt.pix.bytesperline);
+	dev->user_pixfmt =
+		imxcam_get_format(dev->user_fmt.fmt.pix.pixelformat, 0);
+	dev->current_std = V4L2_STD_UNKNOWN;
+
+	dev->sensor_set_stream = sensor_set_stream;
+
+	ret = imxcam_of_parse(dev, node);
+	if (ret)
+		goto unreg_dev;
+
+	if (dev->fim.icap_channel < 0)
+		dev->fim.eof = fim_eof_handler;
+
+	/* init our controls */
+	ret = imxcam_init_controls(dev);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "init controls failed\n");
+		goto unreg_dev;
+	}
+
+	ret = imxcam_register_sync_subdevs(dev);
+	if (ret)
+		goto unreg_dev;
+
+	/* prepare the async subdev notifier and register it */
+	dev->subdev_notifier.subdevs = dev->async_ptrs;
+	dev->subdev_notifier.bound = imxcam_subdev_bound;
+	dev->subdev_notifier.complete = imxcam_probe_complete;
+	ret = v4l2_async_notifier_register(&dev->v4l2_dev,
+					   &dev->subdev_notifier);
+	if (ret)
+		goto unreg_dev;
+
+	return 0;
+
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+	return ret;
+}
+
+static int imxcam_remove(struct platform_device *pdev)
+{
+	struct imxcam_dev *dev =
+		(struct imxcam_dev *)platform_get_drvdata(pdev);
+
+	v4l2_info(&dev->v4l2_dev, "Removing " DEVICE_NAME "\n");
+	v4l2_ctrl_handler_free(&dev->ctrl_hdlr);
+	v4l2_async_notifier_unregister(&dev->subdev_notifier);
+	video_unregister_device(dev->vfd);
+	imxcam_unregister_sync_subdevs(dev);
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	return 0;
+}
+
+static const struct of_device_id imxcam_dt_ids[] = {
+	{ .compatible = "fsl,imx-video-capture" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imxcam_dt_ids);
+
+static struct platform_driver imxcam_pdrv = {
+	.probe		= imxcam_probe,
+	.remove		= imxcam_remove,
+	.driver		= {
+		.name	= DEVICE_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table	= imxcam_dt_ids,
+	},
+};
+
+module_platform_driver(imxcam_pdrv);
+
+MODULE_DESCRIPTION("i.MX5/6 v4l2 capture driver");
+MODULE_AUTHOR("Mentor Graphics Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/imx/capture/imx-camif.h b/drivers/staging/media/imx/capture/imx-camif.h
new file mode 100644
index 0000000..6babbfe
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-camif.h
@@ -0,0 +1,281 @@
+/*
+ * Video Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _IMX_CAMIF_H
+#define _IMX_CAMIF_H
+
+#define dprintk(dev, fmt, arg...)					\
+	v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+/*
+ * These numbers are somewhat arbitrary, but we need at least:
+ * - 1 mipi-csi2 receiver subdev
+ * - 2 video-mux subdevs
+ * - 3 sensor subdevs (2 parallel, 1 mipi-csi2)
+ * - 4 CSI subdevs
+ */
+#define IMXCAM_MAX_SUBDEVS       16
+#define IMXCAM_MAX_SENSORS        8
+#define IMXCAM_MAX_VIDEOMUX       4
+#define IMXCAM_MAX_CSI            4
+
+/*
+ * How long before no EOF interrupts cause a stream restart, or a buffer
+ * dequeue timeout, in msec. The dequeue timeout should be longer than
+ * the EOF timeout.
+ */
+#define IMXCAM_EOF_TIMEOUT       1000
+#define IMXCAM_DQ_TIMEOUT        5000
+
+/*
+ * How long to delay a restart on ADV718x status changes or NFB4EOF,
+ * in msec.
+ */
+#define IMXCAM_RESTART_DELAY      200
+
+/*
+ * Internal subdev notifications
+ */
+#define IMXCAM_NFB4EOF_NOTIFY         _IO('6', 0)
+#define IMXCAM_EOF_TIMEOUT_NOTIFY     _IO('6', 1)
+#define IMXCAM_FRAME_INTERVAL_NOTIFY  _IO('6', 2)
+
+/*
+ * Frame Interval Monitor Control Indexes and default values
+ */
+enum {
+	FIM_CL_ENABLE = 0,
+	FIM_CL_NUM,
+	FIM_CL_TOLERANCE_MIN,
+	FIM_CL_TOLERANCE_MAX,
+	FIM_CL_NUM_SKIP,
+	FIM_NUM_CONTROLS,
+};
+
+#define FIM_CL_ENABLE_DEF      0 /* FIM disabled by default */
+#define FIM_CL_NUM_DEF         8 /* average 8 frames */
+#define FIM_CL_NUM_SKIP_DEF    8 /* skip 8 frames after restart */
+#define FIM_CL_TOLERANCE_MIN_DEF  50 /* usec */
+#define FIM_CL_TOLERANCE_MAX_DEF   0 /* no max tolerance (unbounded) */
+
+struct imxcam_buffer {
+	struct vb2_buffer vb; /* v4l buffer must be first */
+	struct list_head  list;
+};
+
+static inline struct imxcam_buffer *to_imxcam_vb(struct vb2_buffer *vb)
+{
+	return container_of(vb, struct imxcam_buffer, vb);
+}
+
+struct imxcam_pixfmt {
+	char	*name;
+	u32	fourcc;
+	u32     codes[4];
+	int     bpp;     /* total bpp */
+	int     y_depth; /* depth of first Y plane for planar formats */
+};
+
+struct imxcam_dma_buf {
+	void          *virt;
+	dma_addr_t     phys;
+	unsigned long  len;
+};
+
+/*
+ * A sensor's inputs parsed from v4l2_of_endpoint nodes in devicetree
+ */
+#define IMXCAM_MAX_INPUTS 16
+
+struct imxcam_sensor_input {
+	/* input values passed to s_routing */
+	u32 value[IMXCAM_MAX_INPUTS];
+	/* input capabilities (V4L2_IN_CAP_*) */
+	u32 caps[IMXCAM_MAX_INPUTS];
+	/* input names */
+	char name[IMXCAM_MAX_INPUTS][32];
+
+	/* number of inputs */
+	int num;
+	/* first and last input indexes from imxcam perspective */
+	int first;
+	int last;
+};
+
+struct imxcam_sensor {
+	struct v4l2_subdev       *sd;
+	struct v4l2_async_subdev *asd;
+	struct v4l2_of_endpoint  ep;     /* sensor's endpoint info */
+
+	/* csi node and subdev this sensor is connected to */
+	struct device_node       *csi_np;
+	struct v4l2_subdev       *csi_sd; 
+	struct v4l2_of_endpoint  csi_ep; /* parsed endpoint info of csi port */
+
+	struct imxcam_sensor_input input;
+
+	/* input indeces of all video-muxes required to access this sensor */
+	int vidmux_input[IMXCAM_MAX_VIDEOMUX];
+
+	int power_count;                 /* power use counter */
+	int stream_count;                /* stream use counter */
+};
+
+struct imxcam_ctx;
+struct imxcam_dev;
+
+/* frame interval monitor */
+struct imxcam_fim {
+	/* control cluster */
+	struct v4l2_ctrl  *ctrl[FIM_NUM_CONTROLS];
+
+	/* default ctrl values parsed from device tree */
+	u32               of_defaults[FIM_NUM_CONTROLS];
+
+	/* current control values */
+	bool              enabled;
+	int               num_avg;
+	int               num_skip;
+	unsigned long     tolerance_min; /* usec */
+	unsigned long     tolerance_max; /* usec */
+
+	int               counter;
+	struct timespec   last_ts;
+	unsigned long     sum;       /* usec */
+	unsigned long     nominal;   /* usec */
+
+	/*
+	 * input capture method of measuring FI (channel and flags
+	 * from device tree)
+	 */
+	int               icap_channel;
+	int               icap_flags;
+
+	/*
+	 * otherwise, the EOF method of measuring FI, called by
+	 * streaming subdevs from eof irq
+	 */
+	int (*eof)(struct imxcam_dev *dev, struct timeval *now);
+};
+
+struct imxcam_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vfd;
+	struct device           *dev;
+
+	struct mutex		mutex;
+	spinlock_t		irqlock;
+	spinlock_t		notify_lock;
+
+	/* buffer queue used in videobuf2 */
+	struct vb2_queue        buffer_queue;
+
+	/* v4l2 controls */
+	struct v4l2_ctrl_handler ctrl_hdlr;
+	int                      rotation; /* degrees */
+	bool                     hflip;
+	bool                     vflip;
+	enum ipu_motion_sel      motion;
+
+	/* derived from rotation, hflip, vflip controls */
+	enum ipu_rotate_mode     rot_mode;
+
+	struct imxcam_fim        fim;
+
+	/* the format from sensor and from userland */
+	struct v4l2_format        user_fmt;
+	struct imxcam_pixfmt      *user_pixfmt;
+	struct v4l2_mbus_framefmt sensor_fmt;
+	struct v4l2_fract         sensor_tpf;
+	struct imxcam_pixfmt      *sensor_pixfmt;
+	struct v4l2_mbus_config   mbus_cfg;
+
+	/*
+	 * the crop rectangle (from s_crop) specifies the crop dimensions
+	 * and position over the raw capture frame boundaries.
+	 */
+	struct v4l2_rect        crop_bounds;
+	struct v4l2_rect        crop_defrect;
+	struct v4l2_rect        crop;
+
+	/* misc status */
+	int                     current_input; /* the current input */
+	v4l2_std_id             current_std;   /* current video standard */
+	atomic_t                status_change; /* sensor status change */
+	atomic_t                pending_restart; /* a restart is pending */
+	bool                    signal_locked; /* sensor signal lock */
+	bool                    encoder_on;    /* encode is on */
+	bool                    using_ic;      /* IC is being used for encode */
+	bool                    using_vdic;    /* VDIC is used for encode */
+	bool                    vdic_direct;   /* VDIC is using the direct
+						  CSI->VDIC pipeline */
+
+	/* master descriptor list for async subdev registration */
+	struct v4l2_async_subdev async_desc[IMXCAM_MAX_SUBDEVS];
+	struct v4l2_async_subdev *async_ptrs[IMXCAM_MAX_SUBDEVS];
+
+	/* for async subdev registration */
+	struct v4l2_async_notifier subdev_notifier;
+
+	/* camera sensor subdev list */
+	struct imxcam_sensor    sensor_list[IMXCAM_MAX_SENSORS];
+	struct imxcam_sensor    *sensor; /* the current active sensor */
+	int                     num_sensor_inputs;
+	int                     num_sensors;
+
+	/* mipi-csi2 receiver subdev */
+	struct v4l2_subdev      *csi2_sd;
+	struct v4l2_async_subdev *csi2_asd;
+
+	/* CSI subdev list */
+	struct v4l2_subdev      *csi_list[IMXCAM_MAX_CSI];
+	struct v4l2_async_subdev *csi_asd[IMXCAM_MAX_CSI];
+	int                     num_csi;
+
+	/* video-mux subdev list */
+	struct v4l2_subdev      *vidmux_list[IMXCAM_MAX_VIDEOMUX];
+	struct v4l2_async_subdev *vidmux_asd[IMXCAM_MAX_VIDEOMUX];
+	int                     num_vidmux;
+
+	/* synchronous prpenc, smfc, and vdic subdevs */
+	struct v4l2_subdev      *smfc_sd;
+	struct v4l2_subdev      *prpenc_sd;
+	struct v4l2_subdev      *vdic_sd;
+
+	int (*sensor_set_stream)(struct imxcam_dev *dev, int on);
+
+	/*
+	 * the current open context that is doing IO (there can only
+	 * be one allowed IO context at a time).
+	 */
+	struct imxcam_ctx       *io_ctx;
+};
+
+struct imxcam_ctx {
+	struct v4l2_fh          fh;
+	struct imxcam_dev       *dev;
+
+	struct vb2_alloc_ctx    *alloc_ctx;
+
+	/* streaming buffer queue */
+	struct list_head        ready_q;
+
+	/* stream stop and restart handling */
+	struct work_struct      restart_work;
+	struct work_struct      stop_work;
+	struct timer_list       restart_timer;
+	bool                    stop; /* streaming is stopping */
+};
+
+struct v4l2_subdev *imxcam_smfc_init(struct imxcam_dev *dev);
+struct v4l2_subdev *imxcam_ic_prpenc_init(struct imxcam_dev *dev);
+struct v4l2_subdev *imxcam_vdic_init(struct imxcam_dev *dev);
+
+#endif /* _IMX_CAMIF_H */
diff --git a/drivers/staging/media/imx/capture/imx-csi.c b/drivers/staging/media/imx/capture/imx-csi.c
new file mode 100644
index 0000000..23973a6
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-csi.c
@@ -0,0 +1,195 @@
+/*
+ * V4L2 Capture CSI Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2014-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include "imx-camif.h"
+
+struct csi_priv {
+	struct device *dev;
+	struct imxcam_dev *camif;
+	struct v4l2_subdev sd;
+	struct ipu_soc *ipu;
+	struct ipu_csi *csi;
+};
+
+static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev)
+{
+	return container_of(sdev, struct csi_priv, sd);
+}
+
+/*
+ * Update the CSI whole sensor and active windows, and initialize
+ * the CSI interface and muxes.
+ */
+static void csi_setup(struct csi_priv *priv)
+{
+	struct imxcam_dev *camif = priv->camif;
+	int vc_num = camif->sensor->csi_ep.base.id;
+	bool is_csi2 = camif->sensor->ep.bus_type == V4L2_MBUS_CSI2;
+	enum ipu_csi_dest dest;
+
+	ipu_csi_set_window(priv->csi, &camif->crop);
+	ipu_csi_init_interface(priv->csi, &camif->mbus_cfg,
+			       &camif->sensor_fmt);
+	if (is_csi2)
+		ipu_csi_set_mipi_datatype(priv->csi, vc_num,
+					  &camif->sensor_fmt);
+
+	/* select either parallel or MIPI-CSI2 as input to our CSI */
+	ipu_csi_set_src(priv->csi, vc_num, is_csi2);
+
+	/* set CSI destination */
+	if (camif->using_vdic && camif->vdic_direct)
+		dest = IPU_CSI_DEST_VDIC;
+	else if (camif->using_ic && !camif->using_vdic)
+		dest = IPU_CSI_DEST_IC;
+	else
+		dest = IPU_CSI_DEST_IDMAC;
+	ipu_csi_set_dest(priv->csi, dest);
+
+	ipu_csi_dump(priv->csi);
+}
+
+static void csi_put_ipu_resources(struct csi_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->csi))
+		ipu_csi_put(priv->csi);
+	priv->csi = NULL;
+}
+
+static int csi_get_ipu_resources(struct csi_priv *priv)
+{
+	struct imxcam_dev *camif = priv->camif;
+	int csi_id = camif->sensor->csi_ep.base.port;
+
+	priv->ipu = dev_get_drvdata(priv->dev->parent);
+
+	priv->csi = ipu_csi_get(priv->ipu, csi_id);
+	if (IS_ERR(priv->csi)) {
+		v4l2_err(&priv->sd, "failed to get CSI %d\n", csi_id);
+		return PTR_ERR(priv->csi);
+	}
+
+	return 0;
+}
+
+static int csi_start(struct csi_priv *priv)
+{
+	int err;
+
+	err = csi_get_ipu_resources(priv);
+	if (err)
+		return err;
+
+	csi_setup(priv);
+
+	err = ipu_csi_enable(priv->csi);
+	if (err) {
+		v4l2_err(&priv->sd, "CSI enable error: %d\n", err);
+		goto out_put_ipu;
+	}
+
+	return 0;
+
+out_put_ipu:
+	csi_put_ipu_resources(priv);
+	return err;
+}
+
+static int csi_stop(struct csi_priv *priv)
+{
+	ipu_csi_disable(priv->csi);
+
+	csi_put_ipu_resources(priv);
+
+	return 0;
+}
+
+static int csi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct csi_priv *priv = v4l2_get_subdevdata(sd);
+
+	if (!sd->v4l2_dev || !sd->v4l2_dev->dev)
+		return -ENODEV;
+
+	/* get imxcam host device */
+	priv->camif = dev_get_drvdata(sd->v4l2_dev->dev);
+
+	return enable ? csi_start(priv) : csi_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops csi_video_ops = {
+	.s_stream = csi_s_stream,
+};
+
+static struct v4l2_subdev_ops csi_subdev_ops = {
+	.video = &csi_video_ops,
+};
+
+static int imxcam_csi_probe(struct platform_device *pdev)
+{
+	struct csi_priv *priv;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, &priv->sd);
+
+	priv->dev = &pdev->dev;
+
+	v4l2_subdev_init(&priv->sd, &csi_subdev_ops);
+	v4l2_set_subdevdata(&priv->sd, priv);
+	priv->sd.dev = &pdev->dev;
+	priv->sd.owner = THIS_MODULE;
+	strlcpy(priv->sd.name, "imx-camera-csi", sizeof(priv->sd.name));
+
+	return v4l2_async_register_subdev(&priv->sd);
+}
+
+static int imxcam_csi_remove(struct platform_device *pdev)
+{
+	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+	struct csi_priv *priv = sd_to_dev(sd);
+
+	v4l2_async_unregister_subdev(&priv->sd);
+	v4l2_device_unregister_subdev(sd);
+
+	return 0;
+}
+
+static const struct platform_device_id imxcam_csi_ids[] = {
+	{ .name = "imx-ipuv3-csi" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, imxcam_csi_ids);
+
+static struct platform_driver imxcam_csi_driver = {
+	.probe = imxcam_csi_probe,
+	.remove = imxcam_csi_remove,
+	.id_table = imxcam_csi_ids,
+	.driver = {
+		.name = "imx-ipuv3-csi",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(imxcam_csi_driver);
+
+MODULE_AUTHOR("Mentor Graphics Inc.");
+MODULE_DESCRIPTION("i.MX CSI subdev driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-ipuv3-csi");
diff --git a/drivers/staging/media/imx/capture/imx-ic-prpenc.c b/drivers/staging/media/imx/capture/imx-ic-prpenc.c
new file mode 100644
index 0000000..f0bae79
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-ic-prpenc.c
@@ -0,0 +1,660 @@
+/*
+ * V4L2 Capture Encoder Subdev for Freescale i.MX5/6 SOC
+ *
+ * This subdevice handles capture of video frames from the CSI, which
+ * routed directly to the Image Converter preprocess encode task, for
+ * resizing, colorspace conversion, and rotation.
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+
+struct prpenc_priv {
+	struct imxcam_dev    *dev;
+	struct v4l2_subdev    sd;
+
+	struct ipu_soc       *ipu;
+	struct ipuv3_channel *enc_ch;
+	struct ipuv3_channel *enc_rot_in_ch;
+	struct ipuv3_channel *enc_rot_out_ch;
+	struct ipu_ic *ic_enc;
+	struct ipu_smfc *smfc;
+
+	struct v4l2_mbus_framefmt inf; /* input sensor format */
+	struct v4l2_pix_format outf;   /* output user format */
+	enum ipu_color_space in_cs;    /* input colorspace */
+	enum ipu_color_space out_cs;   /* output colorspace */
+
+	/* active (undergoing DMA) buffers, one for each IPU buffer */
+	struct imxcam_buffer *active_frame[2];
+
+	struct imxcam_dma_buf rot_buf[2];
+	struct imxcam_dma_buf underrun_buf;
+	int buf_num;
+
+	struct timer_list eof_timeout_timer;
+	int eof_irq;
+	int nfb4eof_irq;
+
+	bool last_eof;  /* waiting for last EOF at stream off */
+	struct completion last_eof_comp;
+};
+
+static void prpenc_put_ipu_resources(struct prpenc_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->ic_enc))
+		ipu_ic_put(priv->ic_enc);
+	priv->ic_enc = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->enc_ch))
+		ipu_idmac_put(priv->enc_ch);
+	priv->enc_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->enc_rot_in_ch))
+		ipu_idmac_put(priv->enc_rot_in_ch);
+	priv->enc_rot_in_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->enc_rot_out_ch))
+		ipu_idmac_put(priv->enc_rot_out_ch);
+	priv->enc_rot_out_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->smfc))
+		ipu_smfc_put(priv->smfc);
+	priv->smfc = NULL;
+}
+
+static int prpenc_get_ipu_resources(struct prpenc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct v4l2_subdev *csi_sd = dev->sensor->csi_sd;
+	int ret;
+
+	priv->ipu = dev_get_drvdata(csi_sd->dev->parent);
+
+	priv->ic_enc = ipu_ic_get(priv->ipu, IC_TASK_ENCODER);
+	if (IS_ERR(priv->ic_enc)) {
+		v4l2_err(&priv->sd, "failed to get IC ENC\n");
+		ret = PTR_ERR(priv->ic_enc);
+		goto out;
+	}
+
+	priv->enc_ch = ipu_idmac_get(priv->ipu,
+				     IPUV3_CHANNEL_IC_PRP_ENC_MEM);
+	if (IS_ERR(priv->enc_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 IPUV3_CHANNEL_IC_PRP_ENC_MEM);
+		ret = PTR_ERR(priv->enc_ch);
+		goto out;
+	}
+
+	priv->enc_rot_in_ch = ipu_idmac_get(priv->ipu,
+					    IPUV3_CHANNEL_MEM_ROT_ENC);
+	if (IS_ERR(priv->enc_rot_in_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 IPUV3_CHANNEL_MEM_ROT_ENC);
+		ret = PTR_ERR(priv->enc_rot_in_ch);
+		goto out;
+	}
+
+	priv->enc_rot_out_ch = ipu_idmac_get(priv->ipu,
+					     IPUV3_CHANNEL_ROT_ENC_MEM);
+	if (IS_ERR(priv->enc_rot_out_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 IPUV3_CHANNEL_ROT_ENC_MEM);
+		ret = PTR_ERR(priv->enc_rot_out_ch);
+		goto out;
+	}
+
+	return 0;
+out:
+	prpenc_put_ipu_resources(priv);
+	return ret;
+}
+
+static irqreturn_t prpenc_eof_interrupt(int irq, void *dev_id)
+{
+	struct prpenc_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_ctx *ctx = dev->io_ctx;
+	struct imxcam_buffer *frame;
+	struct ipuv3_channel *channel;
+	enum vb2_buffer_state state;
+	struct timeval cur_timeval;
+	u64 cur_time_ns;
+	unsigned long flags;
+	dma_addr_t phys;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	cur_time_ns = ktime_get_ns();
+	cur_timeval = ns_to_timeval(cur_time_ns);
+
+	/* timestamp and return the completed frame */
+	frame = priv->active_frame[priv->buf_num];
+	if (frame) {
+		frame->vb.timestamp = cur_time_ns;
+		state = (dev->signal_locked &&
+			 !atomic_read(&dev->pending_restart)) ?
+			VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
+		vb2_buffer_done(&frame->vb, state);
+	}
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	if (dev->fim.eof && dev->fim.eof(dev, &cur_timeval))
+		v4l2_subdev_notify(&priv->sd, IMXCAM_FRAME_INTERVAL_NOTIFY,
+				   NULL);
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	if (!list_empty(&ctx->ready_q)) {
+		frame = list_entry(ctx->ready_q.next,
+				   struct imxcam_buffer, list);
+		phys = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[priv->buf_num] = frame;
+	} else {
+		phys = priv->underrun_buf.phys;
+		priv->active_frame[priv->buf_num] = NULL;
+	}
+
+	channel = (ipu_rot_mode_is_irt(dev->rot_mode)) ?
+		priv->enc_rot_out_ch : priv->enc_ch;
+
+	if (ipu_idmac_buffer_is_ready(channel, priv->buf_num))
+		ipu_idmac_clear_buffer(channel, priv->buf_num);
+
+	ipu_cpmem_set_buffer(channel, priv->buf_num, phys);
+	ipu_idmac_select_buffer(channel, priv->buf_num);
+
+	priv->buf_num ^= 1;
+
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t prpenc_nfb4eof_interrupt(int irq, void *dev_id)
+{
+	struct prpenc_priv *priv = dev_id;
+
+	v4l2_err(&priv->sd, "NFB4EOF\n");
+
+	/*
+	 * It has been discovered that with rotation, stream off
+	 * creates a single NFB4EOF event which is 100% repeatable. So
+	 * scheduling a restart here causes an endless NFB4EOF-->restart
+	 * cycle. The error itself seems innocuous, capture is not adversely
+	 * affected.
+	 *
+	 * So don't schedule a restart on NFB4EOF error. If the source
+	 * of the NFB4EOF event on disable is ever found, it can
+	 * be re-enabled, but is probably not necessary. Detecting the
+	 * interrupt (and clearing the irq status in the IPU) seems to
+	 * be enough.
+	 */
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function.
+ */
+static void prpenc_eof_timeout(unsigned long data)
+{
+	struct prpenc_priv *priv = (struct prpenc_priv *)data;
+
+	v4l2_err(&priv->sd, "EOF timeout\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_EOF_TIMEOUT_NOTIFY, NULL);
+}
+
+static void prpenc_free_dma_buf(struct prpenc_priv *priv,
+				 struct imxcam_dma_buf *buf)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	if (buf->virt)
+		dma_free_coherent(dev->dev, buf->len, buf->virt, buf->phys);
+
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int prpenc_alloc_dma_buf(struct prpenc_priv *priv,
+				 struct imxcam_dma_buf *buf,
+				 int size)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	prpenc_free_dma_buf(priv, buf);
+
+	buf->len = PAGE_ALIGN(size);
+	buf->virt = dma_alloc_coherent(dev->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		v4l2_err(&priv->sd, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void prpenc_setup_channel(struct prpenc_priv *priv,
+				  struct ipuv3_channel *channel,
+				  enum ipu_rotate_mode rot_mode,
+				  dma_addr_t addr0, dma_addr_t addr1,
+				  bool rot_swap_width_height)
+{
+	struct imxcam_dev *dev = priv->dev;
+	u32 width, height, stride;
+	unsigned int burst_size;
+	struct ipu_image image;
+
+	if (rot_swap_width_height) {
+		width = priv->outf.height;
+		height = priv->outf.width;
+	} else {
+		width = priv->outf.width;
+		height = priv->outf.height;
+	}
+
+	stride = dev->user_pixfmt->y_depth ?
+		(width * dev->user_pixfmt->y_depth) >> 3 :
+		(width * dev->user_pixfmt->bpp) >> 3;
+
+	ipu_cpmem_zero(channel);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = width;
+	image.pix.height = image.rect.height = height;
+	image.pix.bytesperline = stride;
+	image.pix.pixelformat = priv->outf.pixelformat;
+	image.phys0 = addr0;
+	image.phys1 = addr1;
+	ipu_cpmem_set_image(channel, &image);
+
+	if (channel == priv->enc_rot_in_ch ||
+	    channel == priv->enc_rot_out_ch) {
+		burst_size = 8;
+		ipu_cpmem_set_block_mode(channel);
+	} else {
+		burst_size = (width & 0xf) ? 8 : 16;
+	}
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	if (rot_mode)
+		ipu_cpmem_set_rotation(channel, rot_mode);
+
+	if (V4L2_FIELD_HAS_BOTH(priv->inf.field) && channel == priv->enc_ch)
+		ipu_cpmem_interlaced_scan(channel, stride);
+
+	ipu_ic_task_idma_init(priv->ic_enc, channel, width, height,
+			      burst_size, rot_mode);
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, true);
+}
+
+static int prpenc_setup_rotation(struct prpenc_priv *priv,
+				  dma_addr_t phys0, dma_addr_t phys1)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int ret;
+
+	ret = prpenc_alloc_dma_buf(priv, &priv->underrun_buf,
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		return ret;
+	}
+
+	ret = prpenc_alloc_dma_buf(priv, &priv->rot_buf[0],
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc rot_buf[0], %d\n", ret);
+		goto free_underrun;
+	}
+	ret = prpenc_alloc_dma_buf(priv, &priv->rot_buf[1],
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc rot_buf[1], %d\n", ret);
+		goto free_rot0;
+	}
+
+	ret = ipu_ic_task_init(priv->ic_enc,
+			       priv->inf.width, priv->inf.height,
+			       priv->outf.height, priv->outf.width,
+			       priv->in_cs, priv->out_cs);
+	if (ret) {
+		v4l2_err(&priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+		goto free_rot1;
+	}
+
+	/* init the IC ENC-->MEM IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_ch,
+			      IPU_ROTATE_NONE,
+			      priv->rot_buf[0].phys,
+			      priv->rot_buf[1].phys,
+			      true);
+
+	/* init the MEM-->IC ENC ROT IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_rot_in_ch,
+			      dev->rot_mode,
+			      priv->rot_buf[0].phys,
+			      priv->rot_buf[1].phys,
+			      true);
+
+	/* init the destination IC ENC ROT-->MEM IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_rot_out_ch,
+			      IPU_ROTATE_NONE,
+			      phys0, phys1,
+			      false);
+
+	/* now link IC ENC-->MEM to MEM-->IC ENC ROT */
+	ipu_idmac_link(priv->enc_ch, priv->enc_rot_in_ch);
+
+	/* enable the IC */
+	ipu_ic_enable(priv->ic_enc);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->enc_ch, 0);
+	ipu_idmac_select_buffer(priv->enc_ch, 1);
+	ipu_idmac_select_buffer(priv->enc_rot_out_ch, 0);
+	ipu_idmac_select_buffer(priv->enc_rot_out_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->enc_ch);
+	ipu_idmac_enable_channel(priv->enc_rot_in_ch);
+	ipu_idmac_enable_channel(priv->enc_rot_out_ch);
+
+	/* and finally enable the IC PRPENC task */
+	ipu_ic_task_enable(priv->ic_enc);
+
+	return 0;
+
+free_rot1:
+	prpenc_free_dma_buf(priv, &priv->rot_buf[1]);
+free_rot0:
+	prpenc_free_dma_buf(priv, &priv->rot_buf[0]);
+free_underrun:
+	prpenc_free_dma_buf(priv, &priv->underrun_buf);
+	return ret;
+}
+
+static int prpenc_setup_norotation(struct prpenc_priv *priv,
+				    dma_addr_t phys0, dma_addr_t phys1)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int ret;
+
+	ret = prpenc_alloc_dma_buf(priv, &priv->underrun_buf,
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		return ret;
+	}
+
+	ret = ipu_ic_task_init(priv->ic_enc,
+			       priv->inf.width, priv->inf.height,
+			       priv->outf.width, priv->outf.height,
+			       priv->in_cs, priv->out_cs);
+	if (ret) {
+		v4l2_err(&priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+		goto free_underrun;
+	}
+
+	/* init the IC PRP-->MEM IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_ch, dev->rot_mode,
+			      phys0, phys1, false);
+
+	ipu_cpmem_dump(priv->enc_ch);
+	ipu_ic_dump(priv->ic_enc);
+	ipu_dump(priv->ipu);
+
+	ipu_ic_enable(priv->ic_enc);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->enc_ch, 0);
+	ipu_idmac_select_buffer(priv->enc_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->enc_ch);
+
+	/* enable the IC ENCODE task */
+	ipu_ic_task_enable(priv->ic_enc);
+
+	return 0;
+
+free_underrun:
+	prpenc_free_dma_buf(priv, &priv->underrun_buf);
+	return ret;
+}
+
+static int prpenc_start(struct prpenc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_ctx *ctx = dev->io_ctx;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct imxcam_buffer *frame, *tmp;
+	dma_addr_t phys[2] = {0};
+	int i = 0, ret;
+
+	ret = prpenc_get_ipu_resources(priv);
+	if (ret)
+		return ret;
+
+	list_for_each_entry_safe(frame, tmp, &ctx->ready_q, list) {
+		phys[i] = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[i++] = frame;
+		if (i >= 2)
+			break;
+	}
+
+	priv->inf = dev->sensor_fmt;
+	priv->inf.width = dev->crop.width;
+	priv->inf.height = dev->crop.height;
+	priv->in_cs = ipu_mbus_code_to_colorspace(priv->inf.code);
+
+	priv->outf = dev->user_fmt.fmt.pix;
+	priv->out_cs = ipu_pixelformat_to_colorspace(priv->outf.pixelformat);
+
+	priv->buf_num = 0;
+
+	/* init EOF completion waitq */
+	init_completion(&priv->last_eof_comp);
+	priv->last_eof = false;
+
+	/* set IC to receive from CSI */
+	ipu_ic_set_src(priv->ic_enc, csi_id, false);
+
+	if (ipu_rot_mode_is_irt(dev->rot_mode))
+		ret = prpenc_setup_rotation(priv, phys[0], phys[1]);
+	else
+		ret = prpenc_setup_norotation(priv, phys[0], phys[1]);
+	if (ret)
+		goto out_put_ipu;
+
+	priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						 priv->enc_ch,
+						 IPU_IRQ_NFB4EOF);
+	ret = devm_request_irq(dev->dev, priv->nfb4eof_irq,
+			       prpenc_nfb4eof_interrupt, 0,
+			       "imxcam-enc-nfb4eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode NFB4EOF irq: %d\n", ret);
+		goto out_put_ipu;
+	}
+
+	if (ipu_rot_mode_is_irt(dev->rot_mode))
+		priv->eof_irq = ipu_idmac_channel_irq(
+			priv->ipu, priv->enc_rot_out_ch, IPU_IRQ_EOF);
+	else
+		priv->eof_irq = ipu_idmac_channel_irq(
+			priv->ipu, priv->enc_ch, IPU_IRQ_EOF);
+
+	ret = devm_request_irq(dev->dev, priv->eof_irq,
+			       prpenc_eof_interrupt, 0,
+			       "imxcam-enc-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode eof irq: %d\n", ret);
+		goto out_free_nfb4eof_irq;
+	}
+
+	/* sensor stream on */
+	ret = dev->sensor_set_stream(dev, 1);
+	if (ret) {
+		v4l2_err(&priv->sd, "sensor stream on failed\n");
+		goto out_free_eof_irq;
+	}
+
+	/* start the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	return 0;
+
+out_free_eof_irq:
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+out_free_nfb4eof_irq:
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+out_put_ipu:
+	prpenc_put_ipu_resources(priv);
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		vb2_buffer_done(&frame->vb, VB2_BUF_STATE_QUEUED);
+	}
+	return ret;
+}
+
+static int prpenc_stop(struct prpenc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+	int i, ret;
+
+	/* mark next EOF interrupt as the last before stream off */
+	spin_lock_irqsave(&dev->irqlock, flags);
+	priv->last_eof = true;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/*
+	 * and then wait for interrupt handler to mark completion.
+	 */
+	ret = wait_for_completion_timeout(&priv->last_eof_comp,
+					  msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+	if (ret == 0)
+		v4l2_warn(&priv->sd, "wait last encode EOF timeout\n");
+
+	/* sensor stream off */
+	ret = dev->sensor_set_stream(dev, 0);
+	if (ret)
+		v4l2_warn(&priv->sd, "sensor stream off failed\n");
+
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+
+	/* disable IC tasks and the channels */
+	ipu_ic_task_disable(priv->ic_enc);
+
+	ipu_idmac_disable_channel(priv->enc_ch);
+	if (ipu_rot_mode_is_irt(dev->rot_mode)) {
+		ipu_idmac_disable_channel(priv->enc_rot_in_ch);
+		ipu_idmac_disable_channel(priv->enc_rot_out_ch);
+	}
+
+	if (ipu_rot_mode_is_irt(dev->rot_mode))
+		ipu_idmac_unlink(priv->enc_ch, priv->enc_rot_in_ch);
+
+	ipu_ic_disable(priv->ic_enc);
+
+	prpenc_free_dma_buf(priv, &priv->rot_buf[0]);
+	prpenc_free_dma_buf(priv, &priv->rot_buf[1]);
+	prpenc_free_dma_buf(priv, &priv->underrun_buf);
+
+	prpenc_put_ipu_resources(priv);
+
+	/* cancel the EOF timeout timer */
+	del_timer_sync(&priv->eof_timeout_timer);
+
+	/* return any remaining active frames with error */
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame && frame->vb.state == VB2_BUF_STATE_ACTIVE) {
+			frame->vb.timestamp = ktime_get_ns();
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+		}
+	}
+
+	return 0;
+}
+
+static int prpenc_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct prpenc_priv *priv = v4l2_get_subdevdata(sd);
+
+	return enable ? prpenc_start(priv) : prpenc_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops prpenc_video_ops = {
+	.s_stream = prpenc_s_stream,
+};
+
+static struct v4l2_subdev_ops prpenc_subdev_ops = {
+	.video = &prpenc_video_ops,
+};
+
+struct v4l2_subdev *imxcam_ic_prpenc_init(struct imxcam_dev *dev)
+{
+	struct prpenc_priv *priv;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	init_timer(&priv->eof_timeout_timer);
+	priv->eof_timeout_timer.data = (unsigned long)priv;
+	priv->eof_timeout_timer.function = prpenc_eof_timeout;
+
+	v4l2_subdev_init(&priv->sd, &prpenc_subdev_ops);
+	strlcpy(priv->sd.name, "imx-camera-prpenc", sizeof(priv->sd.name));
+	v4l2_set_subdevdata(&priv->sd, priv);
+
+	priv->dev = dev;
+	return &priv->sd;
+}
diff --git a/drivers/staging/media/imx/capture/imx-of.c b/drivers/staging/media/imx/capture/imx-of.c
new file mode 100644
index 0000000..b6c5675
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-of.c
@@ -0,0 +1,354 @@
+/*
+ * Video Camera Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Open Firmware parsing.
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/of_platform.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include "imx-camif.h"
+
+/* parse inputs property from a sensor's upstream sink endpoint node */
+static void of_parse_sensor_inputs(struct imxcam_dev *dev,
+				   struct device_node *sink_ep,
+				   struct imxcam_sensor *sensor)
+{
+	struct imxcam_sensor_input *sinput = &sensor->input;
+	int next_input = dev->num_sensor_inputs;
+	int ret, i;
+
+	for (i = 0; i < IMXCAM_MAX_INPUTS; i++) {
+		const char *input_name;
+		u32 val;
+
+		ret = of_property_read_u32_index(sink_ep, "inputs", i, &val);
+		if (ret)
+			break;
+
+		sinput->value[i] = val;
+
+		ret = of_property_read_string_index(sink_ep, "input-names", i,
+						    &input_name);
+		/*
+		 * if input-names not provided, they will be set using
+		 * the subdev name once the subdev is known during
+		 * async bind
+		 */
+		if (!ret)
+			strncpy(sinput->name[i], input_name,
+				sizeof(sinput->name[i]));
+
+		val = 0;
+		ret = of_property_read_u32_index(sink_ep, "input-caps",
+						 i, &val);
+		sinput->caps[i] = val;
+	}
+
+	sinput->num = i;
+
+	/* if no inputs provided just assume a single input */
+	if (sinput->num == 0) {
+		sinput->num = 1;
+		sinput->caps[0] = 0;
+	}
+
+	sinput->first = next_input;
+	sinput->last = next_input + sinput->num - 1;
+
+	dev->num_sensor_inputs = sinput->last + 1;
+}
+
+static int of_parse_sensor(struct imxcam_dev *dev,
+			   struct imxcam_sensor *sensor,
+			   struct device_node *sink_ep,
+			   struct device_node *csi_port,
+			   struct device_node *sensor_node)
+{
+	struct device_node *sensor_ep, *csi_ep;
+
+	sensor_ep = of_graph_get_next_endpoint(sensor_node, NULL);
+	if (!sensor_ep)
+		return -EINVAL;
+	csi_ep = of_get_next_child(csi_port, NULL);
+	if (!csi_ep) {
+		of_node_put(sensor_ep);
+		return -EINVAL;
+	}
+
+	sensor->csi_np = csi_port;
+
+	v4l2_of_parse_endpoint(sensor_ep, &sensor->ep);
+	v4l2_of_parse_endpoint(csi_ep, &sensor->csi_ep);
+
+	of_parse_sensor_inputs(dev, sink_ep, sensor);
+
+	of_node_put(sensor_ep);
+	of_node_put(csi_ep);
+	return 0;
+}
+
+static struct v4l2_async_subdev *add_async_subdev(struct imxcam_dev *dev,
+						  struct device_node *np)
+{
+	struct v4l2_async_subdev *asd;
+	int asd_idx;
+
+	asd_idx = dev->subdev_notifier.num_subdevs;
+	if (asd_idx >= IMXCAM_MAX_SUBDEVS)
+		return ERR_PTR(-ENOSPC);
+
+	asd = &dev->async_desc[asd_idx];
+	dev->async_ptrs[asd_idx] = asd;
+
+	asd->match_type = V4L2_ASYNC_MATCH_OF;
+	asd->match.of.node = np;
+	dev->subdev_notifier.num_subdevs++;
+
+	dev_dbg(dev->dev, "%s: added %s, num %d, node %p\n",
+		__func__, np->name, dev->subdev_notifier.num_subdevs, np);
+
+	return asd;
+}
+
+/* Discover all the subdevices we need downstream from a sink endpoint */
+static int of_discover_subdevs(struct imxcam_dev *dev,
+			       struct device_node *csi_port,
+			       struct device_node *sink_ep,
+			       int *vidmux_input)
+{
+	struct device_node *rpp, *epnode = NULL;
+	struct v4l2_async_subdev *asd;
+	struct imxcam_sensor *sensor;
+	int sensor_idx, num_sink_ports;
+	int i, vidmux_idx = -1, ret = 0;
+
+	rpp = of_graph_get_remote_port_parent(sink_ep);
+	if (!rpp)
+		return 0;
+	if (!of_device_is_available(rpp))
+		goto out;
+
+	asd = add_async_subdev(dev, rpp);
+	if (IS_ERR(asd)) {
+		ret = PTR_ERR(asd);
+		goto out;
+	}
+
+	if (of_device_is_compatible(rpp, "fsl,imx-mipi-csi2")) {
+		/*
+		 * there is only one internal mipi receiver, so exit
+		 * with 0 if we've already passed through here
+		 */
+		if (dev->csi2_asd) {
+			dev->subdev_notifier.num_subdevs--;
+			ret = 0;
+			goto out;
+		}
+
+		/* the mipi csi2 receiver has only one sink port */
+		num_sink_ports = 1;
+		dev->csi2_asd = asd;
+		dev_dbg(dev->dev, "found mipi-csi2 %s\n", rpp->name);
+	} else if (of_device_is_compatible(rpp, "imx-video-mux")) {
+		/* for the video mux, all but the last port are sinks */
+		num_sink_ports = of_get_child_count(rpp) - 1;
+
+		vidmux_idx = dev->num_vidmux;
+		if (vidmux_idx >= IMXCAM_MAX_VIDEOMUX) {
+			ret = -ENOSPC;
+			goto out;
+		}
+
+		dev->vidmux_asd[vidmux_idx] = asd;
+		dev->num_vidmux++;
+		dev_dbg(dev->dev, "found video mux %s\n", rpp->name);
+	} else {
+		/* this rpp must be a sensor, it has no sink ports */
+		num_sink_ports = 0;
+
+		sensor_idx = dev->num_sensors;
+		if (sensor_idx >= IMXCAM_MAX_SENSORS)
+			return -ENOSPC;
+
+		sensor = &dev->sensor_list[sensor_idx];
+
+		ret = of_parse_sensor(dev, sensor, sink_ep, csi_port, rpp);
+		if (ret)
+			goto out;
+
+		/*
+		 * save the input indeces of all video-muxes recorded in
+		 * this pipeline path required to receive data from this
+		 * sensor.
+		 */
+		memcpy(sensor->vidmux_input, vidmux_input,
+		       sizeof(sensor->vidmux_input));
+
+		sensor->asd = asd;
+		dev->num_sensors++;
+		dev_dbg(dev->dev, "found sensor %s\n", rpp->name);
+	}
+
+	/* continue discovery downstream */
+	dev_dbg(dev->dev, "scanning %d sink ports on %s\n",
+		num_sink_ports, rpp->name);
+
+	for (i = 0; i < num_sink_ports; i++) {
+		epnode = of_graph_get_next_endpoint(rpp, epnode);
+		if (!epnode) {
+			v4l2_err(&dev->v4l2_dev,
+				 "no endpoint at port %d on %s\n",
+				 i, rpp->name);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (vidmux_idx >= 0)
+			vidmux_input[vidmux_idx] = i;
+
+		ret = of_discover_subdevs(dev, csi_port, epnode, vidmux_input);
+		of_node_put(epnode);
+		if (ret)
+			break;
+	}
+
+out:
+	of_node_put(rpp);
+	return ret;
+}
+
+static int of_parse_ports(struct imxcam_dev *dev, struct device_node *np)
+{
+	struct device_node *port, *epnode;
+	struct v4l2_async_subdev *asd;
+	int vidmux_inputs[IMXCAM_MAX_VIDEOMUX];
+	int i, j, csi_idx, ret = 0;
+
+	for (i = 0; ; i++) {
+		port = of_parse_phandle(np, "ports", i);
+		if (!port) {
+			ret = 0;
+			break;
+		}
+
+		csi_idx = dev->num_csi;
+		if (csi_idx >= IMXCAM_MAX_CSI) {
+			ret = -ENOSPC;
+			break;
+		}
+		/* register the CSI subdev */
+		asd = add_async_subdev(dev, port);
+		if (IS_ERR(asd)) {
+			ret = PTR_ERR(asd);
+			break;
+		}
+		dev->csi_asd[csi_idx] = asd;
+		dev->num_csi++;
+
+		/*
+		 * discover and register all async subdevs downstream
+		 * from this CSI port.
+		 */
+		for_each_child_of_node(port, epnode) {
+			for (j = 0; j < IMXCAM_MAX_VIDEOMUX; j++)
+				vidmux_inputs[j] = -1;
+
+			ret = of_discover_subdevs(dev, port, epnode,
+						  vidmux_inputs);
+			of_node_put(epnode);
+			if (ret)
+				break;
+		}
+
+		of_node_put(port);
+		if (ret)
+			break;
+	}
+
+	if (ret)
+		return ret;
+
+	if (!dev->num_sensors) {
+		v4l2_err(&dev->v4l2_dev, "no sensors found!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int of_parse_fim(struct imxcam_dev *dev, struct device_node *np)
+{
+	struct imxcam_fim *fim = &dev->fim;
+	struct device_node *fim_np;
+	u32 val, tol[2], icap[2];
+	int ret;
+
+	fim_np = of_get_child_by_name(np, "fim");
+	if (!fim_np) {
+		/* set to the default defaults */
+		fim->of_defaults[FIM_CL_ENABLE] = FIM_CL_ENABLE_DEF;
+		fim->of_defaults[FIM_CL_NUM] = FIM_CL_NUM_DEF;
+		fim->of_defaults[FIM_CL_NUM_SKIP] = FIM_CL_NUM_SKIP_DEF;
+		fim->of_defaults[FIM_CL_TOLERANCE_MIN] =
+			FIM_CL_TOLERANCE_MIN_DEF;
+		fim->of_defaults[FIM_CL_TOLERANCE_MAX] =
+			FIM_CL_TOLERANCE_MAX_DEF;
+		fim->icap_channel = -1;
+		return 0;
+	}
+
+	ret = of_property_read_u32(fim_np, "enable", &val);
+	if (ret)
+		val = FIM_CL_ENABLE_DEF;
+	fim->of_defaults[FIM_CL_ENABLE] = val;
+
+	ret = of_property_read_u32(fim_np, "num-avg", &val);
+	if (ret)
+		val = FIM_CL_NUM_DEF;
+	fim->of_defaults[FIM_CL_NUM] = val;
+
+	ret = of_property_read_u32(fim_np, "num-skip", &val);
+	if (ret)
+		val = FIM_CL_NUM_SKIP_DEF;
+	fim->of_defaults[FIM_CL_NUM_SKIP] = val;
+
+	ret = of_property_read_u32_array(fim_np, "tolerance-range", tol, 2);
+	if (ret) {
+		tol[0] = FIM_CL_TOLERANCE_MIN_DEF;
+		tol[1] = FIM_CL_TOLERANCE_MAX_DEF;
+	}
+	fim->of_defaults[FIM_CL_TOLERANCE_MIN] = tol[0];
+	fim->of_defaults[FIM_CL_TOLERANCE_MAX] = tol[1];
+
+	ret = of_property_read_u32_array(fim_np, "input-capture-channel",
+					 icap, 2);
+	if (!ret) {
+		fim->icap_channel = icap[0];
+		fim->icap_flags = icap[1];
+	} else {
+		fim->icap_channel = -1;
+	}
+
+	of_node_put(fim_np);
+	return 0;
+}
+
+int imxcam_of_parse(struct imxcam_dev *dev, struct device_node *np)
+{
+	int ret = of_parse_fim(dev, np);
+	if (ret)
+		return ret;
+
+	return of_parse_ports(dev, np);
+}
diff --git a/drivers/staging/media/imx/capture/imx-of.h b/drivers/staging/media/imx/capture/imx-of.h
new file mode 100644
index 0000000..6f233bf
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-of.h
@@ -0,0 +1,18 @@
+/*
+ * Video Camera Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Open Firmware parsing.
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _IMX_CAM_OF_H
+#define _IMX_CAM_OF_H
+
+extern int imxcam_of_parse(struct imxcam_dev *dev, struct device_node *np);
+
+#endif
diff --git a/drivers/staging/media/imx/capture/imx-smfc.c b/drivers/staging/media/imx/capture/imx-smfc.c
new file mode 100644
index 0000000..4429c8e
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-smfc.c
@@ -0,0 +1,505 @@
+/*
+ * V4L2 Capture SMFC Subdev for Freescale i.MX5/6 SOC
+ *
+ * This subdevice handles capture of raw/unconverted video frames
+ * from the CSI, directly to memory via the Sensor Multi-FIFO Controller.
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+
+struct imx_smfc_priv {
+	struct imxcam_dev    *dev;
+	struct v4l2_subdev    sd;
+
+	struct ipu_soc *ipu;
+	struct ipuv3_channel *smfc_ch;
+	struct ipu_smfc *smfc;
+
+	struct v4l2_mbus_framefmt inf; /* input sensor format */
+	struct v4l2_pix_format outf;   /* output user format */
+
+	/* active (undergoing DMA) buffers, one for each IPU buffer */
+	struct imxcam_buffer *active_frame[2];
+
+	struct imxcam_dma_buf underrun_buf;
+	int buf_num;
+
+	struct timer_list eof_timeout_timer;
+	int eof_irq;
+	int nfb4eof_irq;
+
+	bool last_eof;  /* waiting for last EOF at stream off */
+	struct completion last_eof_comp;
+};
+
+static void imx_smfc_put_ipu_resources(struct imx_smfc_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->smfc_ch))
+		ipu_idmac_put(priv->smfc_ch);
+	priv->smfc_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->smfc))
+		ipu_smfc_put(priv->smfc);
+	priv->smfc = NULL;
+}
+
+static int imx_smfc_get_ipu_resources(struct imx_smfc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct v4l2_subdev *csi_sd = dev->sensor->csi_sd;
+	int csi_ch_num, ret;
+
+	priv->ipu = dev_get_drvdata(csi_sd->dev->parent);
+
+	/*
+	 * Choose the direct CSI-->SMFC-->MEM channel corresponding
+	 * to the IPU and CSI IDs.
+	 */
+	csi_ch_num = IPUV3_CHANNEL_CSI0 +
+		(ipu_get_num(priv->ipu) << 1) + csi_id;
+
+	priv->smfc = ipu_smfc_get(priv->ipu, csi_ch_num);
+	if (IS_ERR(priv->smfc)) {
+		v4l2_err(&priv->sd, "failed to get SMFC\n");
+		ret = PTR_ERR(priv->smfc);
+		goto out;
+	}
+
+	priv->smfc_ch = ipu_idmac_get(priv->ipu, csi_ch_num);
+	if (IS_ERR(priv->smfc_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 csi_ch_num);
+		ret = PTR_ERR(priv->smfc_ch);
+		goto out;
+	}
+
+	return 0;
+out:
+	imx_smfc_put_ipu_resources(priv);
+	return ret;
+}
+
+static irqreturn_t imx_smfc_eof_interrupt(int irq, void *dev_id)
+{
+	struct imx_smfc_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_ctx *ctx = dev->io_ctx;
+	struct imxcam_buffer *frame;
+	enum vb2_buffer_state state;
+	struct timeval cur_timeval;
+	unsigned long flags;
+	u64 cur_time_ns;
+	dma_addr_t phys;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	cur_time_ns = ktime_get_ns();
+	cur_timeval = ns_to_timeval(cur_time_ns);
+
+	/* timestamp and return the completed frame */
+	frame = priv->active_frame[priv->buf_num];
+	if (frame) {
+		frame->vb.timestamp = cur_time_ns;
+		state = (dev->signal_locked &&
+			 !atomic_read(&dev->pending_restart)) ?
+			VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
+		vb2_buffer_done(&frame->vb, state);
+	}
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	if (dev->fim.eof && dev->fim.eof(dev, &cur_timeval))
+		v4l2_subdev_notify(&priv->sd, IMXCAM_FRAME_INTERVAL_NOTIFY,
+				   NULL);
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	if (!list_empty(&ctx->ready_q)) {
+		frame = list_entry(ctx->ready_q.next,
+				   struct imxcam_buffer, list);
+		phys = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[priv->buf_num] = frame;
+	} else {
+		phys = priv->underrun_buf.phys;
+		priv->active_frame[priv->buf_num] = NULL;
+	}
+
+	if (ipu_idmac_buffer_is_ready(priv->smfc_ch, priv->buf_num))
+		ipu_idmac_clear_buffer(priv->smfc_ch, priv->buf_num);
+
+	ipu_cpmem_set_buffer(priv->smfc_ch, priv->buf_num, phys);
+	ipu_idmac_select_buffer(priv->smfc_ch, priv->buf_num);
+
+	priv->buf_num ^= 1;
+
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_smfc_nfb4eof_interrupt(int irq, void *dev_id)
+{
+	struct imx_smfc_priv *priv = dev_id;
+
+	v4l2_err(&priv->sd, "NFB4EOF\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_NFB4EOF_NOTIFY, NULL);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function.
+ */
+static void imx_smfc_eof_timeout(unsigned long data)
+{
+	struct imx_smfc_priv *priv = (struct imx_smfc_priv *)data;
+
+	v4l2_err(&priv->sd, "EOF timeout\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_EOF_TIMEOUT_NOTIFY, NULL);
+}
+
+static void imx_smfc_free_dma_buf(struct imx_smfc_priv *priv,
+				 struct imxcam_dma_buf *buf)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	if (buf->virt)
+		dma_free_coherent(dev->dev, buf->len, buf->virt, buf->phys);
+
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int imx_smfc_alloc_dma_buf(struct imx_smfc_priv *priv,
+				 struct imxcam_dma_buf *buf,
+				 int size)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	imx_smfc_free_dma_buf(priv, buf);
+
+	buf->len = PAGE_ALIGN(size);
+	buf->virt = dma_alloc_coherent(dev->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		v4l2_err(&priv->sd, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* init the IC PRPENC-->MEM IDMAC channel */
+static void imx_smfc_setup_channel(struct imx_smfc_priv *priv,
+				  dma_addr_t addr0, dma_addr_t addr1,
+				  bool rot_swap_width_height)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	int vc_num = dev->sensor->csi_ep.base.id;
+	u32 width, height, stride;
+	unsigned int burst_size;
+	struct ipu_image image;
+	bool passthrough;
+
+	width = priv->outf.width;
+	height = priv->outf.height;
+
+	stride = dev->user_pixfmt->y_depth ?
+		(width * dev->user_pixfmt->y_depth) >> 3 :
+		(width * dev->user_pixfmt->bpp) >> 3;
+
+	ipu_cpmem_zero(priv->smfc_ch);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = width;
+	image.pix.height = image.rect.height = height;
+	image.pix.bytesperline = stride;
+	image.pix.pixelformat = priv->outf.pixelformat;
+	image.phys0 = addr0;
+	image.phys1 = addr1;
+	ipu_cpmem_set_image(priv->smfc_ch, &image);
+
+	burst_size = (width & 0xf) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(priv->smfc_ch, burst_size);
+
+	/*
+	 * If the sensor uses 16-bit parallel CSI bus, we must handle
+	 * the data internally in the IPU as 16-bit generic, aka
+	 * passthrough mode.
+	 */
+	passthrough = (dev->sensor->ep.bus_type != V4L2_MBUS_CSI2 &&
+		       dev->sensor->ep.bus.parallel.bus_width >= 16);
+
+	if (passthrough)
+		ipu_cpmem_set_format_passthrough(priv->smfc_ch, 16);
+
+	if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2)
+		ipu_smfc_map_channel(priv->smfc, csi_id, vc_num);
+	else
+		ipu_smfc_map_channel(priv->smfc, csi_id, 0);
+
+	/*
+	 * Set the channel for the direct CSI-->memory via SMFC
+	 * use-case to very high priority, by enabling the watermark
+	 * signal in the SMFC, enabling WM in the channel, and setting
+	 * the channel priority to high.
+	 *
+	 * Refer to the i.mx6 rev. D TRM Table 36-8: Calculated priority
+	 * value.
+	 *
+	 * The WM's are set very low by intention here to ensure that
+	 * the SMFC FIFOs do not overflow.
+	 */
+	ipu_smfc_set_watermark(priv->smfc, 0x02, 0x01);
+	ipu_cpmem_set_high_priority(priv->smfc_ch);
+	ipu_idmac_enable_watermark(priv->smfc_ch, true);
+	ipu_cpmem_set_axi_id(priv->smfc_ch, 0);
+	ipu_idmac_lock_enable(priv->smfc_ch, 8);
+
+	burst_size = ipu_cpmem_get_burstsize(priv->smfc_ch);
+	burst_size = passthrough ? (burst_size >> 3) - 1 : (burst_size >> 2) - 1;
+
+	ipu_smfc_set_burstsize(priv->smfc, burst_size);
+
+	if (V4L2_FIELD_HAS_BOTH(priv->inf.field))
+		ipu_cpmem_interlaced_scan(priv->smfc_ch, stride);
+
+	ipu_idmac_set_double_buffer(priv->smfc_ch, true);
+}
+
+static int imx_smfc_setup(struct imx_smfc_priv *priv,
+			  dma_addr_t phys0, dma_addr_t phys1)
+{
+	int ret;
+
+	ret = imx_smfc_alloc_dma_buf(priv, &priv->underrun_buf,
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		return ret;
+	}
+
+	imx_smfc_setup_channel(priv, phys0, phys1, false);
+
+	ipu_cpmem_dump(priv->smfc_ch);
+	ipu_dump(priv->ipu);
+
+	ipu_smfc_enable(priv->smfc);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->smfc_ch, 0);
+	ipu_idmac_select_buffer(priv->smfc_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->smfc_ch);
+
+	return 0;
+}
+
+static int imx_smfc_start(struct imx_smfc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_ctx *ctx = dev->io_ctx;
+	struct imxcam_buffer *frame, *tmp;
+	dma_addr_t phys[2] = {0};
+	int i = 0, ret;
+
+	ret = imx_smfc_get_ipu_resources(priv);
+	if (ret)
+		return ret;
+
+	list_for_each_entry_safe(frame, tmp, &ctx->ready_q, list) {
+		phys[i] = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[i++] = frame;
+		if (i >= 2)
+			break;
+	}
+
+	priv->inf = dev->sensor_fmt;
+	priv->inf.width = dev->crop.width;
+	priv->inf.height = dev->crop.height;
+	priv->outf = dev->user_fmt.fmt.pix;
+
+	priv->buf_num = 0;
+
+	/* init EOF completion waitq */
+	init_completion(&priv->last_eof_comp);
+	priv->last_eof = false;
+
+	ret = imx_smfc_setup(priv, phys[0], phys[1]);
+	if (ret)
+		goto out_put_ipu;
+
+	priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						 priv->smfc_ch,
+						 IPU_IRQ_NFB4EOF);
+	ret = devm_request_irq(dev->dev, priv->nfb4eof_irq,
+			       imx_smfc_nfb4eof_interrupt, 0,
+			       "imxcam-enc-nfb4eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode NFB4EOF irq: %d\n", ret);
+		goto out_put_ipu;
+	}
+
+	priv->eof_irq = ipu_idmac_channel_irq(priv->ipu, priv->smfc_ch,
+					      IPU_IRQ_EOF);
+
+	ret = devm_request_irq(dev->dev, priv->eof_irq,
+			       imx_smfc_eof_interrupt, 0,
+			       "imxcam-enc-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode eof irq: %d\n", ret);
+		goto out_free_nfb4eof_irq;
+	}
+
+	/* sensor stream on */
+	ret = dev->sensor_set_stream(dev, 1);
+	if (ret) {
+		v4l2_err(&priv->sd, "sensor stream on failed\n");
+		goto out_free_eof_irq;
+	}
+
+	/* start the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	return 0;
+
+out_free_eof_irq:
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+out_free_nfb4eof_irq:
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+out_put_ipu:
+	imx_smfc_put_ipu_resources(priv);
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		vb2_buffer_done(&frame->vb, VB2_BUF_STATE_QUEUED);
+	}
+	return ret;
+}
+
+static int imx_smfc_stop(struct imx_smfc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+	int i, ret;
+
+	/* mark next EOF interrupt as the last before stream off */
+	spin_lock_irqsave(&dev->irqlock, flags);
+	priv->last_eof = true;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/*
+	 * and then wait for interrupt handler to mark completion.
+	 */
+	ret = wait_for_completion_timeout(&priv->last_eof_comp,
+					  msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+	if (ret == 0)
+		v4l2_warn(&priv->sd, "wait last encode EOF timeout\n");
+
+	/* sensor stream off */
+	ret = dev->sensor_set_stream(dev, 0);
+	if (ret)
+		v4l2_warn(&priv->sd, "sensor stream off failed\n");
+
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+
+	ipu_idmac_disable_channel(priv->smfc_ch);
+
+	ipu_smfc_disable(priv->smfc);
+
+	imx_smfc_free_dma_buf(priv, &priv->underrun_buf);
+
+	imx_smfc_put_ipu_resources(priv);
+
+	/* cancel the EOF timeout timer */
+	del_timer_sync(&priv->eof_timeout_timer);
+
+	/* return any remaining active frames with error */
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame && frame->vb.state == VB2_BUF_STATE_ACTIVE) {
+			frame->vb.timestamp = ktime_get_ns();
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+		}
+	}
+
+	return 0;
+}
+
+static int imx_smfc_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imx_smfc_priv *priv = v4l2_get_subdevdata(sd);
+
+	return enable ? imx_smfc_start(priv) : imx_smfc_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops imx_smfc_video_ops = {
+	.s_stream = imx_smfc_s_stream,
+};
+
+static struct v4l2_subdev_ops imx_smfc_subdev_ops = {
+	.video = &imx_smfc_video_ops,
+};
+
+struct v4l2_subdev *imxcam_smfc_init(struct imxcam_dev *dev)
+{
+	struct imx_smfc_priv *priv;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	init_timer(&priv->eof_timeout_timer);
+	priv->eof_timeout_timer.data = (unsigned long)priv;
+	priv->eof_timeout_timer.function = imx_smfc_eof_timeout;
+
+	v4l2_subdev_init(&priv->sd, &imx_smfc_subdev_ops);
+	strlcpy(priv->sd.name, "imx-camera-smfc", sizeof(priv->sd.name));
+	v4l2_set_subdevdata(&priv->sd, priv);
+
+	priv->dev = dev;
+	return &priv->sd;
+}
diff --git a/drivers/staging/media/imx/capture/imx-vdic.c b/drivers/staging/media/imx/capture/imx-vdic.c
new file mode 100644
index 0000000..3a73ef3
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-vdic.c
@@ -0,0 +1,994 @@
+/*
+ * V4L2 Capture Deinterlacer Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2014-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+
+/*
+ * This subdev implements two different video pipelines:
+ *
+ * CSI -> VDIC -> IC -> CH21 -> MEM
+ *
+ * In this pipeline, the CSI sends a single interlaced field F(n-1)
+ * directly to the VDIC (and optionally the following field F(n)
+ * can be sent to memory via IDMAC channel 13). So only two fields
+ * can be processed by the VDIC. This pipeline only works in VDIC's
+ * high motion mode, which only requires a single field for processing.
+ * The other motion modes (low and medium) require three fields, so this
+ * pipeline does not work in those modes. Also, it is not clear how this
+ * pipeline can deal with the various field orders (sequential BT/TB,
+ * interlaced BT/TB) and there are reported image quality issues output
+ * from the VDIC in this pipeline.
+ *
+ * CSI -> CH[0-3] -> MEM -> CH8,9,10 -> VDIC -> IC -> CH21 -> MEM
+ *
+ * In this pipeline, the CSI sends raw and full frames to memory buffers
+ * via the IDMAC SMFC channels 0-3. Fields from these frames are then
+ * transferred to the VDIC via IDMAC channels 8,9,10. The VDIC requires
+ * three fields: previous field F(n-1), current field F(n), and next
+ * field F(n+1), so we need three raw frames in memory: two completed frames
+ * to send F(n-1), F(n), F(n+1) to the VDIC, and a third frame for active
+ * CSI capture while the completed fields are sent through the VDIC->IC for
+ * processing.
+ *
+ * While the "direct" CSI->VDIC pipeline requires less memory bus bandwidth
+ * (just 1 channel vs. 5 channels for indirect pipeline), it can't be used
+ * for all motion modes, it only processes a single field (so half the
+ * original image resolution is lost), and it has the image quality issues
+ * mentioned above. With the indirect pipeline we have full control over
+ * field order. So by default the direct pipeline is disabled. Enable with
+ * the module param below, if enabled it will be used by high motion mode.
+ */
+
+static int allow_direct;
+module_param_named(direct, allow_direct, int, 0644);
+MODULE_PARM_DESC(direct, "Allow CSI->VDIC direct pipeline (default: 0)");
+
+struct vdic_priv;
+
+struct vdic_pipeline_ops {
+	int (*setup)(struct vdic_priv *priv);
+	void (*start)(struct vdic_priv *priv);
+	void (*stop)(struct vdic_priv *priv);
+	void (*disable)(struct vdic_priv *priv);
+};
+
+struct vdic_field_addr {
+	dma_addr_t prev; /* F(n-1) */
+	dma_addr_t curr; /* F(n) */
+	dma_addr_t next; /* F(n+1) */
+};
+
+struct vdic_priv {
+	struct imxcam_dev    *dev;
+	struct v4l2_subdev    sd;
+
+	/* IPU and its units we require */
+	struct ipu_soc *ipu;
+	struct ipu_ic *ic_vf;
+	struct ipu_smfc *smfc;
+	struct ipu_vdi *vdi;
+
+	struct ipuv3_channel *csi_ch;      /* raw CSI frames channel */
+	struct ipuv3_channel *vdi_in_ch_p; /* F(n-1) transfer channel */
+	struct ipuv3_channel *vdi_in_ch;   /* F(n) transfer channel */
+	struct ipuv3_channel *vdi_in_ch_n; /* F(n+1) transfer channel */
+	struct ipuv3_channel *prpvf_out_ch;/* final progressive frame channel */
+
+	/* pipeline operations */
+	struct vdic_pipeline_ops *ops;
+
+	/* active (undergoing DMA) buffers */
+	struct imxcam_buffer *active_frame[2];
+	struct imxcam_dma_buf underrun_buf;
+	int out_buf_num;
+
+	/*
+	 * Raw CSI frames for indirect pipeline, and the precalculated field
+	 * addresses for each frame. The VDIC requires three fields: previous
+	 * field F(n-1), current field F(n), and next field F(n+1), so we need
+	 * three frames in memory: two completed frames to send F(n-1), F(n),
+	 * F(n+1) to the VDIC, and a third frame for active CSI capture while
+	 * the completed fields are sent through the VDIC->IC for processing.
+	 */
+	struct imxcam_dma_buf csi_frame[3];
+	struct vdic_field_addr field[3];
+
+	int csi_frame_num; /* csi_frame index, 0-2 */
+	int csi_buf_num;   /* CSI channel double buffer index, 0-1 */
+
+	struct v4l2_mbus_framefmt inf; /* input sensor format */
+	struct v4l2_pix_format outf;   /* final output user format */
+	enum ipu_color_space in_cs;    /* input colorspace */
+	enum ipu_color_space out_cs;   /* output colorspace */
+	u32 in_pixfmt;
+
+	u32 in_stride;  /* input and output line strides */
+	u32 out_stride;
+	int field_size; /* 1/2 full image size */
+	bool direct;    /* using direct CSI->VDIC->IC pipeline */
+
+	struct timer_list eof_timeout_timer;
+
+	int csi_eof_irq; /* CSI channel EOF IRQ */
+	int nfb4eof_irq; /* CSI or PRPVF channel NFB4EOF IRQ */
+	int out_eof_irq; /* PRPVF channel EOF IRQ */
+
+	bool last_eof;  /* waiting for last EOF at vdic off */
+	struct completion last_eof_comp;
+};
+
+static void vdic_put_ipu_resources(struct vdic_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->ic_vf))
+		ipu_ic_put(priv->ic_vf);
+	priv->ic_vf = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->csi_ch))
+		ipu_idmac_put(priv->csi_ch);
+	priv->csi_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi_in_ch_p))
+		ipu_idmac_put(priv->vdi_in_ch_p);
+	priv->vdi_in_ch_p = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi_in_ch))
+		ipu_idmac_put(priv->vdi_in_ch);
+	priv->vdi_in_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi_in_ch_n))
+		ipu_idmac_put(priv->vdi_in_ch_n);
+	priv->vdi_in_ch_n = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->prpvf_out_ch))
+		ipu_idmac_put(priv->prpvf_out_ch);
+	priv->prpvf_out_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi))
+		ipu_vdi_put(priv->vdi);
+	priv->vdi = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->smfc))
+		ipu_smfc_put(priv->smfc);
+	priv->smfc = NULL;
+}
+
+static int vdic_get_ipu_resources(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct v4l2_subdev *csi_sd = dev->sensor->csi_sd;
+	int ret, err_chan;
+
+	priv->ipu = dev_get_drvdata(csi_sd->dev->parent);
+
+	priv->ic_vf = ipu_ic_get(priv->ipu, IC_TASK_VIEWFINDER);
+	if (IS_ERR(priv->ic_vf)) {
+		v4l2_err(&priv->sd, "failed to get IC VF\n");
+		ret = PTR_ERR(priv->ic_vf);
+		goto out;
+	}
+
+	priv->vdi = ipu_vdi_get(priv->ipu);
+	if (IS_ERR(priv->vdi)) {
+		v4l2_err(&priv->sd, "failed to get VDIC\n");
+		ret = PTR_ERR(priv->vdi);
+		goto out;
+	}
+
+	priv->prpvf_out_ch = ipu_idmac_get(priv->ipu,
+					   IPUV3_CHANNEL_IC_PRP_VF_MEM);
+	if (IS_ERR(priv->prpvf_out_ch)) {
+		err_chan = IPUV3_CHANNEL_IC_PRP_VF_MEM;
+		ret = PTR_ERR(priv->prpvf_out_ch);
+		goto out_err_chan;
+	}
+
+	if (!priv->direct) {
+		/*
+		 * Choose the CSI-->SMFC-->MEM channel corresponding
+		 * to the IPU and CSI IDs.
+		 */
+		int csi_ch_num = IPUV3_CHANNEL_CSI0 +
+			(ipu_get_num(priv->ipu) << 1) + csi_id;
+
+		priv->csi_ch = ipu_idmac_get(priv->ipu, csi_ch_num);
+		if (IS_ERR(priv->csi_ch)) {
+			err_chan = csi_ch_num;
+			ret = PTR_ERR(priv->csi_ch);
+			goto out_err_chan;
+		}
+
+		priv->smfc = ipu_smfc_get(priv->ipu, csi_ch_num);
+		if (IS_ERR(priv->smfc)) {
+			v4l2_err(&priv->sd, "failed to get SMFC\n");
+			ret = PTR_ERR(priv->smfc);
+			goto out;
+		}
+
+		priv->vdi_in_ch_p = ipu_idmac_get(priv->ipu,
+						  IPUV3_CHANNEL_MEM_VDI_P);
+		if (IS_ERR(priv->vdi_in_ch_p)) {
+			err_chan = IPUV3_CHANNEL_MEM_VDI_P;
+			ret = PTR_ERR(priv->vdi_in_ch_p);
+			goto out_err_chan;
+		}
+
+		priv->vdi_in_ch = ipu_idmac_get(priv->ipu,
+						IPUV3_CHANNEL_MEM_VDI);
+		if (IS_ERR(priv->vdi_in_ch)) {
+			err_chan = IPUV3_CHANNEL_MEM_VDI;
+			ret = PTR_ERR(priv->vdi_in_ch);
+			goto out_err_chan;
+		}
+
+		priv->vdi_in_ch_n = ipu_idmac_get(priv->ipu,
+						  IPUV3_CHANNEL_MEM_VDI_N);
+		if (IS_ERR(priv->vdi_in_ch_n)) {
+			err_chan = IPUV3_CHANNEL_MEM_VDI_N;
+			ret = PTR_ERR(priv->vdi_in_ch_n);
+			goto out_err_chan;
+		}
+	}
+
+	return 0;
+
+out_err_chan:
+	v4l2_err(&priv->sd, "could not get IDMAC channel %u\n", err_chan);
+out:
+	vdic_put_ipu_resources(priv);
+	return ret;
+}
+
+static void prepare_csi_buffer(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int next_frame, curr_frame;
+
+	curr_frame = priv->csi_frame_num;
+	next_frame = (curr_frame + 2) % 3;
+
+	dev_dbg(dev->dev, "%d - %d %d\n",
+		priv->csi_buf_num, curr_frame, next_frame);
+
+	ipu_cpmem_set_buffer(priv->csi_ch, priv->csi_buf_num,
+			     priv->csi_frame[next_frame].phys);
+	ipu_idmac_select_buffer(priv->csi_ch, priv->csi_buf_num);
+}
+
+static void prepare_vdi_in_buffers(struct vdic_priv *priv)
+{
+	int last_frame, curr_frame;
+
+	curr_frame = priv->csi_frame_num;
+	last_frame = curr_frame - 1;
+	if (last_frame < 0)
+		last_frame = 2;
+
+	ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0,
+			     priv->field[last_frame].prev);
+	ipu_cpmem_set_buffer(priv->vdi_in_ch,   0,
+			     priv->field[curr_frame].curr);
+	ipu_cpmem_set_buffer(priv->vdi_in_ch_n, 0,
+			     priv->field[curr_frame].next);
+
+	ipu_idmac_select_buffer(priv->vdi_in_ch_p, 0);
+	ipu_idmac_select_buffer(priv->vdi_in_ch, 0);
+	ipu_idmac_select_buffer(priv->vdi_in_ch_n, 0);
+}
+
+static void prepare_prpvf_out_buffer(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_ctx *ctx = dev->io_ctx;
+	struct imxcam_buffer *frame;
+	dma_addr_t phys;
+
+	if (!list_empty(&ctx->ready_q)) {
+		frame = list_entry(ctx->ready_q.next,
+				   struct imxcam_buffer, list);
+		phys = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[priv->out_buf_num] = frame;
+	} else {
+		phys = priv->underrun_buf.phys;
+		priv->active_frame[priv->out_buf_num] = NULL;
+	}
+
+	ipu_cpmem_set_buffer(priv->prpvf_out_ch, priv->out_buf_num, phys);
+	ipu_idmac_select_buffer(priv->prpvf_out_ch, priv->out_buf_num);
+}
+
+/* prpvf_out_ch EOF interrupt (progressive frame ready) */
+static irqreturn_t prpvf_out_eof_interrupt(int irq, void *dev_id)
+{
+	struct vdic_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	enum vb2_buffer_state state;
+	struct timeval cur_timeval;
+	u64 cur_time_ns;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	cur_time_ns = ktime_get_ns();
+	cur_timeval = ns_to_timeval(cur_time_ns);
+
+	/* timestamp and return the completed frame */
+	frame = priv->active_frame[priv->out_buf_num];
+	if (frame) {
+		frame->vb.timestamp = cur_time_ns;
+		state = (dev->signal_locked &&
+			 !atomic_read(&dev->pending_restart)) ?
+			VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
+		vb2_buffer_done(&frame->vb, state);
+	}
+
+	if (!priv->direct)
+		goto flip;
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->out_buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	prepare_prpvf_out_buffer(priv);
+
+flip:
+	priv->out_buf_num ^= 1;
+
+	if (dev->fim.eof && dev->fim.eof(dev, &cur_timeval))
+		v4l2_subdev_notify(&priv->sd, IMXCAM_FRAME_INTERVAL_NOTIFY,
+				   NULL);
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+/* csi_ch EOF interrupt */
+static irqreturn_t csi_eof_interrupt(int irq, void *dev_id)
+{
+	struct vdic_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->out_buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	/* prepare next buffers */
+	prepare_csi_buffer(priv);
+	prepare_prpvf_out_buffer(priv);
+	prepare_vdi_in_buffers(priv);
+
+	/* increment double-buffer index and frame index */
+	priv->csi_buf_num ^= 1;
+	priv->csi_frame_num = (priv->csi_frame_num + 1) % 3;
+
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t nfb4eof_interrupt(int irq, void *dev_id)
+{
+	struct vdic_priv *priv = dev_id;
+
+	v4l2_err(&priv->sd, "NFB4EOF\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_NFB4EOF_NOTIFY, NULL);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function.
+ */
+static void vdic_eof_timeout(unsigned long data)
+{
+	struct vdic_priv *priv = (struct vdic_priv *)data;
+
+	v4l2_err(&priv->sd, "EOF timeout\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_EOF_TIMEOUT_NOTIFY, NULL);
+}
+
+static void vdic_free_dma_buf(struct vdic_priv *priv,
+			      struct imxcam_dma_buf *buf)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	if (buf->virt)
+		dma_free_coherent(dev->dev, buf->len, buf->virt, buf->phys);
+
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int vdic_alloc_dma_buf(struct vdic_priv *priv,
+			      struct imxcam_dma_buf *buf,
+			      int size)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	vdic_free_dma_buf(priv, buf);
+
+	buf->len = PAGE_ALIGN(size);
+	buf->virt = dma_alloc_coherent(dev->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		v4l2_err(&priv->sd, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void setup_csi_channel(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_sensor *sensor = dev->sensor;
+	struct ipuv3_channel *channel = priv->csi_ch;
+	struct v4l2_mbus_framefmt *inf = &priv->inf;
+	int csi_id = sensor->csi_ep.base.port;
+	int vc_num = sensor->csi_ep.base.id;
+	unsigned int burst_size;
+	struct ipu_image image;
+	bool passthrough;
+
+	ipu_cpmem_zero(channel);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = inf->width;
+	image.pix.height = image.rect.height = inf->height;
+	image.pix.bytesperline = priv->in_stride;
+	image.pix.pixelformat = priv->in_pixfmt;
+	image.phys0 = priv->csi_frame[0].phys;
+	image.phys1 = priv->csi_frame[1].phys;
+	ipu_cpmem_set_image(channel, &image);
+
+	burst_size = (inf->width & 0xf) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	/*
+	 * If the sensor uses 16-bit parallel CSI bus, we must handle
+	 * the data internally in the IPU as 16-bit generic, aka
+	 * passthrough mode.
+	 */
+	passthrough = (sensor->ep.bus_type != V4L2_MBUS_CSI2 &&
+		       sensor->ep.bus.parallel.bus_width >= 16);
+
+	if (passthrough)
+		ipu_cpmem_set_format_passthrough(channel, 16);
+
+	if (sensor->ep.bus_type == V4L2_MBUS_CSI2)
+		ipu_smfc_map_channel(priv->smfc, csi_id, vc_num);
+	else
+		ipu_smfc_map_channel(priv->smfc, csi_id, 0);
+
+	/*
+	 * Set the channel for the direct CSI-->memory via SMFC
+	 * use-case to very high priority, by enabling the watermark
+	 * signal in the SMFC, enabling WM in the channel, and setting
+	 * the channel priority to high.
+	 *
+	 * Refer to the i.mx6 rev. D TRM Table 36-8: Calculated priority
+	 * value.
+	 *
+	 * The WM's are set very low by intention here to ensure that
+	 * the SMFC FIFOs do not overflow.
+	 */
+	ipu_smfc_set_watermark(priv->smfc, 0x02, 0x01);
+	ipu_cpmem_set_high_priority(channel);
+	ipu_idmac_enable_watermark(channel, true);
+	ipu_cpmem_set_axi_id(channel, 0);
+	ipu_idmac_lock_enable(channel, 8);
+
+	burst_size = ipu_cpmem_get_burstsize(channel);
+	burst_size = passthrough ?
+		(burst_size >> 3) - 1 : (burst_size >> 2) - 1;
+
+	ipu_smfc_set_burstsize(priv->smfc, burst_size);
+
+	ipu_idmac_set_double_buffer(channel, true);
+}
+
+static void setup_vdi_channel(struct vdic_priv *priv,
+			      struct ipuv3_channel *channel,
+			      dma_addr_t phys0, dma_addr_t phys1,
+			      bool out_chan)
+{
+	u32 stride, width, height, pixfmt;
+	unsigned int burst_size;
+	struct ipu_image image;
+
+	if (out_chan) {
+		width = priv->outf.width;
+		height = priv->outf.height;
+		pixfmt = priv->outf.pixelformat;
+		stride = priv->out_stride;
+	} else {
+		width = priv->inf.width;
+		height = priv->inf.height / 2;
+		pixfmt = priv->in_pixfmt;
+		stride = priv->in_stride;
+	}
+
+	ipu_cpmem_zero(channel);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = width;
+	image.pix.height = image.rect.height = height;
+	image.pix.bytesperline = stride;
+	image.pix.pixelformat = pixfmt;
+	image.phys0 = phys0;
+	image.phys1 = phys1;
+	ipu_cpmem_set_image(channel, &image);
+
+	burst_size = (width & 0xf) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	if (out_chan)
+		ipu_ic_task_idma_init(priv->ic_vf, channel, width, height,
+				      burst_size, IPU_ROTATE_NONE);
+
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, out_chan);
+}
+
+static int vdic_setup_direct(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_ctx *ctx = dev->io_ctx;
+	struct imxcam_buffer *frame, *tmp;
+	dma_addr_t phys[2] = {0};
+	int i = 0;
+
+	priv->out_buf_num = 0;
+
+	list_for_each_entry_safe(frame, tmp, &ctx->ready_q, list) {
+		phys[i] = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[i++] = frame;
+		if (i >= 2)
+			break;
+	}
+
+	/* init the prpvf out channel */
+	setup_vdi_channel(priv, priv->prpvf_out_ch, phys[0], phys[1], true);
+
+	return 0;
+}
+
+static void vdic_start_direct(struct vdic_priv *priv)
+{
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->prpvf_out_ch, 0);
+	ipu_idmac_select_buffer(priv->prpvf_out_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->prpvf_out_ch);
+}
+
+static void vdic_stop_direct(struct vdic_priv *priv)
+{
+	ipu_idmac_disable_channel(priv->prpvf_out_ch);
+}
+
+static void vdic_disable_direct(struct vdic_priv *priv)
+{
+	/* nothing to do */
+}
+
+static int vdic_setup_indirect(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct vdic_field_addr *field;
+	struct imxcam_dma_buf *frame;
+	int ret, in_size, i;
+
+	/*
+	 * FIXME: following in_size calc would not be correct for planar pixel
+	 * formats, but all mbus pixel codes are packed formats, so so far this
+	 * is OK.
+	 */
+	in_size = priv->in_stride * priv->inf.height;
+
+	priv->csi_buf_num = priv->csi_frame_num = priv->out_buf_num = 0;
+	priv->field_size = in_size / 2;
+
+	/* request EOF irq for vdi out channel */
+	priv->csi_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						  priv->csi_ch,
+						  IPU_IRQ_EOF);
+	ret = devm_request_irq(dev->dev, priv->csi_eof_irq,
+			       csi_eof_interrupt, 0,
+			       "imxcam-csi-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd, "Error registering CSI eof irq: %d\n",
+			 ret);
+		return ret;
+	}
+
+	for (i = 0; i < 3; i++) {
+		frame = &priv->csi_frame[i];
+
+		ret = vdic_alloc_dma_buf(priv, frame, in_size);
+		if (ret) {
+			v4l2_err(&priv->sd,
+				 "failed to alloc csi_frame[%d], %d\n", i, ret);
+			while (--i >= 0)
+				vdic_free_dma_buf(priv, &priv->csi_frame[i]);
+			goto out_free_irq;
+		}
+
+		/* precalculate the field addresses for this frame */
+		field = &priv->field[i];
+		switch (priv->inf.field) {
+		case V4L2_FIELD_SEQ_TB:
+			field->prev = frame->phys + priv->field_size;
+			field->curr = frame->phys;
+			field->next = frame->phys + priv->field_size;
+			break;
+		case V4L2_FIELD_SEQ_BT:
+			field->prev = frame->phys;
+			field->curr = frame->phys + priv->field_size;
+			field->next = frame->phys;
+			break;
+		case V4L2_FIELD_INTERLACED_BT:
+			field->prev = frame->phys;
+			field->curr = frame->phys + priv->in_stride;
+			field->next = frame->phys;
+			break;
+		default:
+			/* assume V4L2_FIELD_INTERLACED_TB */
+			field->prev = frame->phys + priv->in_stride;
+			field->curr = frame->phys;
+			field->next = frame->phys + priv->in_stride;
+			break;
+		}
+	}
+
+	priv->active_frame[0] = priv->active_frame[1] = NULL;
+
+	/* init the CSI channel */
+	setup_csi_channel(priv);
+
+	/* init the vdi-in channels */
+	setup_vdi_channel(priv, priv->vdi_in_ch_p, 0, 0, false);
+	setup_vdi_channel(priv, priv->vdi_in_ch, 0, 0, false);
+	setup_vdi_channel(priv, priv->vdi_in_ch_n, 0, 0, false);
+
+	/* init the prpvf out channel */
+	setup_vdi_channel(priv, priv->prpvf_out_ch, 0, 0, true);
+
+	return 0;
+
+out_free_irq:
+	devm_free_irq(dev->dev, priv->csi_eof_irq, priv);
+	return ret;
+}
+
+static void vdic_start_indirect(struct vdic_priv *priv)
+{
+	int i;
+
+	/* set buffers ready */
+	for (i = 0; i < 2; i++)
+		ipu_idmac_select_buffer(priv->csi_ch, i);
+
+	/* enable SMFC */
+	ipu_smfc_enable(priv->smfc);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->csi_ch);
+	ipu_idmac_enable_channel(priv->prpvf_out_ch);
+	ipu_idmac_enable_channel(priv->vdi_in_ch_p);
+	ipu_idmac_enable_channel(priv->vdi_in_ch);
+	ipu_idmac_enable_channel(priv->vdi_in_ch_n);
+}
+
+static void vdic_stop_indirect(struct vdic_priv *priv)
+{
+	/* disable channels */
+	ipu_idmac_disable_channel(priv->prpvf_out_ch);
+	ipu_idmac_disable_channel(priv->vdi_in_ch_p);
+	ipu_idmac_disable_channel(priv->vdi_in_ch);
+	ipu_idmac_disable_channel(priv->vdi_in_ch_n);
+	ipu_idmac_disable_channel(priv->csi_ch);
+
+	/* disable SMFC */
+	ipu_smfc_disable(priv->smfc);
+}
+
+static void vdic_disable_indirect(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int i;
+
+	devm_free_irq(dev->dev, priv->csi_eof_irq, priv);
+
+	for (i = 0; i < 3; i++)
+		vdic_free_dma_buf(priv, &priv->csi_frame[i]);
+}
+
+static struct vdic_pipeline_ops direct_ops = {
+	.setup = vdic_setup_direct,
+	.start = vdic_start_direct,
+	.stop = vdic_stop_direct,
+	.disable = vdic_disable_direct,
+};
+
+static struct vdic_pipeline_ops indirect_ops = {
+	.setup = vdic_setup_indirect,
+	.start = vdic_start_indirect,
+	.stop = vdic_stop_indirect,
+	.disable = vdic_disable_indirect,
+};
+
+static int vdic_start(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct imxcam_buffer *frame;
+	int i, ret;
+
+	priv->direct = (allow_direct && dev->motion == HIGH_MOTION);
+	/* this info is needed by CSI subdev for destination routing */
+	dev->vdic_direct = priv->direct;
+
+	priv->ops = priv->direct ? &direct_ops : &indirect_ops;
+
+	ret = vdic_get_ipu_resources(priv);
+	if (ret)
+		return ret;
+
+	priv->inf = dev->sensor_fmt;
+	priv->in_pixfmt = dev->sensor_pixfmt->fourcc;
+	priv->inf.width = dev->crop.width;
+	priv->inf.height = dev->crop.height;
+	priv->in_stride = dev->sensor_pixfmt->y_depth ?
+		(priv->inf.width * dev->sensor_pixfmt->y_depth) >> 3 :
+		(priv->inf.width * dev->sensor_pixfmt->bpp) >> 3;
+	priv->in_cs = ipu_mbus_code_to_colorspace(priv->inf.code);
+
+	priv->outf = dev->user_fmt.fmt.pix;
+	priv->out_cs = ipu_pixelformat_to_colorspace(priv->outf.pixelformat);
+	priv->out_stride = dev->user_pixfmt->y_depth ?
+		(priv->outf.width * dev->user_pixfmt->y_depth) >> 3 :
+		(priv->outf.width * dev->user_pixfmt->bpp) >> 3;
+
+	/* set IC to receive from VDIC */
+	ipu_ic_set_src(priv->ic_vf, csi_id, true);
+
+	/*
+	 * set VDIC to receive from CSI for direct path, and memory
+	 * for indirect.
+	 */
+	ipu_vdi_set_src(priv->vdi, priv->direct);
+
+	ret = vdic_alloc_dma_buf(priv, &priv->underrun_buf,
+				 priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		goto out_put_ipu;
+	}
+
+	/* init EOF completion waitq */
+	init_completion(&priv->last_eof_comp);
+	priv->last_eof = false;
+
+	/* request EOF irq for prpvf out channel */
+	priv->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						  priv->prpvf_out_ch,
+						  IPU_IRQ_EOF);
+	ret = devm_request_irq(dev->dev, priv->out_eof_irq,
+			       prpvf_out_eof_interrupt, 0,
+			       "imxcam-prpvf-out-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering prpvf out eof irq: %d\n", ret);
+		goto out_free_underrun;
+	}
+
+	/* request NFB4EOF irq */
+	priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu, priv->direct ?
+						  priv->prpvf_out_ch :
+						  priv->csi_ch,
+						  IPU_IRQ_NFB4EOF);
+	ret = devm_request_irq(dev->dev, priv->nfb4eof_irq,
+			       nfb4eof_interrupt, 0,
+			       "imxcam-vdic-nfb4eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering NFB4EOF irq: %d\n", ret);
+		goto out_free_eof_irq;
+	}
+
+	/* init the VDIC */
+	ipu_vdi_setup(priv->vdi, priv->inf.code,
+		      priv->inf.width, priv->inf.height, priv->inf.field,
+		      dev->motion);
+
+	ret = ipu_ic_task_init(priv->ic_vf,
+			       priv->inf.width, priv->inf.height,
+			       priv->outf.width, priv->outf.height,
+			       priv->in_cs, priv->out_cs);
+	if (ret) {
+		v4l2_err(&priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+		goto out_free_nfb4eof_irq;
+	}
+
+	ret = priv->ops->setup(priv);
+	if (ret)
+		goto out_free_nfb4eof_irq;
+
+	ipu_vdi_enable(priv->vdi);
+	ipu_ic_enable(priv->ic_vf);
+
+	priv->ops->start(priv);
+
+	/* enable the IC VF task */
+	ipu_ic_task_enable(priv->ic_vf);
+
+	/* sensor stream on */
+	ret = dev->sensor_set_stream(dev, 1);
+	if (ret) {
+		v4l2_err(&priv->sd, "sensor stream on failed\n");
+		goto out_stop;
+	}
+
+	/* start the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	return 0;
+
+out_stop:
+	ipu_ic_task_disable(priv->ic_vf);
+	priv->ops->stop(priv);
+	ipu_ic_disable(priv->ic_vf);
+	ipu_vdi_disable(priv->vdi);
+	priv->ops->disable(priv);
+out_free_nfb4eof_irq:
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+out_free_eof_irq:
+	devm_free_irq(dev->dev, priv->out_eof_irq, priv);
+out_free_underrun:
+	vdic_free_dma_buf(priv, &priv->underrun_buf);
+out_put_ipu:
+	vdic_put_ipu_resources(priv);
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame)
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_QUEUED);
+	}
+	return ret;
+}
+
+static int vdic_stop(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+	int i, ret;
+
+	/* mark next EOF interrupt as the last before vdic off */
+	spin_lock_irqsave(&dev->irqlock, flags);
+	priv->last_eof = true;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/*
+	 * and then wait for interrupt handler to mark completion.
+	 */
+	ret = wait_for_completion_timeout(&priv->last_eof_comp,
+					  msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+	if (ret == 0)
+		v4l2_warn(&priv->sd, "wait last encode EOF timeout\n");
+
+	/* sensor stream off */
+	ret = dev->sensor_set_stream(dev, 0);
+	if (ret)
+		v4l2_warn(&priv->sd, "sensor stream off failed\n");
+
+	ipu_ic_task_disable(priv->ic_vf);
+	priv->ops->stop(priv);
+	ipu_ic_disable(priv->ic_vf);
+	ipu_vdi_disable(priv->vdi);
+	priv->ops->disable(priv);
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+	devm_free_irq(dev->dev, priv->out_eof_irq, priv);
+	vdic_free_dma_buf(priv, &priv->underrun_buf);
+	vdic_put_ipu_resources(priv);
+
+	/* cancel the EOF timeout timer */
+	del_timer_sync(&priv->eof_timeout_timer);
+
+	/* return any remaining active frames with error */
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame && frame->vb.state == VB2_BUF_STATE_ACTIVE) {
+			frame->vb.timestamp = ktime_get_ns();
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+		}
+	}
+
+	return 0;
+}
+
+static int vdic_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+	return enable ? vdic_start(priv) : vdic_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops vdic_video_ops = {
+	.s_stream = vdic_s_stream,
+};
+
+static struct v4l2_subdev_ops vdic_subdev_ops = {
+	.video = &vdic_video_ops,
+};
+
+struct v4l2_subdev *imxcam_vdic_init(struct imxcam_dev *dev)
+{
+	struct vdic_priv *priv;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	init_timer(&priv->eof_timeout_timer);
+	priv->eof_timeout_timer.data = (unsigned long)priv;
+	priv->eof_timeout_timer.function = vdic_eof_timeout;
+
+	v4l2_subdev_init(&priv->sd, &vdic_subdev_ops);
+	strlcpy(priv->sd.name, "imx-camera-vdic", sizeof(priv->sd.name));
+	v4l2_set_subdevdata(&priv->sd, priv);
+
+	priv->dev = dev;
+	return &priv->sd;
+}
diff --git a/include/media/imx.h b/include/media/imx.h
new file mode 100644
index 0000000..5025a72
--- /dev/null
+++ b/include/media/imx.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2014-2015 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef __MEDIA_IMX_H__
+#define __MEDIA_IMX_H__
+
+#include <uapi/media/imx.h>
+
+#endif
diff --git a/include/uapi/Kbuild b/include/uapi/Kbuild
index 245aa6e..9a51957 100644
--- a/include/uapi/Kbuild
+++ b/include/uapi/Kbuild
@@ -6,6 +6,7 @@
 header-y += asm-generic/
 header-y += linux/
 header-y += sound/
+header-y += media/
 header-y += mtd/
 header-y += rdma/
 header-y += video/
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b6a357a..9343950 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -180,6 +180,10 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_TC358743_BASE		(V4L2_CID_USER_BASE + 0x1080)
 
+/* The base for the imx driver controls.
+ * We reserve 16 controls for this driver. */
+#define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x1090)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild
new file mode 100644
index 0000000..fa78958
--- /dev/null
+++ b/include/uapi/media/Kbuild
@@ -0,0 +1,2 @@
+# UAPI Header export list
+header-y += imx.h
diff --git a/include/uapi/media/imx.h b/include/uapi/media/imx.h
new file mode 100644
index 0000000..de1447c
--- /dev/null
+++ b/include/uapi/media/imx.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014-2015 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef __UAPI_MEDIA_IMX_H__
+#define __UAPI_MEDIA_IMX_H__
+
+enum imx_ctrl_id {
+	V4L2_CID_IMX_MOTION = (V4L2_CID_USER_IMX_BASE + 0),
+	V4L2_CID_IMX_FIM_ENABLE,
+	V4L2_CID_IMX_FIM_NUM,
+	V4L2_CID_IMX_FIM_TOLERANCE_MIN,
+	V4L2_CID_IMX_FIM_TOLERANCE_MAX,
+	V4L2_CID_IMX_FIM_NUM_SKIP,
+};
+
+#endif
-- 
1.9.1


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

* [PATCH 30/38] media: imx: Add MIPI CSI-2 Receiver driver
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (28 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 29/38] media: Add camera interface driver for i.MX5/6 Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 31/38] media: imx: Add video switch Steve Longerbeam
                   ` (10 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds MIPI CSI-2 Receiver subdev driver. This subdev is required
for sensors with a MIPI CSI2 interface.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig     |   8 +
 drivers/staging/media/imx/capture/Makefile    |   1 +
 drivers/staging/media/imx/capture/mipi-csi2.c | 373 ++++++++++++++++++++++++++
 3 files changed, 382 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/mipi-csi2.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index ee2cbab..ac6fce0 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -1,3 +1,11 @@
 menu "i.MX5/6 Camera Sub devices"
 
+config IMX_MIPI_CSI2
+       tristate "MIPI CSI2 Receiver Driver"
+       depends on VIDEO_IMX_CAMERA
+       default y
+       ---help---
+         MIPI CSI-2 Receiver driver support. This driver is required
+	 for sensor drivers with a MIPI CSI2 interface.
+
 endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index 5c965f9..8961a4f 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -3,3 +3,4 @@ imx-camera-objs := imx-camif.o imx-ic-prpenc.o imx-of.o \
 
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
+obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
diff --git a/drivers/staging/media/imx/capture/mipi-csi2.c b/drivers/staging/media/imx/capture/mipi-csi2.c
new file mode 100644
index 0000000..d0a8c10
--- /dev/null
+++ b/drivers/staging/media/imx/capture/mipi-csi2.c
@@ -0,0 +1,373 @@
+/*
+ * MIPI CSI-2 Receiver Subdev for Freescale i.MX5/6 SOC.
+ *
+ * Copyright (c) 2012-2014 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+#include <asm/mach/irq.h>
+#include <video/imx-ipu-v3.h>
+
+struct imxcsi2_dev {
+	struct device          *dev;
+	struct v4l2_subdev      sd;
+	struct clk             *dphy_clk;
+	struct clk             *cfg_clk;
+	struct clk             *pix_clk; /* what is this? */
+	void __iomem           *base;
+	int                     intr1;
+	int                     intr2;
+	struct v4l2_of_bus_mipi_csi2 bus;
+	spinlock_t              lock;
+	bool                    on;
+};
+
+#define DEVICE_NAME "imx-mipi-csi2"
+
+/* Register offsets */
+#define CSI2_VERSION            0x000
+#define CSI2_N_LANES            0x004
+#define CSI2_PHY_SHUTDOWNZ      0x008
+#define CSI2_DPHY_RSTZ          0x00c
+#define CSI2_RESETN             0x010
+#define CSI2_PHY_STATE          0x014
+#define CSI2_DATA_IDS_1         0x018
+#define CSI2_DATA_IDS_2         0x01c
+#define CSI2_ERR1               0x020
+#define CSI2_ERR2               0x024
+#define CSI2_MSK1               0x028
+#define CSI2_MSK2               0x02c
+#define CSI2_PHY_TST_CTRL0      0x030
+#define CSI2_PHY_TST_CTRL1      0x034
+#define CSI2_SFT_RESET          0xf00
+
+static inline struct imxcsi2_dev *sd_to_dev(struct v4l2_subdev *sdev)
+{
+	return container_of(sdev, struct imxcsi2_dev, sd);
+}
+
+static inline u32 imxcsi2_read(struct imxcsi2_dev *csi2, unsigned int regoff)
+{
+	return readl(csi2->base + regoff);
+}
+
+static inline void imxcsi2_write(struct imxcsi2_dev *csi2, u32 val,
+				 unsigned int regoff)
+{
+	writel(val, csi2->base + regoff);
+}
+
+static void imxcsi2_set_lanes(struct imxcsi2_dev *csi2)
+{
+	int lanes = csi2->bus.num_data_lanes;
+	unsigned long flags;
+
+	spin_lock_irqsave(&csi2->lock, flags);
+
+	imxcsi2_write(csi2, lanes - 1, CSI2_N_LANES);
+
+	spin_unlock_irqrestore(&csi2->lock, flags);
+}
+
+static void __imxcsi2_enable(struct imxcsi2_dev *csi2, bool enable)
+{
+	if (enable) {
+		imxcsi2_write(csi2, 0xffffffff, CSI2_PHY_SHUTDOWNZ);
+		imxcsi2_write(csi2, 0xffffffff, CSI2_DPHY_RSTZ);
+		imxcsi2_write(csi2, 0xffffffff, CSI2_RESETN);
+	} else {
+		imxcsi2_write(csi2, 0x0, CSI2_PHY_SHUTDOWNZ);
+		imxcsi2_write(csi2, 0x0, CSI2_DPHY_RSTZ);
+		imxcsi2_write(csi2, 0x0, CSI2_RESETN);
+	}
+}
+
+static void imxcsi2_enable(struct imxcsi2_dev *csi2, bool enable)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&csi2->lock, flags);
+	__imxcsi2_enable(csi2, enable);
+	spin_unlock_irqrestore(&csi2->lock, flags);
+}
+
+static void imxcsi2_reset(struct imxcsi2_dev *csi2)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&csi2->lock, flags);
+
+	__imxcsi2_enable(csi2, false);
+
+	imxcsi2_write(csi2, 0x00000001, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL1);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000002, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00010044, CSI2_PHY_TST_CTRL1);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000014, CSI2_PHY_TST_CTRL1);
+	imxcsi2_write(csi2, 0x00000002, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL0);
+
+	__imxcsi2_enable(csi2, true);
+
+	spin_unlock_irqrestore(&csi2->lock, flags);
+}
+
+static int imxcsi2_dphy_wait(struct imxcsi2_dev *csi2)
+{
+	u32 reg;
+	int i;
+
+	/* wait for mipi sensor ready */
+	for (i = 0; i < 50; i++) {
+		reg = imxcsi2_read(csi2, CSI2_PHY_STATE);
+		if (reg != 0x200)
+			break;
+		usleep_range(10000, 10001);
+	}
+
+	if (i >= 50) {
+		v4l2_err(&csi2->sd,
+			 "wait for clock lane timeout, phy_state = 0x%08x\n",
+			 reg);
+		return -ETIME;
+	}
+
+	/* wait for mipi stable */
+	for (i = 0; i < 50; i++) {
+		reg = imxcsi2_read(csi2, CSI2_ERR1);
+		if (reg == 0x0)
+			break;
+		usleep_range(10000, 10001);
+	}
+
+	if (i >= 50) {
+		v4l2_err(&csi2->sd,
+			 "wait for controller timeout, err1 = 0x%08x\n",
+			 reg);
+		return -ETIME;
+	}
+
+	/* finally let's wait for active clock on the clock lane */
+	for (i = 0; i < 50; i++) {
+		reg = imxcsi2_read(csi2, CSI2_PHY_STATE);
+		if (reg & (1 << 8))
+			break;
+		usleep_range(10000, 10001);
+	}
+
+	if (i >= 50) {
+		v4l2_err(&csi2->sd,
+			 "wait for active clock timeout, phy_state = 0x%08x\n",
+			 reg);
+		return -ETIME;
+	}
+
+	v4l2_info(&csi2->sd, "ready, dphy version 0x%x\n",
+		  imxcsi2_read(csi2, CSI2_VERSION));
+
+	return 0;
+}
+
+/*
+ * V4L2 subdev operations
+ */
+static int imxcsi2_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct imxcsi2_dev *csi2 = sd_to_dev(sd);
+
+	if (on && !csi2->on) {
+		v4l2_info(&csi2->sd, "power on\n");
+		clk_prepare_enable(csi2->cfg_clk);
+		clk_prepare_enable(csi2->dphy_clk);
+		imxcsi2_set_lanes(csi2);
+		imxcsi2_reset(csi2);
+	} else if (!on && csi2->on) {
+		v4l2_info(&csi2->sd, "power off\n");
+		imxcsi2_enable(csi2, false);
+		clk_disable_unprepare(csi2->dphy_clk);
+		clk_disable_unprepare(csi2->cfg_clk);
+	}
+
+	csi2->on = on;
+	return 0;
+}
+
+static int imxcsi2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imxcsi2_dev *csi2 = sd_to_dev(sd);
+	int ret = 0;
+
+	if (enable) {
+		clk_prepare_enable(csi2->pix_clk);
+		ret = imxcsi2_dphy_wait(csi2);
+	} else {
+		clk_disable_unprepare(csi2->pix_clk);
+	}
+
+	return ret;
+}
+
+static struct v4l2_subdev_core_ops imxcsi2_core_ops = {
+	.s_power = imxcsi2_s_power,
+};
+
+static struct v4l2_subdev_video_ops imxcsi2_video_ops = {
+	.s_stream = imxcsi2_s_stream,
+};
+
+static struct v4l2_subdev_ops imxcsi2_subdev_ops = {
+	.core = &imxcsi2_core_ops,
+	.video = &imxcsi2_video_ops,
+};
+
+static int imxcsi2_parse_endpoints(struct imxcsi2_dev *csi2)
+{
+	struct device_node *node = csi2->dev->of_node;
+	struct device_node *epnode;
+	struct v4l2_of_endpoint ep;
+	int ret = 0;
+
+	epnode = of_graph_get_next_endpoint(node, NULL);
+	if (!epnode) {
+		v4l2_err(&csi2->sd, "failed to get endpoint node\n");
+		return -EINVAL;
+	}
+
+	v4l2_of_parse_endpoint(epnode, &ep);
+	if (ep.bus_type != V4L2_MBUS_CSI2) {
+		v4l2_err(&csi2->sd, "invalid bus type, must be MIPI CSI2\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	csi2->bus = ep.bus.mipi_csi2;
+
+	v4l2_info(&csi2->sd, "data lanes: %d\n", csi2->bus.num_data_lanes);
+	v4l2_info(&csi2->sd, "flags: 0x%08x\n", csi2->bus.flags);
+out:
+	of_node_put(epnode);
+	return ret;
+}
+
+static int imxcsi2_probe(struct platform_device *pdev)
+{
+	struct imxcsi2_dev *csi2;
+	struct resource *res;
+	int ret;
+
+	csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
+	if (!csi2)
+		return -ENOMEM;
+
+	csi2->dev = &pdev->dev;
+	spin_lock_init(&csi2->lock);
+
+	v4l2_subdev_init(&csi2->sd, &imxcsi2_subdev_ops);
+	v4l2_set_subdevdata(&csi2->sd, &pdev->dev);
+	csi2->sd.dev = &pdev->dev;
+	csi2->sd.owner = THIS_MODULE;
+	strcpy(csi2->sd.name, DEVICE_NAME);
+
+	ret = imxcsi2_parse_endpoints(csi2);
+	if (ret)
+		return ret;
+
+	csi2->cfg_clk = devm_clk_get(&pdev->dev, "cfg_clk");
+	if (IS_ERR(csi2->cfg_clk)) {
+		v4l2_err(&csi2->sd, "failed to get cfg clock\n");
+		ret = PTR_ERR(csi2->cfg_clk);
+		return ret;
+	}
+
+	csi2->dphy_clk = devm_clk_get(&pdev->dev, "dphy_clk");
+	if (IS_ERR(csi2->dphy_clk)) {
+		v4l2_err(&csi2->sd, "failed to get dphy clock\n");
+		ret = PTR_ERR(csi2->dphy_clk);
+		return ret;
+	}
+
+	csi2->pix_clk = devm_clk_get(&pdev->dev, "pix_clk");
+	if (IS_ERR(csi2->pix_clk)) {
+		v4l2_err(&csi2->sd, "failed to get pixel clock\n");
+		ret = PTR_ERR(csi2->pix_clk);
+		return ret;
+	}
+
+	csi2->intr1 = platform_get_irq(pdev, 0);
+	csi2->intr2 = platform_get_irq(pdev, 1);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!res || csi2->intr1 < 0 || csi2->intr2 < 0) {
+		v4l2_err(&csi2->sd, "failed to get platform resources\n");
+		return -ENODEV;
+	}
+
+	csi2->base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
+	if (!csi2->base) {
+		v4l2_err(&csi2->sd, "failed to map CSI-2 registers\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, &csi2->sd);
+
+	return v4l2_async_register_subdev(&csi2->sd);
+}
+
+static int imxcsi2_remove(struct platform_device *pdev)
+{
+	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+	struct imxcsi2_dev *csi2 = sd_to_dev(sd);
+
+	imxcsi2_s_power(sd, 0);
+
+	v4l2_async_unregister_subdev(&csi2->sd);
+	v4l2_device_unregister_subdev(sd);
+
+	return 0;
+}
+
+static const struct of_device_id imxcsi2_dt_ids[] = {
+	{ .compatible = "fsl,imx-mipi-csi2", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imxcsi2_dt_ids);
+
+static struct platform_driver imxcsi2_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = imxcsi2_dt_ids,
+	},
+	.probe = imxcsi2_probe,
+	.remove = imxcsi2_remove,
+};
+
+module_platform_driver(imxcsi2_driver);
+
+MODULE_DESCRIPTION("i.MX5/6 MIPI CSI-2 Receiver driver");
+MODULE_AUTHOR("Mentor Graphics Inc.");
+MODULE_LICENSE("GPL");
+
-- 
1.9.1


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

* [PATCH 31/38] media: imx: Add video switch
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (29 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 30/38] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-16 16:13   ` Ian Arkver
  2016-06-14 22:49 ` [PATCH 32/38] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
                   ` (9 subsequent siblings)
  40 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Philipp Zabel, Sascha Hauer, Steve Longerbeam

From: Philipp Zabel <p.zabel@pengutronix.de>

This driver can handle SoC internal and extern video bus multiplexers,
controlled either by register bit fields or by GPIO.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig          |   9 +
 drivers/staging/media/imx/capture/Makefile         |   1 +
 .../staging/media/imx/capture/imx-video-switch.c   | 348 +++++++++++++++++++++
 3 files changed, 358 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index ac6fce0..ecd09abe 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -8,4 +8,13 @@ config IMX_MIPI_CSI2
          MIPI CSI-2 Receiver driver support. This driver is required
 	 for sensor drivers with a MIPI CSI2 interface.
 
+config IMX_VIDEO_SWITCH
+	tristate "i.MX5/6 Video Bus Multiplexer"
+	depends on VIDEO_IMX_CAMERA
+	default y
+	---help---
+	  This driver provides support for the i.MX5/6 internal video bus
+	  multiplexer controlled by register bitfields as well as
+	  external multiplexers controller by a GPIO.
+
 endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index 8961a4f..f17b199 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -4,3 +4,4 @@ imx-camera-objs := imx-camif.o imx-ic-prpenc.o imx-of.o \
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
 obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
+obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
diff --git a/drivers/staging/media/imx/capture/imx-video-switch.c b/drivers/staging/media/imx/capture/imx-video-switch.c
new file mode 100644
index 0000000..0c86679
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-video-switch.c
@@ -0,0 +1,348 @@
+/*
+ * devicetree probed mediacontrol video multiplexer.
+ *
+ * Copyright (C) 2013 Sascha Hauer, Pengutronix
+ * Copyright (c) 2014-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/of_graph.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+
+struct vidsw {
+	struct device *dev;
+	struct v4l2_subdev subdev;
+#ifdef CONFIG_MEDIA_CONTROLLER
+	struct media_pad *pads;
+#endif
+	struct v4l2_mbus_framefmt *format_mbus;
+	struct v4l2_of_endpoint *endpoint;
+	struct regmap_field *field;
+	unsigned int gpio;
+	int output_pad;
+	int numpads;
+	int active;
+};
+
+#define to_vidsw(sd) container_of(sd, struct vidsw, subdev)
+
+static int vidsw_set_mux(struct vidsw *vidsw, int input_index)
+{
+	if (vidsw->active >= 0) {
+		if (vidsw->active == input_index)
+			return 0;
+		else
+			return -EBUSY;
+	}
+
+	vidsw->active = input_index;
+
+	dev_dbg(vidsw->dev, "setting %d active\n", vidsw->active);
+
+	if (vidsw->field)
+		regmap_field_write(vidsw->field, vidsw->active);
+	else if (gpio_is_valid(vidsw->gpio))
+		gpio_set_value(vidsw->gpio, vidsw->active);
+
+	return 0;
+}
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+static int vidsw_link_setup(struct media_entity *entity,
+		const struct media_pad *local,
+		const struct media_pad *remote, u32 flags)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+	struct vidsw *vidsw = to_vidsw(sd);
+
+	dev_dbg(vidsw->dev, "link setup %s -> %s", remote->entity->name,
+		local->entity->name);
+
+	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+		if (local->index == vidsw->active) {
+			dev_dbg(vidsw->dev, "going inactive\n");
+			vidsw->active = -1;
+		}
+		return 0;
+	}
+
+	return vidsw_set_mux(vidsw, local->index);
+}
+
+static struct media_entity_operations vidsw_ops = {
+	.link_setup = vidsw_link_setup,
+};
+#endif
+
+static int vidsw_s_routing(struct v4l2_subdev *sd, u32 input,
+			   u32 output, u32 config)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	return vidsw_set_mux(vidsw, input);
+}
+
+static int vidsw_async_init(struct vidsw *vidsw, struct device_node *node)
+{
+	struct v4l2_of_endpoint endpoint;
+	struct device_node *epnode;
+	int pad, numpads;
+#ifdef CONFIG_MEDIA_CONTROLLER
+	int ret;
+#endif
+
+	numpads = of_get_child_count(node);
+	if (numpads < 2) {
+		dev_err(vidsw->dev, "Not enough ports %d\n", numpads);
+		return -EINVAL;
+	}
+
+	vidsw->numpads = numpads;
+
+	/*
+	 * the last endpoint must define the mux output pad,
+	 * the rest are the mux input pads.
+	 */
+	vidsw->output_pad = numpads - 1;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+	vidsw->pads = devm_kzalloc(vidsw->dev,
+				   numpads * sizeof(*vidsw->pads),
+				   GFP_KERNEL);
+	if (!vidsw->pads)
+		return -ENOMEM;
+#endif
+
+	vidsw->endpoint = devm_kzalloc(vidsw->dev,
+				       numpads * sizeof(*vidsw->endpoint),
+				       GFP_KERNEL);
+	if (!vidsw->endpoint)
+		return -ENOMEM;
+
+	vidsw->format_mbus = devm_kzalloc(vidsw->dev,
+					  numpads * sizeof(*vidsw->format_mbus),
+					  GFP_KERNEL);
+	if (!vidsw->format_mbus)
+		return -ENOMEM;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+	vidsw->subdev.entity.ops = &vidsw_ops;
+
+	/* init the pad directions */
+	for (pad = 0; pad < vidsw->output_pad; pad++)
+		vidsw->pads[pad].flags = MEDIA_PAD_FL_SINK;
+	vidsw->pads[vidsw->output_pad].flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&vidsw->subdev.entity,
+				     vidsw->numpads, vidsw->pads);
+	if (ret < 0)
+		return ret;
+#endif
+
+	epnode = NULL;
+	for (pad = 0; pad < vidsw->numpads; pad++) {
+		epnode = of_graph_get_next_endpoint(node, epnode);
+		if (!epnode)
+			return -EINVAL;
+
+		v4l2_of_parse_endpoint(epnode, &endpoint);
+		vidsw->endpoint[pad] = endpoint;
+		of_node_put(epnode);
+	}
+
+	return 0;
+}
+
+static int vidsw_registered(struct v4l2_subdev *sd)
+{
+	return 0;
+}
+
+int vidsw_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	dev_dbg(vidsw->dev, "reporting configration %d\n", vidsw->active);
+
+	/* Mirror the input side on the output side */
+	cfg->type = vidsw->endpoint[vidsw->active].bus_type;
+	if (cfg->type == V4L2_MBUS_PARALLEL || cfg->type == V4L2_MBUS_BT656)
+		cfg->flags = vidsw->endpoint[vidsw->active].bus.parallel.flags;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops vidsw_subdev_video_ops = {
+	.g_mbus_config = vidsw_g_mbus_config,
+	.s_routing = vidsw_s_routing,
+};
+
+static int vidsw_get_format(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *sdformat)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	sdformat->format = vidsw->format_mbus[sdformat->pad];
+
+	return 0;
+}
+
+static int vidsw_set_format(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *sdformat)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	if (sdformat->pad >= vidsw->numpads)
+		return -EINVAL;
+
+	/* Output pad mirrors active input pad, no limitations on input pads */
+	if (sdformat->pad == vidsw->output_pad && vidsw->active >= 0)
+		sdformat->format = vidsw->format_mbus[vidsw->active];
+
+	if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = sdformat->format;
+	else
+		vidsw->format_mbus[sdformat->pad] = sdformat->format;
+
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops vidsw_pad_ops = {
+	.get_fmt = vidsw_get_format,
+	.set_fmt = vidsw_set_format,
+};
+
+static struct v4l2_subdev_ops vidsw_subdev_ops = {
+	.pad = &vidsw_pad_ops,
+	.video = &vidsw_subdev_video_ops,
+};
+
+static struct v4l2_subdev_internal_ops vidsw_internal_ops = {
+	.registered = vidsw_registered,
+};
+
+static int of_get_reg_field(struct device_node *node, struct reg_field *field)
+{
+	u32 reg_bit_mask[2];
+	int ret;
+
+	ret = of_property_read_u32_array(node, "reg", reg_bit_mask, 2);
+	if (ret < 0)
+		return ret;
+
+	field->reg = reg_bit_mask[0];
+	field->lsb = __ffs(reg_bit_mask[1]);
+	field->msb = __fls(reg_bit_mask[1]);
+
+	return 0;
+}
+
+static int vidsw_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct reg_field field;
+	struct vidsw *vidsw;
+	struct regmap *map;
+	int ret;
+
+	vidsw = devm_kzalloc(&pdev->dev, sizeof(*vidsw), GFP_KERNEL);
+	if (!vidsw)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, vidsw);
+
+	v4l2_subdev_init(&vidsw->subdev, &vidsw_subdev_ops);
+	v4l2_set_subdevdata(&vidsw->subdev, &pdev->dev);
+	vidsw->subdev.internal_ops = &vidsw_internal_ops;
+	snprintf(vidsw->subdev.name, sizeof(vidsw->subdev.name), "%s",
+			np->name);
+	vidsw->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	vidsw->subdev.dev = &pdev->dev;
+	vidsw->dev = &pdev->dev;
+	vidsw->active = -1;
+
+	ret = of_get_reg_field(np, &field);
+	if (ret == 0) {
+		map = syscon_regmap_lookup_by_phandle(np, "gpr");
+		if (!map) {
+			dev_err(&pdev->dev,
+				"Failed to get syscon register map\n");
+			return PTR_ERR(map);
+		}
+
+		vidsw->field = devm_regmap_field_alloc(&pdev->dev, map, field);
+		if (IS_ERR(vidsw->field)) {
+			dev_err(&pdev->dev,
+				"Failed to allocate regmap field\n");
+			return PTR_ERR(vidsw->field);
+		}
+	} else {
+		vidsw->gpio = of_get_named_gpio_flags(np, "gpios", 0, NULL);
+		ret = gpio_request_one(vidsw->gpio,
+				       GPIOF_OUT_INIT_LOW, np->name);
+		if (ret < 0) {
+			dev_warn(&pdev->dev,
+				 "could not request control gpio %d: %d\n",
+				 vidsw->gpio, ret);
+			vidsw->gpio = -1;
+		}
+	}
+
+	ret = vidsw_async_init(vidsw, np);
+	if (ret)
+		return ret;
+
+	ret = v4l2_async_register_subdev(&vidsw->subdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int vidsw_remove(struct platform_device *pdev)
+{
+	/* FIXME */
+
+	return -EBUSY;
+}
+
+static const struct of_device_id vidsw_dt_ids[] = {
+	{ .compatible = "imx-video-mux", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vidsw_dt_ids);
+
+static struct platform_driver vidsw_driver = {
+	.probe		= vidsw_probe,
+	.remove		= vidsw_remove,
+	.driver		= {
+		.of_match_table = vidsw_dt_ids,
+		.name	= "imx-video-mux",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(vidsw_driver);
+
+MODULE_DESCRIPTION("i.MX video stream multiplexer");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
-- 
1.9.1


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

* [PATCH 32/38] media: imx: Add support for MIPI CSI-2 OV5640
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (30 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 31/38] media: imx: Add video switch Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 33/38] media: imx: Add support for Parallel OV5642 Steve Longerbeam
                   ` (8 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This driver is based on ov5640_mipi.c from Freescale imx_3.10.17_1.0.0_beta
branch, modified heavily for code cleanup and converted from int-device
to subdev.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig       |    8 +
 drivers/staging/media/imx/capture/Makefile      |    1 +
 drivers/staging/media/imx/capture/ov5640-mipi.c | 2318 +++++++++++++++++++++++
 3 files changed, 2327 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/ov5640-mipi.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index ecd09abe..d4389d1 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -17,4 +17,12 @@ config IMX_VIDEO_SWITCH
 	  multiplexer controlled by register bitfields as well as
 	  external multiplexers controller by a GPIO.
 
+config IMX_CAMERA_OV5640_MIPI
+       tristate "OmniVision OV5640 MIPI CSI-2 camera support"
+       depends on VIDEO_IMX_CAMERA
+       select IMX_MIPI_CSI2
+       default y
+       ---help---
+         MIPI CSI-2 OV5640 Camera support.
+
 endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index f17b199..1ad4fd2 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
 obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
 obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
+obj-$(CONFIG_IMX_CAMERA_OV5640_MIPI) += ov5640-mipi.o
diff --git a/drivers/staging/media/imx/capture/ov5640-mipi.c b/drivers/staging/media/imx/capture/ov5640-mipi.c
new file mode 100644
index 0000000..3c02b3b
--- /dev/null
+++ b/drivers/staging/media/imx/capture/ov5640-mipi.c
@@ -0,0 +1,2318 @@
+/*
+ * Copyright (c) 2014 Mentor Graphics Inc.
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV5640_VOLTAGE_ANALOG               2800000
+#define OV5640_VOLTAGE_DIGITAL_CORE         1500000
+#define OV5640_VOLTAGE_DIGITAL_IO           1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+/* min/typical/max system clock (xclk) frequencies */
+#define OV5640_XCLK_MIN  6000000
+#define OV5640_XCLK_TYP 24000000
+#define OV5640_XCLK_MAX 54000000
+
+/* min/typical/max pixel clock (mclk) frequencies */
+#define OV5640_MCLK_MIN 48000000
+#define OV5640_MCLK_TYP 48000000
+#define OV5640_MCLK_MAX 96000000
+
+#define OV5640_CHIP_ID  0x300A
+
+#define OV5640_MAX_CONTROLS 64
+
+enum ov5640_mode {
+	ov5640_mode_MIN = 0,
+	ov5640_mode_QCIF_176_144 = 0,
+	ov5640_mode_QVGA_320_240,
+	ov5640_mode_VGA_640_480,
+	ov5640_mode_NTSC_720_480,
+	ov5640_mode_PAL_720_576,
+	ov5640_mode_XGA_1024_768,
+	ov5640_mode_720P_1280_720,
+	ov5640_mode_1080P_1920_1080,
+	ov5640_mode_QSXGA_2592_1944,
+	ov5640_num_modes,
+	ov5640_mode_INIT = 0xff, /*only for sensor init*/
+};
+
+enum ov5640_frame_rate {
+	ov5640_15_fps,
+	ov5640_30_fps
+};
+
+static int ov5640_framerates[] = {
+	[ov5640_15_fps] = 15,
+	[ov5640_30_fps] = 30,
+};
+#define ov5640_num_framerates ARRAY_SIZE(ov5640_framerates)
+
+/* image size under 1280 * 960 are SUBSAMPLING
+ * image size upper 1280 * 960 are SCALING
+ */
+enum ov5640_downsize_mode {
+	SUBSAMPLING,
+	SCALING,
+};
+
+struct reg_value {
+	u16 reg_addr;
+	u8 val;
+	u8 mask;
+	u32 delay_ms;
+};
+
+struct ov5640_mode_info {
+	enum ov5640_mode mode;
+	enum ov5640_downsize_mode dn_mode;
+	u32 width;
+	u32 height;
+	struct reg_value *init_data_ptr;
+	u32 init_data_size;
+};
+
+struct ov5640_dev {
+	struct i2c_client *i2c_client;
+	struct device *dev;
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_of_endpoint ep; /* the parsed DT endpoint info */
+	struct v4l2_mbus_framefmt fmt;
+	struct v4l2_captureparm streamcap;
+	struct clk *xclk; /* system clock to OV5640 */
+	int xclk_freq;    /* requested xclk freq from devicetree */
+
+	enum ov5640_mode current_mode;
+	enum ov5640_frame_rate current_fr;
+
+	bool on;
+	bool awb_on;
+	bool agc_on;
+
+	/* cached control settings */
+	int ctrl_cache[OV5640_MAX_CONTROLS];
+
+	int reset_gpio;
+	int pwdn_gpio;
+	int gp_gpio;
+
+	int prev_sysclk, prev_hts;
+	int ae_low, ae_high, ae_target;
+
+	struct regulator *io_regulator;
+	struct regulator *core_regulator;
+	struct regulator *analog_regulator;
+	struct regulator *gpo_regulator;
+};
+
+static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5640_dev, sd);
+}
+
+static inline struct ov5640_dev *ctrl_to_ov5640_dev(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct ov5640_dev, ctrl_hdl);
+}
+
+struct ov5640_control {
+	struct v4l2_queryctrl ctrl;
+	int (*set)(struct ov5640_dev *sensor, int value);
+};
+
+static void ov5640_power(struct ov5640_dev *sensor, bool enable);
+static void ov5640_reset(struct ov5640_dev *sensor);
+static int ov5640_restore_ctrls(struct ov5640_dev *sensor);
+static int ov5640_set_agc(struct ov5640_dev *sensor, int value);
+static int ov5640_set_exposure(struct ov5640_dev *sensor, int value);
+static int ov5640_get_exposure(struct ov5640_dev *sensor);
+static int ov5640_set_gain(struct ov5640_dev *sensor, int value);
+static int ov5640_get_gain(struct ov5640_dev *sensor);
+
+static struct reg_value ov5640_init_setting_30fps_VGA[] = {
+
+	{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
+	{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
+	{0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
+	{0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
+	{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
+	{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
+	{0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
+	{0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
+	{0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
+	{0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
+	{0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
+	{0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+	{0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+	{0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+	{0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
+	{0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+	{0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0},
+	{0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+	{0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+	{0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+	{0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
+	{0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
+	{0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
+	{0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+	{0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+	{0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+	{0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
+	{0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
+	{0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+	{0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+	{0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+	{0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+	{0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+	{0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+	{0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+	{0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+	{0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+	{0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+	{0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+	{0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+	{0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+	{0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
+	{0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
+	{0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+	{0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+	{0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
+	{0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+	{0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+	{0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
+	{0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
+	{0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
+	{0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
+	{0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+	{0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
+	{0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
+	{0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
+	{0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
+	{0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
+	{0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
+	{0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
+	{0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
+	{0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
+	{0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
+	{0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
+	{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
+};
+
+static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
+
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
+
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+	{0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0},
+	{0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
+	{0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
+	{0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
+	{0x3008, 0x42, 0, 0},
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+	{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0},
+	{0x3008, 0x02, 0, 0}, {0x3503, 0,    0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
+	{0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+	{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
+	{0x3008, 0x42, 0, 0},
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+	{0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+	{0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0},
+	{0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
+	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
+	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+	{0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
+	{0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
+	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
+	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
+	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
+	{0x3503, 0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
+	{0x3008, 0x42, 0, 0},
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+	{0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+	{0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0},
+	{0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
+	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
+	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+	{0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
+	{0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
+	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
+	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
+	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
+	{0x4202, 0x0f, 0, 0},	/* stream off the sensor */
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, /*disable flip*/
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+	{0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+	{0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
+	{0x4202, 0x00, 0, 0},	/* stream on the sensor */
+};
+
+static struct ov5640_mode_info
+ov5640_mode_info_data[ov5640_num_framerates][ov5640_num_modes] = {
+	{
+		{ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144,
+		 ov5640_setting_15fps_QCIF_176_144,
+		 ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
+		{ov5640_mode_QVGA_320_240, SUBSAMPLING, 320,  240,
+		 ov5640_setting_15fps_QVGA_320_240,
+		 ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
+		{ov5640_mode_VGA_640_480, SUBSAMPLING, 640,  480,
+		 ov5640_setting_15fps_VGA_640_480,
+		 ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
+		{ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
+		 ov5640_setting_15fps_NTSC_720_480,
+		 ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
+		{ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576,
+		 ov5640_setting_15fps_PAL_720_576,
+		 ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
+		{ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768,
+		 ov5640_setting_15fps_XGA_1024_768,
+		 ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
+		{ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
+		 ov5640_setting_15fps_720P_1280_720,
+		 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
+		{ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
+		 ov5640_setting_15fps_1080P_1920_1080,
+		 ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
+		{ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944,
+		 ov5640_setting_15fps_QSXGA_2592_1944,
+		 ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
+	}, {
+		{ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144,
+		 ov5640_setting_30fps_QCIF_176_144,
+		 ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
+		{ov5640_mode_QVGA_320_240, SUBSAMPLING, 320,  240,
+		 ov5640_setting_30fps_QVGA_320_240,
+		 ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
+		{ov5640_mode_VGA_640_480, SUBSAMPLING, 640,  480,
+		 ov5640_setting_30fps_VGA_640_480,
+		 ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
+		{ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
+		 ov5640_setting_30fps_NTSC_720_480,
+		 ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
+		{ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576,
+		 ov5640_setting_30fps_PAL_720_576,
+		 ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
+		{ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768,
+		 ov5640_setting_30fps_XGA_1024_768,
+		 ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
+		{ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
+		 ov5640_setting_30fps_720P_1280_720,
+		 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
+		{ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
+		 ov5640_setting_30fps_1080P_1920_1080,
+		 ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
+		{ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
+	},
+};
+
+static int ov5640_probe(struct i2c_client *adapter,
+			const struct i2c_device_id *device_id);
+static int ov5640_remove(struct i2c_client *client);
+
+static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
+{
+	u8 buf[3] = {0};
+	int ret;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	buf[2] = val;
+
+	ret = i2c_master_send(sensor->i2c_client, buf, 3);
+	if (ret < 0) {
+		v4l2_err(&sensor->sd, "%s: error: reg=%x, val=%x\n",
+			__func__, reg, val);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
+{
+	u8 reg_buf[2] = {0};
+	u8 read_val = 0;
+
+	reg_buf[0] = reg >> 8;
+	reg_buf[1] = reg & 0xff;
+
+	if (2 != i2c_master_send(sensor->i2c_client, reg_buf, 2)) {
+		v4l2_err(&sensor->sd, "%s: write reg error: reg=%x\n",
+			__func__, reg);
+		return -EIO;
+	}
+
+	if (1 != i2c_master_recv(sensor->i2c_client, &read_val, 1)) {
+		v4l2_err(&sensor->sd, "%s: read reg error: reg=%x, val=%x\n",
+			__func__, reg, read_val);
+		return -EIO;
+	}
+
+	*val = read_val;
+	return 0;
+}
+
+#define OV5640_READ_REG(s, r, v) {				\
+		ret = ov5640_read_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+#define OV5640_WRITE_REG(s, r, v) {				\
+		ret = ov5640_write_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
+{
+	u8 hi, lo;
+	int ret;
+
+	OV5640_READ_REG(sensor, reg, &hi);
+	OV5640_READ_REG(sensor, reg+1, &lo);
+
+	*val = ((u16)hi << 8) | (u16)lo;
+	return 0;
+}
+#define OV5640_READ_REG16(s, r, v) {				\
+		ret = ov5640_read_reg16((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
+{
+	int ret;
+
+	OV5640_WRITE_REG(sensor, reg, val >> 8);
+	OV5640_WRITE_REG(sensor, reg+1, val & 0xff);
+	return 0;
+}
+#define OV5640_WRITE_REG16(s, r, v) {				\
+		ret = ov5640_write_reg16((s), (r), (v));	\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
+			  u8 mask, u8 val)
+{
+	u8 readval;
+	int ret;
+
+	OV5640_READ_REG(sensor, reg, &readval);
+
+	readval &= ~mask;
+	val &= mask;
+	val |= readval;
+
+	OV5640_WRITE_REG(sensor, reg, val);
+	return 0;
+}
+#define OV5640_MOD_REG(s, r, m, v) {				\
+		ret = ov5640_mod_reg((s), (r), (m), (v));	\
+		if (ret)					\
+			return ret;				\
+	}
+
+/* download ov5640 settings to sensor through i2c */
+static int ov5640_load_regs(struct ov5640_dev *sensor,
+			    struct reg_value *regs,
+			    int size)
+{
+	register u32 delay_ms = 0;
+	register u16 reg_addr = 0;
+	register u8 mask = 0;
+	register u8 val = 0;
+	int i, ret;
+
+	for (i = 0; i < size; ++i, ++regs) {
+		delay_ms = regs->delay_ms;
+		reg_addr = regs->reg_addr;
+		val = regs->val;
+		mask = regs->mask;
+
+		if (mask) {
+			OV5640_MOD_REG(sensor, reg_addr, mask, val);
+		} else {
+			OV5640_WRITE_REG(sensor, reg_addr, val);
+		}
+		if (delay_ms)
+			usleep_range(1000*delay_ms, 1000*delay_ms+1);
+	}
+
+	return 0;
+}
+
+static int ov5640_set_stream(struct ov5640_dev *sensor, bool on)
+{
+	int ret;
+
+	OV5640_WRITE_REG(sensor, 0x4202, on ? 0x00 : 0x0f);
+	return 0;
+}
+
+static int ov5640_get_sysclk(struct ov5640_dev *sensor)
+{
+	 /* calculate sysclk */
+	int xvclk = sensor->xclk_freq / 10000;
+	int multiplier, prediv, VCO, sysdiv, pll_rdiv;
+	int sclk_rdiv_map[] = {1, 2, 4, 8};
+	int bit_div2x = 1, sclk_rdiv, sysclk;
+	u8 temp1, temp2;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3034, &temp1);
+	temp2 = temp1 & 0x0f;
+	if (temp2 == 8 || temp2 == 10)
+		bit_div2x = temp2 / 2;
+
+	OV5640_READ_REG(sensor, 0x3035, &temp1);
+	sysdiv = temp1>>4;
+	if (sysdiv == 0)
+		sysdiv = 16;
+
+	OV5640_READ_REG(sensor, 0x3036, &temp1);
+	multiplier = temp1;
+
+	OV5640_READ_REG(sensor, 0x3037, &temp1);
+	prediv = temp1 & 0x0f;
+	pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
+
+	OV5640_READ_REG(sensor, 0x3108, &temp1);
+	temp2 = temp1 & 0x03;
+	sclk_rdiv = sclk_rdiv_map[temp2];
+
+	VCO = xvclk * multiplier / prediv;
+
+	sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
+
+	return sysclk;
+}
+
+static int ov5640_set_night_mode(struct ov5640_dev *sensor)
+{
+	 /* read HTS from register settings */
+	u8 mode;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3a00, &mode);
+	mode &= 0xfb;
+	OV5640_WRITE_REG(sensor, 0x3a00, mode);
+	return 0;
+}
+
+static int ov5640_get_HTS(struct ov5640_dev *sensor)
+{
+	 /* read HTS from register settings */
+	u16 HTS;
+	int ret;
+
+	OV5640_READ_REG16(sensor, 0x380c, &HTS);
+	return HTS;
+}
+
+static int ov5640_get_VTS(struct ov5640_dev *sensor)
+{
+	u16 VTS;
+	int ret;
+
+	OV5640_READ_REG16(sensor, 0x380e, &VTS);
+	return VTS;
+}
+
+static int ov5640_set_VTS(struct ov5640_dev *sensor, int VTS)
+{
+	int ret;
+
+	OV5640_WRITE_REG16(sensor, 0x380e, VTS);
+	return 0;
+}
+
+static int ov5640_get_light_freq(struct ov5640_dev *sensor)
+{
+	/* get banding filter value */
+	u8 temp, temp1;
+	int light_freq = 0;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3c01, &temp);
+
+	if (temp & 0x80) {
+		/* manual */
+		OV5640_READ_REG(sensor, 0x3c00, &temp1);
+		if (temp1 & 0x04) {
+			/* 50Hz */
+			light_freq = 50;
+		} else {
+			/* 60Hz */
+			light_freq = 60;
+		}
+	} else {
+		/* auto */
+		OV5640_READ_REG(sensor, 0x3c0c, &temp1);
+		if (temp1 & 0x01) {
+			/* 50Hz */
+			light_freq = 50;
+		} else {
+			/* 60Hz */
+		}
+	}
+
+	return light_freq;
+}
+
+static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
+{
+	int prev_vts;
+	int band_step60, max_band60, band_step50, max_band50;
+	int ret;
+
+	/* read preview PCLK */
+	ret = ov5640_get_sysclk(sensor);
+	if (ret < 0)
+		return ret;
+	sensor->prev_sysclk = ret;
+	/* read preview HTS */
+	ret = ov5640_get_HTS(sensor);
+	if (ret < 0)
+		return ret;
+	sensor->prev_hts = ret;
+
+	/* read preview VTS */
+	ret = ov5640_get_VTS(sensor);
+	if (ret < 0)
+		return ret;
+	prev_vts = ret;
+
+	/* calculate banding filter */
+	/* 60Hz */
+	band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100/120;
+	OV5640_WRITE_REG16(sensor, 0x3a0a, band_step60);
+
+	max_band60 = (int)((prev_vts-4)/band_step60);
+	OV5640_WRITE_REG(sensor, 0x3a0d, max_band60);
+
+	/* 50Hz */
+	band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
+	OV5640_WRITE_REG16(sensor, 0x3a08, band_step50);
+
+	max_band50 = (int)((prev_vts-4)/band_step50);
+	OV5640_WRITE_REG(sensor, 0x3a0e, max_band50);
+
+	return 0;
+}
+
+static int ov5640_set_AE_target(struct ov5640_dev *sensor, int target)
+{
+	/* stable in high */
+	int fast_high, fast_low;
+	int ret;
+
+	sensor->ae_low = target * 23 / 25;	/* 0.92 */
+	sensor->ae_high = target * 27 / 25;	/* 1.08 */
+
+	fast_high = sensor->ae_high<<1;
+	if (fast_high > 255)
+		fast_high = 255;
+
+	fast_low = sensor->ae_low >> 1;
+
+	OV5640_WRITE_REG(sensor, 0x3a0f, sensor->ae_high);
+	OV5640_WRITE_REG(sensor, 0x3a10, sensor->ae_low);
+	OV5640_WRITE_REG(sensor, 0x3a1b, sensor->ae_high);
+	OV5640_WRITE_REG(sensor, 0x3a1e, sensor->ae_low);
+	OV5640_WRITE_REG(sensor, 0x3a11, fast_high);
+	OV5640_WRITE_REG(sensor, 0x3a1f, fast_low);
+
+	return 0;
+}
+
+static int ov5640_binning_on(struct ov5640_dev *sensor)
+{
+	u8 temp;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3821, &temp);
+	temp &= 0xfe;
+
+	return temp ? 1 : 0;
+}
+
+static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
+{
+	u8 temp, channel = sensor->ep.base.id;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x4814, &temp);
+	temp &= ~(3 << 6);
+	temp |= (channel << 6);
+	OV5640_WRITE_REG(sensor, 0x4814, temp);
+
+	return 0;
+}
+
+static enum ov5640_mode
+ov5640_find_nearest_mode(struct ov5640_dev *sensor,
+			 int width, int height)
+{
+	int i;
+
+	for (i = ov5640_num_modes - 1; i >= 0; i--) {
+		if (ov5640_mode_info_data[0][i].width <= width &&
+		    ov5640_mode_info_data[0][i].height <= height)
+			break;
+	}
+
+	if (i < 0)
+		i = 0;
+
+	return (enum ov5640_mode)i;
+}
+
+/*
+ * sensor changes between scaling and subsampling, go through
+ * exposure calculation
+ */
+static int ov5640_change_mode_exposure_calc(struct ov5640_dev *sensor,
+					    enum ov5640_frame_rate frame_rate,
+					    enum ov5640_mode mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	u8 average;
+	int prev_shutter, prev_gain16;
+	int cap_shutter, cap_gain16;
+	int cap_sysclk, cap_hts, cap_vts;
+	int light_freq, cap_bandfilt, cap_maxband;
+	long cap_gain16_shutter;
+	int ret = 0;
+
+	/* check if the input mode and frame rate is valid */
+	mode_data = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+	mode_size = ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+	sensor->fmt.width = ov5640_mode_info_data[frame_rate][mode].width;
+	sensor->fmt.height = ov5640_mode_info_data[frame_rate][mode].height;
+
+	if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
+	    mode_data == NULL || mode_size == 0)
+		return -EINVAL;
+
+	/* auto focus */
+	/* ov5640_auto_focus();//if no af function, just skip it */
+
+	/* turn off AE/AG */
+	ret = ov5640_set_agc(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	/* read preview shutter */
+	ret = ov5640_get_exposure(sensor);
+	if (ret < 0)
+		return ret;
+	prev_shutter = ret;
+	ret = ov5640_binning_on(sensor);
+	if (ret < 0)
+		return ret;
+	if (ret && mode != ov5640_mode_720P_1280_720 &&
+	    mode != ov5640_mode_1080P_1920_1080)
+		prev_shutter *= 2;
+
+	/* read preview gain */
+	ret = ov5640_get_gain(sensor);
+	if (ret < 0)
+		return ret;
+	prev_gain16 = ret;
+
+	/* get average */
+	OV5640_READ_REG(sensor, 0x56a1, &average);
+
+	/* turn off night mode for capture */
+	ret = ov5640_set_night_mode(sensor);
+	if (ret < 0)
+		return ret;
+
+	/* turn off overlay */
+	/* OV5640_WRITE_REG(0x3022, 0x06); //if no af function,
+	   just skip it */
+
+	ret = ov5640_set_stream(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	/* Write capture setting */
+	ret = ov5640_load_regs(sensor, mode_data, mode_size);
+	if (ret < 0)
+		return ret;
+
+	/* read capture VTS */
+	ret = ov5640_get_VTS(sensor);
+	if (ret < 0)
+		return ret;
+	cap_vts = ret;
+	ret = ov5640_get_HTS(sensor);
+	if (ret < 0)
+		return ret;
+	cap_hts = ret;
+	ret = ov5640_get_sysclk(sensor);
+	if (ret < 0)
+		return ret;
+	cap_sysclk = ret;
+
+	/* calculate capture banding filter */
+	ret = ov5640_get_light_freq(sensor);
+	if (ret < 0)
+		return ret;
+	light_freq = ret;
+
+	if (light_freq == 60) {
+		/* 60Hz */
+		cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
+	} else {
+		/* 50Hz */
+		cap_bandfilt = cap_sysclk * 100 / cap_hts;
+	}
+	cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
+
+	/* calculate capture shutter/gain16 */
+	if (average > sensor->ae_low && average < sensor->ae_high) {
+		/* in stable range */
+		cap_gain16_shutter =
+			prev_gain16 * prev_shutter *
+			cap_sysclk / sensor->prev_sysclk *
+			sensor->prev_hts / cap_hts *
+			sensor->ae_target / average;
+	} else {
+		cap_gain16_shutter =
+			prev_gain16 * prev_shutter *
+			cap_sysclk / sensor->prev_sysclk *
+			sensor->prev_hts / cap_hts;
+	}
+
+	/* gain to shutter */
+	if (cap_gain16_shutter < (cap_bandfilt * 16)) {
+		/* shutter < 1/100 */
+		cap_shutter = cap_gain16_shutter / 16;
+		if (cap_shutter < 1)
+			cap_shutter = 1;
+
+		cap_gain16 = cap_gain16_shutter / cap_shutter;
+		if (cap_gain16 < 16)
+			cap_gain16 = 16;
+	} else {
+		if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
+			/* exposure reach max */
+			cap_shutter = cap_bandfilt * cap_maxband;
+			cap_gain16 = cap_gain16_shutter / cap_shutter;
+		} else {
+			/* 1/100 < (cap_shutter = n/100) =< max */
+			cap_shutter =
+				((int)(cap_gain16_shutter / 16 / cap_bandfilt))
+				* cap_bandfilt;
+			cap_gain16 = cap_gain16_shutter / cap_shutter;
+		}
+	}
+
+	/* write capture gain */
+	ret = ov5640_set_gain(sensor, cap_gain16);
+	if (ret < 0)
+		return ret;
+
+	/* write capture shutter */
+	if (cap_shutter > (cap_vts - 4)) {
+		cap_vts = cap_shutter + 4;
+		ret = ov5640_set_VTS(sensor, cap_vts);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = ov5640_set_exposure(sensor, cap_shutter);
+	if (ret < 0)
+		return ret;
+
+	return ov5640_set_stream(sensor, true);
+}
+
+/*
+ * if sensor changes inside scaling or subsampling
+ * change mode directly
+ */
+static int ov5640_change_mode_direct(struct ov5640_dev *sensor,
+				     enum ov5640_frame_rate frame_rate,
+				     enum ov5640_mode mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	int ret = 0;
+
+	/* check if the input mode and frame rate is valid */
+	mode_data = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+	mode_size = ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+	sensor->fmt.width = ov5640_mode_info_data[frame_rate][mode].width;
+	sensor->fmt.height = ov5640_mode_info_data[frame_rate][mode].height;
+
+	if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
+	    mode_data == NULL || mode_size == 0)
+		return -EINVAL;
+
+	/* turn off AE/AG */
+	ret = ov5640_set_agc(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_set_stream(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	/* Write capture setting */
+	ret = ov5640_load_regs(sensor, mode_data, mode_size);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_set_stream(sensor, true);
+	if (ret < 0)
+		return ret;
+
+	return ov5640_set_agc(sensor, true);
+}
+
+static int ov5640_change_mode(struct ov5640_dev *sensor,
+			      enum ov5640_frame_rate frame_rate,
+			      enum ov5640_mode mode,
+			      enum ov5640_mode orig_mode)
+{
+	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	int ret = 0;
+
+	if ((mode >= ov5640_num_modes || mode < ov5640_mode_MIN) &&
+	    mode != ov5640_mode_INIT) {
+		v4l2_err(&sensor->sd, "Wrong ov5640 mode detected!\n");
+		return -EINVAL;
+	}
+
+	dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode;
+	orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode;
+	if (mode == ov5640_mode_INIT) {
+		mode_data = ov5640_init_setting_30fps_VGA;
+		mode_size = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
+
+		sensor->fmt.width = 640;
+		sensor->fmt.height = 480;
+		ret = ov5640_load_regs(sensor, mode_data, mode_size);
+		if (ret < 0)
+			return ret;
+
+		mode_data = ov5640_setting_30fps_VGA_640_480;
+		mode_size = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480);
+		ret = ov5640_load_regs(sensor, mode_data, mode_size);
+	} else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
+			(dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
+		/* change between subsampling and scaling
+		 * go through exposure calucation */
+		ret = ov5640_change_mode_exposure_calc(sensor, frame_rate,
+							  mode);
+	} else {
+		/* change inside subsampling or scaling
+		 * download firmware directly */
+		ret = ov5640_change_mode_direct(sensor, frame_rate, mode);
+	}
+
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_set_AE_target(sensor, sensor->ae_target);
+	if (ret < 0)
+		return ret;
+	ret = ov5640_get_light_freq(sensor);
+	if (ret < 0)
+		return ret;
+	ret = ov5640_set_bandingfilter(sensor);
+	if (ret < 0)
+		return ret;
+	ret = ov5640_set_virtual_channel(sensor);
+	if (ret < 0)
+		return ret;
+
+	/* restore controls */
+	ov5640_restore_ctrls(sensor);
+
+	if (ret >= 0 && mode != ov5640_mode_INIT) {
+		sensor->current_mode = mode;
+		sensor->current_fr = frame_rate;
+	}
+
+	return 0;
+}
+
+/* restore the last set video mode after chip power-on */
+static int ov5640_restore_mode(struct ov5640_dev *sensor)
+{
+	int ret = 0;
+
+	/* first we need to set some initial register values */
+	ret = ov5640_change_mode(sensor, sensor->current_fr,
+				    ov5640_mode_INIT, ov5640_mode_INIT);
+	if (ret < 0)
+		return ret;
+
+	/* now restore the last capture mode */
+	return ov5640_change_mode(sensor,
+				  sensor->current_fr,
+				  sensor->current_mode,
+				  ov5640_mode_VGA_640_480);
+}
+
+static int ov5640_regulators_on(struct ov5640_dev *sensor)
+{
+	int ret;
+
+	if (sensor->io_regulator) {
+		ret = regulator_enable(sensor->io_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "io reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->core_regulator) {
+		ret = regulator_enable(sensor->core_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "core reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->gpo_regulator) {
+		ret = regulator_enable(sensor->gpo_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "gpo reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->analog_regulator) {
+		ret = regulator_enable(sensor->analog_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "analog reg enable failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void ov5640_regulators_off(struct ov5640_dev *sensor)
+{
+	if (sensor->analog_regulator)
+		regulator_disable(sensor->analog_regulator);
+	if (sensor->core_regulator)
+		regulator_disable(sensor->core_regulator);
+	if (sensor->io_regulator)
+		regulator_disable(sensor->io_regulator);
+	if (sensor->gpo_regulator)
+		regulator_disable(sensor->gpo_regulator);
+}
+
+/* --------------- Subdev Operations --------------- */
+
+static int ov5640_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	int ret;
+
+	if (on && !sensor->on) {
+		if (sensor->xclk)
+			clk_prepare_enable(sensor->xclk);
+
+		ret = ov5640_regulators_on(sensor);
+		if (ret)
+			return ret;
+
+		ov5640_reset(sensor);
+		ov5640_power(sensor, true);
+		ret = ov5640_restore_mode(sensor);
+		if (ret)
+			return ret;
+		/*
+		 * NOTE: Freescale adds a long delay (600 msec) after
+		 * powering up and programming a mode on the ov5640-mipi
+		 * camera (search for "msec_wait4stable" in FSL's
+		 * ov5640_mipi.c), which equivalently would need to go
+		 * right here. If we run into MIPI CSI-2 receiver dphy
+		 * ready timeouts, it might be a clue to add that delay
+		 * here.
+		 */
+	} else if (!on && sensor->on) {
+		ov5640_power(sensor, false);
+
+		ov5640_regulators_off(sensor);
+
+		if (sensor->xclk)
+			clk_disable_unprepare(sensor->xclk);
+	}
+
+	sensor->on = on;
+
+	return 0;
+}
+
+static int ov5640_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	/* This is the only case currently handled. */
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	cparm->capability = sensor->streamcap.capability;
+	cparm->timeperframe = sensor->streamcap.timeperframe;
+	cparm->capturemode = sensor->streamcap.capturemode;
+
+	return 0;
+}
+
+static int ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	enum ov5640_frame_rate frame_rate;
+	u32 tgt_fps;	/* target frames per secound */
+	int ret = 0;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	/* Check that the new frame rate is allowed. */
+	if ((timeperframe->numerator == 0) ||
+	    (timeperframe->denominator == 0)) {
+		timeperframe->denominator = DEFAULT_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps > MAX_FPS) {
+		timeperframe->denominator = MAX_FPS;
+		timeperframe->numerator = 1;
+	} else if (tgt_fps < MIN_FPS) {
+		timeperframe->denominator = MIN_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	/* Actual frame rate we use */
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps == 15)
+		frame_rate = ov5640_15_fps;
+	else if (tgt_fps == 30)
+		frame_rate = ov5640_30_fps;
+	else {
+		v4l2_err(&sensor->sd, "frame rate %u not supported!\n",
+			 tgt_fps);
+		return -EINVAL;
+	}
+
+	ret = ov5640_change_mode(sensor, frame_rate,
+				 sensor->current_mode,
+				 sensor->current_mode);
+	if (ret < 0)
+		return ret;
+
+	sensor->streamcap.timeperframe = *timeperframe;
+
+	return 0;
+}
+
+static int ov5640_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	if (format->pad)
+		return -EINVAL;
+
+	format->format = sensor->fmt;
+
+	return 0;
+}
+
+static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
+				   struct v4l2_mbus_framefmt *fmt,
+				   enum ov5640_mode *new_mode)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	enum ov5640_mode mode;
+
+	mode = ov5640_find_nearest_mode(sensor, fmt->width, fmt->height);
+
+	fmt->width = ov5640_mode_info_data[0][mode].width;
+	fmt->height = ov5640_mode_info_data[0][mode].height;
+	fmt->code = sensor->fmt.code;
+
+	if (new_mode)
+		*new_mode = mode;
+	return 0;
+}
+
+static int ov5640_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	enum ov5640_mode new_mode;
+	int ret;
+
+	if (format->pad)
+		return -EINVAL;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		ret = ov5640_try_fmt_internal(sd, &format->format, NULL);
+		if (ret)
+			return ret;
+		cfg->try_fmt = format->format;
+		return 0;
+	}
+
+	ret = ov5640_try_fmt_internal(sd, &format->format, &new_mode);
+	if (ret)
+		return ret;
+
+	ret = ov5640_change_mode(sensor, sensor->current_fr,
+				 new_mode, sensor->current_mode);
+	if (ret >= 0)
+		sensor->fmt = format->format;
+
+	return ret;
+}
+
+
+/*
+ * Sensor Controls.
+ */
+
+static int ov5640_set_hue(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 0, 1 << 0);
+		OV5640_WRITE_REG16(sensor, 0x5581, value);
+	} else
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 0, 0);
+
+	return 0;
+}
+
+static int ov5640_set_contrast(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 2, 1 << 2);
+		OV5640_WRITE_REG(sensor, 0x5585, value & 0xff);
+	} else
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 2, 0);
+
+	return 0;
+}
+
+static int ov5640_set_saturation(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 1, 1 << 1);
+		OV5640_WRITE_REG(sensor, 0x5583, value & 0xff);
+		OV5640_WRITE_REG(sensor, 0x5584, value & 0xff);
+	} else
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 1, 0);
+
+	return 0;
+}
+
+static int ov5640_set_awb(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	sensor->awb_on = value ? true : false;
+	OV5640_MOD_REG(sensor, 0x3406, 1 << 0, sensor->awb_on ? 0 : 1);
+	return 0;
+}
+
+static int ov5640_set_red_balance(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG(sensor, 0x3401, value & 0xff);
+	OV5640_WRITE_REG(sensor, 0x3400, (value & 0xf00) >> 8);
+	return 0;
+}
+
+#if 0
+static int ov5640_set_green_balance(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG(sensor, 0x3403, value & 0xff);
+	OV5640_WRITE_REG(sensor, 0x3402, (value & 0xf00) >> 8);
+	return 0;
+}
+#endif
+
+static int ov5640_set_blue_balance(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG(sensor, 0x3405, value & 0xff);
+	OV5640_WRITE_REG(sensor, 0x3404, (value & 0xf00) >> 8);
+	return 0;
+}
+
+static int ov5640_set_exposure(struct ov5640_dev *sensor, int value)
+{
+	u16 max_exp = 0;
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_READ_REG16(sensor, 0x350c, &max_exp);
+	if (value < max_exp) {
+		u32 exp = value << 4;
+
+		OV5640_WRITE_REG(sensor, 0x3502, exp & 0xff);
+		OV5640_WRITE_REG(sensor, 0x3501, (exp >> 8) & 0xff);
+		OV5640_WRITE_REG(sensor, 0x3500, (exp >> 16) & 0x0f);
+	}
+
+	return 0;
+}
+
+/* read exposure, in number of line periods */
+static int ov5640_get_exposure(struct ov5640_dev *sensor)
+{
+	u8 temp;
+	int exp, ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_READ_REG(sensor, 0x3500, &temp);
+	exp = ((int)temp & 0x0f) << 16;
+	OV5640_READ_REG(sensor, 0x3501, &temp);
+	exp |= ((int)temp << 8);
+	OV5640_READ_REG(sensor, 0x3502, &temp);
+	exp |= (int)temp;
+
+	return exp >> 4;
+}
+
+static int ov5640_set_agc(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	/* this enables/disables both AEC and AGC */
+	sensor->agc_on = value ? true : false;
+	OV5640_MOD_REG(sensor, 0x3503, 0x3, sensor->agc_on ? 0 : 0x3);
+
+	return 0;
+}
+
+static int ov5640_set_gain(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG16(sensor, 0x350a, value & 0x3ff);
+	return 0;
+}
+
+static int ov5640_get_gain(struct ov5640_dev *sensor)
+{
+	u16 gain;
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_READ_REG16(sensor, 0x350a, &gain);
+
+	return gain & 0x3ff;
+}
+
+#if 0
+static int ov5640_set_test_pattern(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	OV5640_MOD_REG(sensor, 0x503d, 0xa4, value ? 0xa4 : 0);
+	return 0;
+}
+#endif
+
+static struct ov5640_control ov5640_ctrls[] = {
+	{
+		.set = ov5640_set_agc,
+		.ctrl = {
+			.id = V4L2_CID_AUTOGAIN,
+			.name = "Auto Gain/Exposure Control",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5640_set_exposure,
+		.ctrl = {
+			.id = V4L2_CID_EXPOSURE,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 65535,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_gain,
+		.ctrl = {
+			.id = V4L2_CID_GAIN,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 1023,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_hue,
+		.ctrl = {
+			.id = V4L2_CID_HUE,
+			.name = "Hue",
+			.minimum = 0,
+			.maximum = 359,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_contrast,
+		.ctrl = {
+			.id = V4L2_CID_CONTRAST,
+			.name = "Contrast",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_saturation,
+		.ctrl = {
+			.id = V4L2_CID_SATURATION,
+			.name = "Saturation",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 64,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_awb,
+		.ctrl = {
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5640_set_red_balance,
+		.ctrl = {
+			.id = V4L2_CID_RED_BALANCE,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_blue_balance,
+		.ctrl = {
+			.id = V4L2_CID_BLUE_BALANCE,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	},
+};
+#define OV5640_NUM_CONTROLS ARRAY_SIZE(ov5640_ctrls)
+
+static struct ov5640_control *ov5640_get_ctrl(int id, int *index)
+{
+	struct ov5640_control *ret = NULL;
+	int i;
+
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		if (id == ov5640_ctrls[i].ctrl.id) {
+			ret = &ov5640_ctrls[i];
+			break;
+		}
+	}
+
+	if (ret && index)
+		*index = i;
+	return ret;
+}
+
+static int ov5640_restore_ctrls(struct ov5640_dev *sensor)
+{
+	struct ov5640_control *c;
+	int i;
+
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		c = &ov5640_ctrls[i];
+		c->set(sensor, sensor->ctrl_cache[i]);
+	}
+
+	return 0;
+}
+
+static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov5640_dev *sensor = ctrl_to_ov5640_dev(ctrl);
+	struct ov5640_control *c;
+	int ret = 0;
+	int i;
+
+	c = ov5640_get_ctrl(ctrl->id, &i);
+	if (!c)
+		return -EINVAL;
+
+	ret = c->set(sensor, ctrl->val);
+	/* update cached value if no error */
+	if (!ret)
+		sensor->ctrl_cache[i] = ctrl->val;
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
+	.s_ctrl = ov5640_s_ctrl,
+};
+
+static int ov5640_init_controls(struct ov5640_dev *sensor)
+{
+	struct ov5640_control *c;
+	int i;
+
+	v4l2_ctrl_handler_init(&sensor->ctrl_hdl, OV5640_NUM_CONTROLS);
+
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		c = &ov5640_ctrls[i];
+
+		v4l2_ctrl_new_std(&sensor->ctrl_hdl, &ov5640_ctrl_ops,
+				  c->ctrl.id, c->ctrl.minimum, c->ctrl.maximum,
+				  c->ctrl.step, c->ctrl.default_value);
+	}
+
+	sensor->sd.ctrl_handler = &sensor->ctrl_hdl;
+	if (sensor->ctrl_hdl.error) {
+		int err = sensor->ctrl_hdl.error;
+
+		v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+		v4l2_err(&sensor->sd, "%s: error %d\n", __func__, err);
+		return err;
+	}
+	v4l2_ctrl_handler_setup(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->pad)
+		return -EINVAL;
+	if (fse->index >= ov5640_num_modes)
+		return -EINVAL;
+
+	fse->min_width = fse->max_width =
+		ov5640_mode_info_data[0][fse->index].width;
+	fse->min_height = fse->max_height =
+		ov5640_mode_info_data[0][fse->index].height;
+
+	return 0;
+}
+
+static int ov5640_enum_frame_interval(
+	struct v4l2_subdev *sd,
+	struct v4l2_subdev_pad_config *cfg,
+	struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	enum ov5640_mode mode;
+
+	if (fie->pad)
+		return -EINVAL;
+	if (fie->index < 0 || fie->index >= ov5640_num_framerates)
+		return -EINVAL;
+
+	if (fie->width == 0 || fie->height == 0)
+		return -EINVAL;
+
+	mode = ov5640_find_nearest_mode(sensor, fie->width, fie->height);
+
+	if (ov5640_mode_info_data[fie->index][mode].init_data_ptr == NULL)
+		return -EINVAL;
+
+	fie->interval.numerator = 1;
+	fie->interval.denominator = ov5640_framerates[fie->index];
+
+	dev_dbg(sensor->dev, "%dx%d: [%d] = %d fps\n",
+		fie->width, fie->height, fie->index, fie->interval.denominator);
+	return 0;
+}
+
+static int ov5640_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	*status = !sensor->on ? V4L2_IN_ST_NO_POWER : 0;
+
+	return 0;
+}
+
+static int ov5640_s_routing(struct v4l2_subdev *sd, u32 input,
+			    u32 output, u32 config)
+{
+	return (input != 0) ? -EINVAL : 0;
+}
+
+static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	if (code->pad)
+		return -EINVAL;
+	if (code->index != 0)
+		return -EINVAL;
+
+	code->code = sensor->fmt.code;
+
+	return 0;
+}
+
+static int ov5640_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	cfg->type = V4L2_MBUS_CSI2;
+	cfg->flags = sensor->ep.bus.mipi_csi2.flags;
+	cfg->flags |= (1 << (sensor->ep.bus.mipi_csi2.num_data_lanes - 1));
+	cfg->flags |= V4L2_MBUS_CSI2_CHANNEL_0;
+
+	return 0;
+}
+
+static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	return ov5640_set_stream(sensor, enable);
+}
+
+static struct v4l2_subdev_core_ops ov5640_core_ops = {
+	.s_power = ov5640_s_power,
+	.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,
+};
+
+static struct v4l2_subdev_video_ops ov5640_video_ops = {
+	.s_parm = ov5640_s_parm,
+	.g_parm = ov5640_g_parm,
+	.g_input_status = ov5640_g_input_status,
+	.s_routing = ov5640_s_routing,
+	.g_mbus_config  = ov5640_g_mbus_config,
+	.s_stream = ov5640_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5640_pad_ops = {
+	.enum_mbus_code = ov5640_enum_mbus_code,
+	.get_fmt = ov5640_get_fmt,
+	.set_fmt = ov5640_set_fmt,
+	.enum_frame_size = ov5640_enum_frame_size,
+	.enum_frame_interval = ov5640_enum_frame_interval,
+};
+
+static struct v4l2_subdev_ops ov5640_subdev_ops = {
+	.core = &ov5640_core_ops,
+	.video = &ov5640_video_ops,
+	.pad = &ov5640_pad_ops,
+};
+
+static void ov5640_power(struct ov5640_dev *sensor, bool enable)
+{
+	gpio_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
+}
+
+static void ov5640_reset(struct ov5640_dev *sensor)
+{
+	gpio_set_value(sensor->reset_gpio, 1);
+
+	/* camera power cycle */
+	ov5640_power(sensor, false);
+	usleep_range(5000, 5001);
+	ov5640_power(sensor, true);
+	usleep_range(5000, 5001);
+
+	gpio_set_value(sensor->reset_gpio, 0);
+	usleep_range(1000, 1001);
+
+	gpio_set_value(sensor->reset_gpio, 1);
+	usleep_range(5000, 5001);
+}
+
+static void ov5640_get_regulators(struct ov5640_dev *sensor)
+{
+	sensor->io_regulator = devm_regulator_get(sensor->dev, "DOVDD");
+	if (!IS_ERR(sensor->io_regulator)) {
+		regulator_set_voltage(sensor->io_regulator,
+				      OV5640_VOLTAGE_DIGITAL_IO,
+				      OV5640_VOLTAGE_DIGITAL_IO);
+	} else {
+		dev_dbg(sensor->dev, "%s: no io voltage reg found\n",
+			__func__);
+		sensor->io_regulator = NULL;
+	}
+
+	sensor->core_regulator = devm_regulator_get(sensor->dev, "DVDD");
+	if (!IS_ERR(sensor->core_regulator)) {
+		regulator_set_voltage(sensor->core_regulator,
+				      OV5640_VOLTAGE_DIGITAL_CORE,
+				      OV5640_VOLTAGE_DIGITAL_CORE);
+	} else {
+		sensor->core_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no core voltage reg found\n",
+			__func__);
+	}
+
+	sensor->analog_regulator = devm_regulator_get(sensor->dev, "AVDD");
+	if (!IS_ERR(sensor->analog_regulator)) {
+		regulator_set_voltage(sensor->analog_regulator,
+				      OV5640_VOLTAGE_ANALOG,
+				      OV5640_VOLTAGE_ANALOG);
+	} else {
+		sensor->analog_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no analog voltage reg found\n",
+			__func__);
+	}
+}
+
+static int ov5640_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *endpoint;
+	struct ov5640_dev *sensor;
+	int i, xclk, ret;
+
+	sensor = devm_kzalloc(dev, sizeof(struct ov5640_dev), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->i2c_client = client;
+	sensor->dev = dev;
+	sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
+	sensor->fmt.width = 640;
+	sensor->fmt.height = 480;
+	sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY |
+					   V4L2_CAP_TIMEPERFRAME;
+	sensor->streamcap.capturemode = 0;
+	sensor->streamcap.timeperframe.denominator = DEFAULT_FPS;
+	sensor->streamcap.timeperframe.numerator = 1;
+
+	sensor->current_mode = ov5640_mode_VGA_640_480;
+	sensor->current_fr = ov5640_30_fps;
+
+	sensor->ae_target = 52;
+
+	endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	v4l2_of_parse_endpoint(endpoint, &sensor->ep);
+	if (sensor->ep.bus_type != V4L2_MBUS_CSI2) {
+		dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
+		return -EINVAL;
+	}
+	of_node_put(endpoint);
+
+	/* get system clock (xclk) frequency */
+	ret = of_property_read_u32(dev->of_node, "xclk", &xclk);
+	if (!ret) {
+		if (xclk < OV5640_XCLK_MIN || xclk > OV5640_XCLK_MAX) {
+			dev_err(dev, "invalid xclk frequency\n");
+			return -EINVAL;
+		}
+		sensor->xclk_freq = xclk;
+	}
+
+	/* get system clock (xclk) */
+	sensor->xclk = devm_clk_get(dev, "xclk");
+	if (!IS_ERR(sensor->xclk)) {
+		if (!sensor->xclk_freq) {
+			dev_err(dev, "xclk requires xclk frequency!\n");
+			return -EINVAL;
+		}
+		clk_set_rate(sensor->xclk, sensor->xclk_freq);
+	} else {
+		/* assume system clock enabled by default */
+		sensor->xclk = NULL;
+	}
+
+	/* request power down pin */
+	ret = of_get_named_gpio(dev->of_node, "pwdn-gpios", 0);
+	if (!gpio_is_valid(ret)) {
+		dev_err(dev, "no sensor pwdn pin available");
+		return -EINVAL;
+	}
+	sensor->pwdn_gpio = ret;
+	ret = devm_gpio_request_one(dev, sensor->pwdn_gpio,
+				    GPIOF_OUT_INIT_HIGH, "ov5640_mipi_pwdn");
+	if (ret < 0) {
+		dev_err(dev, "request for power down gpio failed\n");
+		return ret;
+	}
+
+	/* request reset pin */
+	ret = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
+	if (!gpio_is_valid(ret)) {
+		dev_warn(dev, "no sensor reset pin available");
+		return -EINVAL;
+	}
+	sensor->reset_gpio = ret;
+	ret = devm_gpio_request_one(dev, sensor->reset_gpio,
+				    GPIOF_OUT_INIT_HIGH, "ov5640_mipi_reset");
+	if (ret < 0) {
+		dev_err(dev, "request for reset gpio failed\n");
+		return ret;
+	}
+
+	/* initialize the cached controls to their defaults */
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		struct ov5640_control *c = &ov5640_ctrls[i];
+
+		sensor->ctrl_cache[i] = c->ctrl.default_value;
+	}
+	sensor->awb_on = sensor->agc_on = true;
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
+
+	ov5640_get_regulators(sensor);
+
+	ret = ov5640_s_power(&sensor->sd, 1);
+	if (ret)
+		goto reg_off;
+	ret = ov5640_init_controls(sensor);
+	if (ret)
+		goto reg_off;
+	ret = ov5640_s_power(&sensor->sd, 0);
+	if (ret)
+		goto free_ctrls;
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret)
+		goto free_ctrls;
+
+	return 0;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+reg_off:
+	ov5640_regulators_off(sensor);
+	return ret;
+}
+
+/*!
+ * ov5640 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  Error code indicating success or failure
+ */
+static int ov5640_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	ov5640_regulators_off(sensor);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov5640_id[] = {
+	{"ov5640_mipi", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static const struct of_device_id ov5640_dt_ids[] = {
+	{ .compatible = "ovti,ov5640_mipi" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
+
+static struct i2c_driver ov5640_i2c_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = "ov5640_mipi",
+		.of_match_table	= ov5640_dt_ids,
+	},
+	.id_table = ov5640_id,
+	.probe    = ov5640_probe,
+	.remove   = ov5640_remove,
+};
+
+module_i2c_driver(ov5640_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
-- 
1.9.1


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

* [PATCH 33/38] media: imx: Add support for Parallel OV5642
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (31 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 32/38] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder Steve Longerbeam
                   ` (7 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This driver is based on ov5642.c from Freescale imx_3.10.17_1.0.0_beta
branch, modified heavily for code cleanup and converted from int-device
to subdev.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig  |    7 +
 drivers/staging/media/imx/capture/Makefile |    1 +
 drivers/staging/media/imx/capture/ov5642.c | 4333 ++++++++++++++++++++++++++++
 3 files changed, 4341 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/ov5642.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index d4389d1..42d87db 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -17,6 +17,13 @@ config IMX_VIDEO_SWITCH
 	  multiplexer controlled by register bitfields as well as
 	  external multiplexers controller by a GPIO.
 
+config IMX_CAMERA_OV5642
+       tristate "OmniVision OV5642 Parallel camera support"
+       depends on VIDEO_IMX_CAMERA
+       default y
+       ---help---
+         Parallel interface OV5642 camera support.
+
 config IMX_CAMERA_OV5640_MIPI
        tristate "OmniVision OV5640 MIPI CSI-2 camera support"
        depends on VIDEO_IMX_CAMERA
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index 1ad4fd2..07633be 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
 obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
 obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
 obj-$(CONFIG_IMX_CAMERA_OV5640_MIPI) += ov5640-mipi.o
+obj-$(CONFIG_IMX_CAMERA_OV5642) += ov5642.o
diff --git a/drivers/staging/media/imx/capture/ov5642.c b/drivers/staging/media/imx/capture/ov5642.c
new file mode 100644
index 0000000..6116636
--- /dev/null
+++ b/drivers/staging/media/imx/capture/ov5642.c
@@ -0,0 +1,4333 @@
+/*
+ * Copyright (c) 2012-2014 Mentor Graphics Inc.
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV5642_VOLTAGE_ANALOG               2800000
+#define OV5642_VOLTAGE_DIGITAL_CORE         1500000
+#define OV5642_VOLTAGE_DIGITAL_IO           1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+/* min/typical/max system clock (xclk) frequencies */
+#define OV5642_XCLK_MIN  6000000
+#define OV5642_XCLK_TYP 24000000
+#define OV5642_XCLK_MAX 54000000
+
+/* min/typical/max pixel clock (mclk) frequencies */
+#define OV5642_MCLK_MIN 48000000
+#define OV5642_MCLK_TYP 48000000
+#define OV5642_MCLK_MAX 96000000
+
+#define OV5642_CHIP_ID  0x300A
+
+#define OV5642_MAX_CONTROLS 64
+
+enum ov5642_mode {
+	ov5642_mode_MIN = 0,
+	ov5642_mode_QCIF_176_144 = 0,
+	ov5642_mode_QVGA_320_240,
+	ov5642_mode_VGA_640_480,
+	ov5642_mode_NTSC_720_480,
+	ov5642_mode_PAL_720_576,
+	ov5642_mode_XGA_1024_768,
+	ov5642_mode_720P_1280_720,
+	ov5642_mode_1080P_1920_1080,
+	ov5642_mode_QSXGA_2592_1944,
+	ov5642_num_modes,
+};
+
+enum ov5642_frame_rate {
+	ov5642_15_fps,
+	ov5642_30_fps
+};
+
+static int ov5642_framerates[] = {
+	[ov5642_15_fps] = 15,
+	[ov5642_30_fps] = 30,
+};
+#define ov5642_num_framerates ARRAY_SIZE(ov5642_framerates)
+
+struct reg_value {
+	u16 reg_addr;
+	u8 val;
+	u8 mask;
+	u32 delay_ms;
+};
+
+struct ov5642_mode_info {
+	enum ov5642_mode mode;
+	u32 width;
+	u32 height;
+	struct reg_value *init_data_ptr;
+	u32 init_data_size;
+};
+
+struct ov5642_dev {
+	struct i2c_client *i2c_client;
+	struct device *dev;
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_of_endpoint ep; /* the parsed DT endpoint info */
+	struct v4l2_mbus_framefmt fmt;
+	struct v4l2_captureparm streamcap;
+	struct clk *xclk; /* system clock to OV5642 */
+	int xclk_freq;    /* requested xclk freq from devicetree */
+
+	enum ov5642_mode current_mode;
+	enum ov5642_frame_rate current_fr;
+
+	bool on;
+	bool awb_on;
+	bool agc_on;
+
+	/* cached control settings */
+	int ctrl_cache[OV5642_MAX_CONTROLS];
+
+	int reset_gpio;
+	int pwdn_gpio;
+	int gp_gpio;
+
+	struct regulator *io_regulator;
+	struct regulator *core_regulator;
+	struct regulator *analog_regulator;
+	struct regulator *gpo_regulator;
+};
+
+static inline struct ov5642_dev *to_ov5642_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5642_dev, sd);
+}
+
+static inline struct ov5642_dev *ctrl_to_ov5642_dev(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct ov5642_dev, ctrl_hdl);
+}
+
+struct ov5642_control {
+	struct v4l2_queryctrl ctrl;
+	int (*set)(struct ov5642_dev *sensor, int value);
+};
+
+static void ov5642_power(struct ov5642_dev *sensor, bool enable);
+static int ov5642_restore_ctrls(struct ov5642_dev *sensor);
+static int ov5642_set_agc(struct ov5642_dev *sensor, int value);
+static int ov5642_set_exposure(struct ov5642_dev *sensor, int value);
+static int ov5642_set_gain(struct ov5642_dev *sensor, int value);
+
+#if 0
+/* not used, but keep around */
+static struct reg_value ov5642_rotate_none_VGA[] = {
+	{0x3818, 0xc1, 0x00, 0x00}, {0x3621, 0x87, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_vert_flip_VGA[] = {
+	{0x3818, 0x20, 0xbf, 0x00}, {0x3621, 0x20, 0xff, 0x00},
+};
+static struct reg_value ov5642_rotate_horiz_flip_VGA[] = {
+	{0x3818, 0x81, 0x00, 0x01}, {0x3621, 0xa7, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_180_VGA[] = {
+	{0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00},
+};
+static struct reg_value ov5642_rotate_none_FULL[] = {
+	{0x3818, 0xc0, 0x00, 0x00}, {0x3621, 0x09, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_vert_flip_FULL[] = {
+	{0x3818, 0x20, 0xbf, 0x01}, {0x3621, 0x20, 0xff, 0x00},
+};
+static struct reg_value ov5642_rotate_horiz_flip_FULL[] = {
+	{0x3818, 0x80, 0x00, 0x01}, {0x3621, 0x29, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_180_FULL[] = {
+	{0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00},
+};
+#endif
+
+static struct reg_value ov5642_initial_setting[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c00, 0x04, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0},
+	{0x5182, 0x00, 0, 0}, {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5001, 0xff, 0, 0}, {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0},
+	{0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0},
+	{0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0},
+	{0x501f, 0x00, 0, 0}, {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0},
+	{0x350b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x0b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 200},
+};
+
+static struct reg_value ov5642_setting_15fps_QCIF_176_144[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_QCIF_176_144[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x10, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_QSXGA_2592_1944[] = {
+	{0x3503, 0x07, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0},
+	{0x3002, 0x00, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0},
+	{0x3005, 0xff, 0, 0}, {0x3006, 0xff, 0, 0}, {0x3007, 0x3f, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3818, 0xc0, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3602, 0xe4, 0, 0}, {0x3612, 0xac, 0, 0}, {0x3613, 0x44, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3623, 0x22, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3705, 0xda, 0, 0}, {0x370a, 0x80, 0, 0}, {0x3801, 0x95, 0, 0},
+	{0x3803, 0x0e, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x20, 0, 0},
+	{0x3806, 0x07, 0, 0}, {0x3807, 0x98, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3815, 0x44, 0, 0},
+	{0x3824, 0x11, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x5682, 0x0a, 0, 0}, {0x5683, 0x20, 0, 0}, {0x5686, 0x07, 0, 0},
+	{0x5687, 0x98, 0, 0}, {0x5001, 0xff, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x4407, 0x04, 0, 0}, {0x3008, 0x02, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4713, 0x03, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x3815, 0x01, 0, 0},
+	{0x501f, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0},
+	{0x5002, 0xe0, 0, 0}, {0x530a, 0x01, 0, 0}, {0x530d, 0x10, 0, 0},
+	{0x530c, 0x04, 0, 0}, {0x5312, 0x20, 0, 0}, {0x5282, 0x01, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x3012, 0x00, 0, 0},
+};
+
+
+static struct reg_value ov5642_setting_VGA_2_QVGA[] = {
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x3815, 0x04, 0, 0},
+};
+
+static struct reg_value ov5642_setting_QSXGA_2_VGA[] = {
+	{0x3503, 0x00, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0},
+	{0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0},
+	{0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x3818, 0xc1, 0, 0}, {0x3621, 0x87, 0, 0},
+	{0x350c, 0x03, 0, 0}, {0x350d, 0xe8, 0, 0}, {0x3602, 0xfc, 0, 0},
+	{0x3612, 0xff, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3622, 0x60, 0, 0},
+	{0x3623, 0x01, 0, 0}, {0x3604, 0x48, 0, 0}, {0x3705, 0xdb, 0, 0},
+	{0x370a, 0x81, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0},
+	{0x3807, 0xc0, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0},
+	{0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3810, 0x40, 0, 0}, {0x3815, 0x04, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3825, 0xb4, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0},
+	{0x5001, 0xff, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x4407, 0x0c, 0, 0}, {0x3008, 0x02, 0, 0}, {0x460b, 0x37, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4713, 0x02, 0, 0},
+	{0x471c, 0xd0, 0, 0}, {0x3815, 0x04, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x3002, 0x5c, 0, 0}, {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0},
+	{0x530a, 0x01, 0, 0}, {0x530d, 0x0c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x5312, 0x40, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x3012, 0x02, 0, 0}, {0x3010, 0x00, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_VGA_640_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_QVGA_320_240[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3808, 0x01, 0, 0},
+	{0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_NTSC_720_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x302c, 0x60, 0x60, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_PAL_720_576[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xd8, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x5682, 0x04, 0, 0},
+	{0x5683, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x302c, 0x60, 0x60, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_720P_1280_720[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+	{0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+	{0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+	{0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+	{0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+	{0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+	{0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+	{0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+	{0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+	{0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+	{0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0},
+	{0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0},
+	{0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0},
+	{0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0},
+	{0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0},
+	{0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0},
+	{0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0},
+	{0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0},
+	{0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0},
+	{0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_1080P_1920_1080[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+	{0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+	{0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+	{0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+	{0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+	{0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+	{0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+	{0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+	{0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+	{0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+	{0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x07, 0, 0},
+	{0x350c, 0x04, 0, 0}, {0x350d, 0x58, 0, 0}, {0x3801, 0x8a, 0, 0},
+	{0x3803, 0x0a, 0, 0}, {0x3804, 0x07, 0, 0}, {0x3805, 0x80, 0, 0},
+	{0x3806, 0x04, 0, 0}, {0x3807, 0x39, 0, 0}, {0x3808, 0x07, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+	{0x380c, 0x09, 0, 0}, {0x380d, 0xd6, 0, 0}, {0x380e, 0x04, 0, 0},
+	{0x380f, 0x58, 0, 0}, {0x381c, 0x11, 0, 0}, {0x381d, 0xba, 0, 0},
+	{0x381e, 0x04, 0, 0}, {0x381f, 0x48, 0, 0}, {0x3820, 0x04, 0, 0},
+	{0x3821, 0x18, 0, 0}, {0x3a08, 0x14, 0, 0}, {0x3a09, 0xe0, 0, 0},
+	{0x3a0a, 0x11, 0, 0}, {0x3a0b, 0x60, 0, 0}, {0x3a0d, 0x04, 0, 0},
+	{0x3a0e, 0x03, 0, 0}, {0x5682, 0x07, 0, 0}, {0x5683, 0x60, 0, 0},
+	{0x5686, 0x04, 0, 0}, {0x5687, 0x1c, 0, 0}, {0x5001, 0x7f, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, {0x501f, 0x00, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0},
+	{0x5002, 0xe0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_VGA_640_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_QVGA_320_240[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_NTSC_720_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3824, 0x11, 0, 0}, {0x3825, 0xb4, 0, 0}, {0x3826, 0x00, 0, 0},
+	{0x3827, 0x3d, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380A, 0x01, 0, 0}, {0x380B, 0xe0, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0},
+	{0x3807, 0x55, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0x55, 0, 0},
+	{0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_PAL_720_576[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3824, 0x11, 0, 0}, {0x3825, 0xdc, 0, 0}, {0x3826, 0x00, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380A, 0x02, 0, 0}, {0x380B, 0x40, 0, 0},
+	{0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0},
+	{0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xc0, 0, 0},
+	{0x5682, 0x04, 0, 0}, {0x5683, 0xb0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_XGA_1024_768[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+	{0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0}, {0x302c, 0x60, 0x60, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_XGA_1024_768[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+	{0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_720P_1280_720[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+	{0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+	{0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+	{0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+	{0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+	{0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+	{0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+	{0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+	{0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+	{0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+	{0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0},
+	{0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0},
+	{0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0},
+	{0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0},
+	{0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0},
+	{0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0},
+	{0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0},
+	{0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0},
+	{0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0},
+	{0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, {0x3010, 0x30, 0, 0},
+	{0x3a08, 0x06, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x05, 0, 0},
+	{0x3a0b, 0x50, 0, 0}, {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x07, 0, 0},
+};
+
+static struct ov5642_mode_info
+ov5642_mode_info_data[ov5642_num_framerates][ov5642_num_modes] = {
+	{
+		{ov5642_mode_QCIF_176_144, 176, 144,
+		ov5642_setting_15fps_QCIF_176_144,
+		ARRAY_SIZE(ov5642_setting_15fps_QCIF_176_144)},
+		{ov5642_mode_QVGA_320_240,   320,  240,
+		ov5642_setting_15fps_QVGA_320_240,
+		ARRAY_SIZE(ov5642_setting_15fps_QVGA_320_240)},
+		{ov5642_mode_VGA_640_480,    640,  480,
+		ov5642_setting_15fps_VGA_640_480,
+		ARRAY_SIZE(ov5642_setting_15fps_VGA_640_480)},
+		{ov5642_mode_NTSC_720_480,   720,  480,
+		ov5642_setting_15fps_NTSC_720_480,
+		ARRAY_SIZE(ov5642_setting_15fps_NTSC_720_480)},
+		{ov5642_mode_PAL_720_576,   720,  576,
+		ov5642_setting_15fps_PAL_720_576,
+		ARRAY_SIZE(ov5642_setting_15fps_PAL_720_576)},
+		{ov5642_mode_XGA_1024_768, 1024, 768,
+		ov5642_setting_15fps_XGA_1024_768,
+		ARRAY_SIZE(ov5642_setting_15fps_XGA_1024_768)},
+		{ov5642_mode_720P_1280_720,  1280, 720,
+		ov5642_setting_15fps_720P_1280_720,
+		ARRAY_SIZE(ov5642_setting_15fps_720P_1280_720)},
+		{ov5642_mode_1080P_1920_1080, 1920, 1080,
+		ov5642_setting_15fps_1080P_1920_1080,
+		ARRAY_SIZE(ov5642_setting_15fps_1080P_1920_1080)},
+		{ov5642_mode_QSXGA_2592_1944, 2592, 1944,
+		ov5642_setting_15fps_QSXGA_2592_1944,
+		ARRAY_SIZE(ov5642_setting_15fps_QSXGA_2592_1944)},
+	},
+	{
+		{ov5642_mode_QCIF_176_144, 176, 144,
+		ov5642_setting_30fps_QCIF_176_144,
+		ARRAY_SIZE(ov5642_setting_30fps_QCIF_176_144)},
+		{ov5642_mode_QVGA_320_240,   320,  240,
+		ov5642_setting_30fps_QVGA_320_240,
+		ARRAY_SIZE(ov5642_setting_30fps_QVGA_320_240)},
+		{ov5642_mode_VGA_640_480,    640,  480,
+		ov5642_setting_30fps_VGA_640_480,
+		ARRAY_SIZE(ov5642_setting_30fps_VGA_640_480)},
+		{ov5642_mode_NTSC_720_480,   720, 480,
+		ov5642_setting_30fps_NTSC_720_480,
+		ARRAY_SIZE(ov5642_setting_30fps_NTSC_720_480)},
+		{ov5642_mode_PAL_720_576,    720, 576,
+		ov5642_setting_30fps_PAL_720_576,
+		ARRAY_SIZE(ov5642_setting_30fps_PAL_720_576)},
+		{ov5642_mode_XGA_1024_768, 1024, 768,
+		ov5642_setting_30fps_XGA_1024_768,
+		ARRAY_SIZE(ov5642_setting_30fps_XGA_1024_768)},
+		{ov5642_mode_720P_1280_720,  1280, 720,
+		ov5642_setting_30fps_720P_1280_720,
+		ARRAY_SIZE(ov5642_setting_30fps_720P_1280_720)},
+		{ov5642_mode_1080P_1920_1080, 0, 0, NULL, 0},
+		{ov5642_mode_QSXGA_2592_1944, 0, 0, NULL, 0},
+	},
+};
+
+static int ov5642_write_reg(struct ov5642_dev *sensor, u16 reg, u8 val)
+{
+	u8 au8Buf[3] = {0};
+	int ret;
+
+	au8Buf[0] = reg >> 8;
+	au8Buf[1] = reg & 0xff;
+	au8Buf[2] = val;
+
+	ret = i2c_master_send(sensor->i2c_client, au8Buf, 3);
+	if (ret < 0) {
+		dev_err(sensor->dev, "%s:write reg error:reg=%x,val=%x\n",
+			__func__, reg, val);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ov5642_read_reg(struct ov5642_dev *sensor, u16 reg, u8 *val)
+{
+	u8 au8RegBuf[2] = {0};
+	u8 u8RdVal = 0;
+
+	au8RegBuf[0] = reg >> 8;
+	au8RegBuf[1] = reg & 0xff;
+
+	if (2 != i2c_master_send(sensor->i2c_client, au8RegBuf, 2)) {
+		dev_err(sensor->dev, "%s:write reg error:reg=%x\n",
+			__func__, reg);
+		return -EIO;
+	}
+
+	if (1 != i2c_master_recv(sensor->i2c_client, &u8RdVal, 1)) {
+		dev_err(sensor->dev, "%s:read reg error:reg=%x,val=%x\n",
+			__func__, reg, u8RdVal);
+		return -EIO;
+	}
+
+	*val = u8RdVal;
+	return 0;
+}
+
+#define OV5642_READ_REG(s, r, v) {				\
+		ret = ov5642_read_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+#define OV5642_WRITE_REG(s, r, v) {				\
+		ret = ov5642_write_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5642_read_reg16(struct ov5642_dev *sensor, u16 reg, u16 *val)
+{
+	u8 hi, lo;
+	int ret;
+
+	OV5642_READ_REG(sensor, reg, &hi);
+	OV5642_READ_REG(sensor, reg+1, &lo);
+
+	*val = ((u16)hi << 8) | (u16)lo;
+	return 0;
+}
+
+#define OV5642_READ_REG16(s, r, v) {				\
+		ret = ov5642_read_reg16((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5642_mod_reg(struct ov5642_dev *sensor, u16 reg,
+			  u8 mask, u8 val)
+{
+	u8 readval;
+	int ret;
+
+	OV5642_READ_REG(sensor, reg, &readval);
+
+	readval &= ~mask;
+	val &= mask;
+	val |= readval;
+
+	OV5642_WRITE_REG(sensor, reg, val);
+	return 0;
+}
+
+#define OV5642_MOD_REG(s, r, m, v) {				\
+		ret = ov5642_mod_reg((s), (r), (m), (v));	\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5642_load_regs(struct ov5642_dev *sensor,
+			    struct reg_value *regs,
+			    int size)
+{
+	register u32 delay_ms = 0;
+	register u16 reg_addr = 0;
+	register u8 mask = 0;
+	register u8 val = 0;
+	int i, ret = 0;
+
+	for (i = 0; i < size; ++i, ++regs) {
+		delay_ms = regs->delay_ms;
+		reg_addr = regs->reg_addr;
+		val = regs->val;
+		mask = regs->mask;
+
+		if (mask) {
+			OV5642_MOD_REG(sensor, reg_addr, mask, val);
+		} else {
+			OV5642_WRITE_REG(sensor, reg_addr, val);
+		}
+		if (delay_ms)
+			usleep_range(1000*delay_ms, 1000*delay_ms+1);
+	}
+
+	return 0;
+}
+
+static int ov5642_dump_format(struct ov5642_dev *sensor)
+{
+	u8 scaling, l_pix, r_pix, pix_fmt;
+	u16 hs, vs, hw, vh, dvp_w, dvp_h;
+	int ret;
+
+	OV5642_READ_REG16(sensor, 0x3800, &hs);
+	OV5642_READ_REG16(sensor, 0x3802, &vs);
+	OV5642_READ_REG16(sensor, 0x3804, &hw);
+	OV5642_READ_REG16(sensor, 0x3806, &vh);
+
+	OV5642_READ_REG(sensor, 0x5001, &scaling);
+	OV5642_READ_REG16(sensor, 0x3808, &dvp_w);
+	OV5642_READ_REG16(sensor, 0x380a, &dvp_h);
+
+	OV5642_READ_REG(sensor, 0x4711, &l_pix);
+	OV5642_READ_REG(sensor, 0x4712, &r_pix);
+	OV5642_READ_REG(sensor, 0x4300, &pix_fmt);
+
+	dev_dbg(sensor->dev, "Image Window:\n");
+	dev_dbg(sensor->dev, "\thoriz: %u@%u\n", hw, hs);
+	dev_dbg(sensor->dev, "\tvert : %u@%u\n", vh, vs);
+	dev_dbg(sensor->dev, "DVP Scaling:\n");
+	dev_dbg(sensor->dev, "\thoriz %s, vert %s, %ux%u\n",
+		(scaling & (1 << 4)) ? "enabled" : "disabled",
+		(scaling & (1 << 5)) ? "enabled" : "disabled",
+		dvp_w, dvp_h);
+	dev_dbg(sensor->dev, "DVP Padding:\n");
+	dev_dbg(sensor->dev, "\tleft %u pixels, right %u pixels\n",
+		l_pix, r_pix);
+	dev_dbg(sensor->dev, "Pixel Format: %02x\n", pix_fmt);
+
+	return 0;
+}
+
+static int ov5642_init_mode(struct ov5642_dev *sensor,
+			    enum ov5642_frame_rate frame_rate,
+			    enum ov5642_mode mode);
+static int ov5642_write_snapshot_para(struct ov5642_dev *sensor,
+				      enum ov5642_frame_rate frame_rate,
+				      enum ov5642_mode mode);
+
+static enum ov5642_mode
+ov5642_find_nearest_mode(struct ov5642_dev *sensor,
+			 int width, int height)
+{
+	int i;
+
+	for (i = ov5642_num_modes - 1; i >= 0; i--) {
+		if (ov5642_mode_info_data[0][i].width <= width &&
+		    ov5642_mode_info_data[0][i].height <= height)
+			break;
+	}
+
+	if (i < 0)
+		i = 0;
+
+	return (enum ov5642_mode)i;
+}
+
+static int ov5642_change_mode(struct ov5642_dev *sensor,
+			      enum ov5642_frame_rate new_frame_rate,
+			      enum ov5642_frame_rate old_frame_rate,
+			      enum ov5642_mode new_mode,
+			      enum ov5642_mode orig_mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	int ret = 0;
+
+	if (new_mode >= ov5642_num_modes || new_mode < ov5642_mode_MIN) {
+		dev_err(sensor->dev, "Wrong ov5642 mode detected!\n");
+		return -EINVAL;
+	}
+
+	if (new_frame_rate == old_frame_rate) {
+		if (new_mode == orig_mode)
+			return 0;
+		else if (new_mode == ov5642_mode_VGA_640_480 &&
+			 orig_mode == ov5642_mode_QSXGA_2592_1944) {
+			mode_data = ov5642_setting_QSXGA_2_VGA;
+			mode_size = ARRAY_SIZE(ov5642_setting_QSXGA_2_VGA);
+			sensor->fmt.width = 640;
+			sensor->fmt.height = 480;
+		} else if (new_mode == ov5642_mode_QVGA_320_240 &&
+			   orig_mode == ov5642_mode_VGA_640_480) {
+			mode_data = ov5642_setting_VGA_2_QVGA;
+			mode_size = ARRAY_SIZE(ov5642_setting_VGA_2_QVGA);
+			sensor->fmt.width = 320;
+			sensor->fmt.height = 240;
+		} else
+			goto load_full;
+
+		ret = ov5642_load_regs(sensor, mode_data, mode_size);
+	} else {
+load_full:
+		ret = ov5642_write_snapshot_para(sensor, new_frame_rate,
+						    new_mode);
+	}
+
+	if (ret)
+		return ret;
+
+	/* restore controls */
+	ov5642_restore_ctrls(sensor);
+
+	if (ret >= 0) {
+		sensor->current_mode = new_mode;
+		sensor->current_fr = new_frame_rate;
+
+		ov5642_dump_format(sensor);
+	}
+
+	return ret;
+}
+
+static int ov5642_restore_mode(struct ov5642_dev *sensor)
+{
+	int ret = 0;
+
+	/* first we need to set some initial register values */
+	ret = ov5642_load_regs(sensor, ov5642_initial_setting,
+				  ARRAY_SIZE(ov5642_initial_setting));
+	if (ret < 0)
+		return ret;
+
+	/* now restore the last capture mode */
+	return ov5642_change_mode(sensor,
+				  sensor->current_fr,
+				  sensor->current_fr,
+				  sensor->current_mode,
+				  ov5642_mode_VGA_640_480);
+}
+
+static int ov5642_init_mode(struct ov5642_dev *sensor,
+			    enum ov5642_frame_rate frame_rate,
+			    enum ov5642_mode mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+
+	if (mode >= ov5642_num_modes || mode < ov5642_mode_MIN) {
+		dev_err(sensor->dev, "Wrong ov5642 mode detected!\n");
+		return -EINVAL;
+	}
+
+	mode_data = ov5642_mode_info_data[frame_rate][mode].init_data_ptr;
+	mode_size = ov5642_mode_info_data[frame_rate][mode].init_data_size;
+
+	sensor->fmt.width = ov5642_mode_info_data[frame_rate][mode].width;
+	sensor->fmt.height = ov5642_mode_info_data[frame_rate][mode].height;
+
+	if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
+	    mode_data == NULL || mode_size == 0)
+		return -EINVAL;
+
+	return ov5642_load_regs(sensor, mode_data, mode_size);
+}
+
+static int ov5642_write_snapshot_para(struct ov5642_dev *sensor,
+				      enum ov5642_frame_rate frame_rate,
+				      enum ov5642_mode mode)
+{
+	bool m_60Hz = false;
+	u16 capture_frame_rate = 50;
+	u16 g_preview_frame_rate = 225;
+	u8 ret_l, ret_m, ret_h, gain, lines_10ms;
+	u16 ulcapture_exposure, capture_maxlines;
+	u16 icapture_gain, preview_maxlines;
+	u32 ulcapture_exposure_gain, g_preview_exposure;
+	int ret;
+
+	ov5642_set_agc(sensor, 0);
+
+	ret_h = ret_m = ret_l = 0;
+	g_preview_exposure = 0;
+	OV5642_READ_REG(sensor, 0x3500, &ret_h);
+	OV5642_READ_REG(sensor, 0x3501, &ret_m);
+	OV5642_READ_REG(sensor, 0x3502, &ret_l);
+	g_preview_exposure = (ret_h << 12) + (ret_m << 4) + (ret_l >> 4);
+
+	OV5642_READ_REG16(sensor, 0x380e, &preview_maxlines);
+	/*Read back AGC Gain for preview*/
+	gain = 0;
+	OV5642_READ_REG(sensor, 0x350b, &gain);
+
+	ret = ov5642_init_mode(sensor, frame_rate, mode);
+	if (ret < 0)
+		return ret;
+
+	OV5642_READ_REG16(sensor, 0x380e, &capture_maxlines);
+	if (m_60Hz)
+		lines_10ms = capture_frame_rate * (u32)capture_maxlines/12000;
+	else
+		lines_10ms = capture_frame_rate * (u32)capture_maxlines/10000;
+
+	if (preview_maxlines == 0)
+		preview_maxlines = 1;
+
+	ulcapture_exposure = (g_preview_exposure * capture_frame_rate *
+			      (u32)capture_maxlines) /
+		(preview_maxlines * g_preview_frame_rate);
+	icapture_gain = (gain & 0x0f) + 16;
+	if (gain & 0x10)
+		icapture_gain = icapture_gain << 1;
+
+	if (gain & 0x20)
+		icapture_gain = icapture_gain << 1;
+
+	if (gain & 0x40)
+		icapture_gain = icapture_gain << 1;
+
+	if (gain & 0x80)
+		icapture_gain = icapture_gain << 1;
+
+	ulcapture_exposure_gain = 2 * ulcapture_exposure * icapture_gain;
+
+	if (ulcapture_exposure_gain < (u32)capture_maxlines*16) {
+		ulcapture_exposure = ulcapture_exposure_gain/16;
+		if (ulcapture_exposure > lines_10ms) {
+			ulcapture_exposure /= lines_10ms;
+			ulcapture_exposure *= lines_10ms;
+		}
+	} else
+		ulcapture_exposure = (u32)capture_maxlines;
+
+	if (ulcapture_exposure == 0)
+		ulcapture_exposure = 1;
+
+	icapture_gain = (ulcapture_exposure_gain*2/ulcapture_exposure + 1)/2;
+	gain = 0;
+	if (icapture_gain > 31) {
+		gain |= 0x10;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 31) {
+		gain |= 0x20;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 31) {
+		gain |= 0x40;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 31) {
+		gain |= 0x80;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 16)
+		gain |= ((icapture_gain - 16) & 0x0f);
+
+	if (gain == 0x10)
+		gain = 0x11;
+
+	ov5642_set_gain(sensor, gain);
+	ov5642_set_exposure(sensor, ulcapture_exposure);
+
+	return 0;
+}
+
+static int ov5642_regulators_on(struct ov5642_dev *sensor)
+{
+	int ret;
+
+	if (sensor->io_regulator) {
+		ret = regulator_enable(sensor->io_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "io reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->core_regulator) {
+		ret = regulator_enable(sensor->core_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "core reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->gpo_regulator) {
+		ret = regulator_enable(sensor->gpo_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "gpo reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->analog_regulator) {
+		ret = regulator_enable(sensor->analog_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "analog reg enable failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void ov5642_regulators_off(struct ov5642_dev *sensor)
+{
+	if (sensor->analog_regulator)
+		regulator_disable(sensor->analog_regulator);
+	if (sensor->core_regulator)
+		regulator_disable(sensor->core_regulator);
+	if (sensor->io_regulator)
+		regulator_disable(sensor->io_regulator);
+	if (sensor->gpo_regulator)
+		regulator_disable(sensor->gpo_regulator);
+}
+
+/* --------------- Subdev Operations --------------- */
+
+static int ov5642_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	int ret;
+
+	if (on && !sensor->on) {
+		if (sensor->xclk)
+			clk_prepare_enable(sensor->xclk);
+
+		ret = ov5642_regulators_on(sensor);
+		if (ret)
+			return ret;
+
+		ov5642_power(sensor, true);
+		ret = ov5642_restore_mode(sensor);
+		if (ret)
+			return ret;
+	} else if (!on && sensor->on) {
+		ov5642_power(sensor, false);
+
+		ov5642_regulators_off(sensor);
+
+		if (sensor->xclk)
+			clk_disable_unprepare(sensor->xclk);
+	}
+
+	sensor->on = on;
+
+	return 0;
+}
+
+static int ov5642_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	cparm->capability = sensor->streamcap.capability;
+	cparm->timeperframe = sensor->streamcap.timeperframe;
+	cparm->capturemode = sensor->streamcap.capturemode;
+
+	return 0;
+}
+
+static int ov5642_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	enum ov5642_frame_rate new_frame_rate;
+	u32 tgt_fps;
+	int ret;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	/* Check that the new frame rate is allowed. */
+	if ((timeperframe->numerator == 0) ||
+	    (timeperframe->denominator == 0)) {
+		timeperframe->denominator = DEFAULT_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps > MAX_FPS) {
+		timeperframe->denominator = MAX_FPS;
+		timeperframe->numerator = 1;
+	} else if (tgt_fps < MIN_FPS) {
+		timeperframe->denominator = MIN_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	/* Actual frame rate we use */
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps == 15)
+		new_frame_rate = ov5642_15_fps;
+	else if (tgt_fps == 30)
+		new_frame_rate = ov5642_30_fps;
+	else {
+		dev_err(sensor->dev, "frame rate is not supported!\n");
+		return -EINVAL;
+	}
+
+	ret = ov5642_change_mode(
+		sensor, new_frame_rate, sensor->current_fr,
+		sensor->current_mode, sensor->current_mode);
+	if (ret < 0)
+		return ret;
+
+	sensor->streamcap.timeperframe = *timeperframe;
+
+	return 0;
+}
+
+static int ov5642_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	if (format->pad)
+		return -EINVAL;
+
+	format->format = sensor->fmt;
+
+	return 0;
+}
+
+static int ov5642_try_fmt_internal(struct v4l2_subdev *sd,
+				   struct v4l2_mbus_framefmt *fmt,
+				   enum ov5642_mode *new_mode)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	enum ov5642_mode mode;
+
+	mode = ov5642_find_nearest_mode(sensor, fmt->width, fmt->height);
+
+	fmt->width = ov5642_mode_info_data[0][mode].width;
+	fmt->height = ov5642_mode_info_data[0][mode].height;
+	fmt->code = sensor->fmt.code;
+
+	if (new_mode)
+		*new_mode = mode;
+	return 0;
+}
+
+static int ov5642_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	enum ov5642_mode new_mode;
+	int ret;
+
+	if (format->pad)
+		return -EINVAL;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		ret = ov5642_try_fmt_internal(sd, &format->format, NULL);
+		if (ret)
+			return ret;
+		cfg->try_fmt = format->format;
+		return 0;
+	}
+
+	ret = ov5642_try_fmt_internal(sd, &format->format, &new_mode);
+	if (ret)
+		return ret;
+
+	ret = ov5642_change_mode(sensor,
+				 sensor->current_fr,
+				 sensor->current_fr,
+				 new_mode, sensor->current_mode);
+	if (ret >= 0)
+		sensor->fmt = format->format;
+
+	return ret;
+}
+
+
+/*
+ * Sensor Controls.
+ */
+
+static int ov5642_set_hue(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 0, 1 << 0);
+		OV5642_MOD_REG(sensor, 0x558a, 1 << 6, 1 << 6);
+		OV5642_WRITE_REG(sensor, 0x5581, value & 0xff);
+		OV5642_WRITE_REG(sensor, 0x5582, (value & 0x100) >> 8);
+	} else
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 0, 0);
+	return 0;
+}
+
+static int ov5642_set_contrast(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 2, 1 << 2);
+		OV5642_WRITE_REG(sensor, 0x5589, value & 0xff);
+	} else
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 2, 0);
+	return 0;
+}
+
+static int ov5642_set_saturation(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 1, 1 << 1);
+		OV5642_WRITE_REG(sensor, 0x5583, value & 0xff);
+		OV5642_WRITE_REG(sensor, 0x5584, value & 0xff);
+	} else
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 1, 0);
+	return 0;
+}
+
+static int ov5642_set_awb(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	sensor->awb_on = value ? true : false;
+	OV5642_MOD_REG(sensor, 0x3406, 1 << 0, sensor->awb_on ? 0 : 1);
+	return 0;
+}
+
+static int ov5642_set_red_balance(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x3401, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x3400, (value & 0xf00) >> 8);
+	return 0;
+}
+
+#if 0
+static int ov5642_set_green_balance(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x3403, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x3402, (value & 0xf00) >> 8);
+	return 0;
+}
+#endif
+
+static int ov5642_set_blue_balance(struct ov5642_dev *sensor, int value)
+{
+	int ret = 0;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x3405, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x3404, (value & 0xf00) >> 8);
+	return 0;
+}
+
+static int ov5642_set_exposure(struct ov5642_dev *sensor, int value)
+{
+	u16 max_exp = 0;
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5642_READ_REG16(sensor, 0x350c, &max_exp);
+	if (value < max_exp) {
+		u32 exp = value << 4;
+
+		OV5642_WRITE_REG(sensor, 0x3502, exp & 0xff);
+		OV5642_WRITE_REG(sensor, 0x3501, (exp >> 8) & 0xff);
+		OV5642_WRITE_REG(sensor, 0x3500, (exp >> 16) & 0x0f);
+	}
+
+	return 0;
+}
+
+static int ov5642_set_agc(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	/* this enables/disables both AEC and AGC */
+	sensor->agc_on = value ? true : false;
+	OV5642_MOD_REG(sensor, 0x3503, 0x7, sensor->agc_on ? 0 : 0x7);
+	return 0;
+}
+
+static int ov5642_set_gain(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x350b, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x350a, (value & 0x100) >> 8);
+	return 0;
+}
+
+#if 0
+static int ov5642_set_test_pattern(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	OV5642_MOD_REG(sensor, 0x503d, 0xa4, value ? 0xa4 : 0);
+	return 0;
+}
+#endif
+
+static struct ov5642_control ov5642_ctrls[] = {
+	{
+		.set = ov5642_set_agc,
+		.ctrl = {
+			.id = V4L2_CID_AUTOGAIN,
+			.name = "Auto Gain/Exposure Control",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5642_set_exposure,
+		.ctrl = {
+			.id = V4L2_CID_EXPOSURE,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 65535,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_gain,
+		.ctrl = {
+			.id = V4L2_CID_GAIN,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 511,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_hue,
+		.ctrl = {
+			.id = V4L2_CID_HUE,
+			.name = "Hue",
+			.minimum = 0,
+			.maximum = 359,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_contrast,
+		.ctrl = {
+			.id = V4L2_CID_CONTRAST,
+			.name = "Contrast",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_saturation,
+		.ctrl = {
+			.id = V4L2_CID_SATURATION,
+			.name = "Saturation",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 64,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_awb,
+		.ctrl = {
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5642_set_red_balance,
+		.ctrl = {
+			.id = V4L2_CID_RED_BALANCE,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 1024,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_blue_balance,
+		.ctrl = {
+			.id = V4L2_CID_BLUE_BALANCE,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 1024,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	},
+};
+#define OV5642_NUM_CONTROLS ARRAY_SIZE(ov5642_ctrls)
+
+static struct ov5642_control *ov5642_get_ctrl(int id, int *index)
+{
+	struct ov5642_control *ret = NULL;
+	int i;
+
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		if (id == ov5642_ctrls[i].ctrl.id) {
+			ret = &ov5642_ctrls[i];
+			break;
+		}
+	}
+
+	if (ret && index)
+		*index = i;
+	return ret;
+}
+
+static int ov5642_restore_ctrls(struct ov5642_dev *sensor)
+{
+	struct ov5642_control *c;
+	int i;
+
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		c = &ov5642_ctrls[i];
+		c->set(sensor, sensor->ctrl_cache[i]);
+	}
+
+	return 0;
+}
+
+static int ov5642_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov5642_dev *sensor = ctrl_to_ov5642_dev(ctrl);
+	struct ov5642_control *c;
+	int ret = 0;
+	int i;
+
+	c = ov5642_get_ctrl(ctrl->id, &i);
+	if (!c)
+		return -EINVAL;
+
+	ret = c->set(sensor, ctrl->val);
+	/* update cached value if no error */
+	if (!ret)
+		sensor->ctrl_cache[i] = ctrl->val;
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5642_ctrl_ops = {
+	.s_ctrl = ov5642_s_ctrl,
+};
+
+static int ov5642_init_controls(struct ov5642_dev *sensor)
+{
+	struct ov5642_control *c;
+	int i;
+
+	v4l2_ctrl_handler_init(&sensor->ctrl_hdl, OV5642_NUM_CONTROLS);
+
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		c = &ov5642_ctrls[i];
+
+		v4l2_ctrl_new_std(&sensor->ctrl_hdl, &ov5642_ctrl_ops,
+				  c->ctrl.id, c->ctrl.minimum, c->ctrl.maximum,
+				  c->ctrl.step, c->ctrl.default_value);
+	}
+
+	sensor->sd.ctrl_handler = &sensor->ctrl_hdl;
+	if (sensor->ctrl_hdl.error) {
+		int err = sensor->ctrl_hdl.error;
+
+		v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+		v4l2_err(&sensor->sd, "%s: error %d\n", __func__, err);
+		return err;
+	}
+	v4l2_ctrl_handler_setup(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static int ov5642_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->pad)
+		return -EINVAL;
+	if (fse->index >= ov5642_num_modes)
+		return -EINVAL;
+
+	fse->min_width = fse->max_width =
+		ov5642_mode_info_data[0][fse->index].width;
+	fse->min_height = fse->max_height =
+		ov5642_mode_info_data[0][fse->index].height;
+
+	return 0;
+}
+
+static int ov5642_enum_frame_interval(
+	struct v4l2_subdev *sd,
+	struct v4l2_subdev_pad_config *cfg,
+	struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	enum ov5642_mode mode;
+
+	if (fie->pad)
+		return -EINVAL;
+	if (fie->index < 0 || fie->index >= ov5642_num_framerates)
+		return -EINVAL;
+
+	if (fie->width == 0 || fie->height == 0)
+		return -EINVAL;
+
+	mode = ov5642_find_nearest_mode(sensor, fie->width, fie->height);
+
+	if (ov5642_mode_info_data[fie->index][mode].init_data_ptr == NULL)
+		return -EINVAL;
+
+	fie->interval.numerator = 1;
+	fie->interval.denominator = ov5642_framerates[fie->index];
+
+	dev_dbg(sensor->dev, "%dx%d: [%d] = %d fps\n",
+		fie->width, fie->height, fie->index, fie->interval.denominator);
+	return 0;
+}
+
+static int ov5642_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	*status = !sensor->on ? V4L2_IN_ST_NO_POWER : 0;
+
+	return 0;
+}
+
+static int ov5642_s_routing(struct v4l2_subdev *sd, u32 input,
+			    u32 output, u32 config)
+{
+	return (input != 0) ? -EINVAL : 0;
+}
+
+static int ov5642_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	if (code->pad)
+		return -EINVAL;
+	if (code->index != 0)
+		return -EINVAL;
+
+	code->code = sensor->fmt.code;
+
+	return 0;
+}
+
+static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = sensor->ep.bus.parallel.flags;
+
+	return 0;
+}
+
+static int ov5642_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops ov5642_core_ops = {
+	.s_power = ov5642_s_power,
+	.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,
+};
+
+static struct v4l2_subdev_video_ops ov5642_video_ops = {
+	.s_parm = ov5642_s_parm,
+	.g_parm = ov5642_g_parm,
+	.g_input_status = ov5642_g_input_status,
+	.s_routing = ov5642_s_routing,
+	.g_mbus_config  = ov5642_g_mbus_config,
+	.s_stream = ov5642_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5642_pad_ops = {
+	.enum_mbus_code = ov5642_enum_mbus_code,
+	.get_fmt = ov5642_get_fmt,
+	.set_fmt = ov5642_set_fmt,
+	.enum_frame_size = ov5642_enum_frame_size,
+	.enum_frame_interval = ov5642_enum_frame_interval,
+};
+
+static struct v4l2_subdev_ops ov5642_subdev_ops = {
+	.core = &ov5642_core_ops,
+	.video = &ov5642_video_ops,
+	.pad = &ov5642_pad_ops,
+};
+
+static void ov5642_power(struct ov5642_dev *sensor, bool enable)
+{
+	gpio_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
+}
+
+#if 0
+/* we never need to reset the chip, but keep around */
+static void ov5642_reset(struct ov5642_dev *sensor)
+{
+	gpio_set_value(sensor->reset_gpio, 0);
+	usleep_range(1000, 1001);
+	gpio_set_value(sensor->reset_gpio, 1);
+}
+#endif
+
+static void ov5642_get_regulators(struct ov5642_dev *sensor)
+{
+	sensor->io_regulator = devm_regulator_get(sensor->dev, "DOVDD");
+	if (!IS_ERR(sensor->io_regulator)) {
+		regulator_set_voltage(sensor->io_regulator,
+				      OV5642_VOLTAGE_DIGITAL_IO,
+				      OV5642_VOLTAGE_DIGITAL_IO);
+	} else {
+		dev_dbg(sensor->dev, "%s: no io voltage reg found\n",
+			__func__);
+		sensor->io_regulator = NULL;
+	}
+
+	sensor->core_regulator = devm_regulator_get(sensor->dev, "DVDD");
+	if (!IS_ERR(sensor->core_regulator)) {
+		regulator_set_voltage(sensor->core_regulator,
+				      OV5642_VOLTAGE_DIGITAL_CORE,
+				      OV5642_VOLTAGE_DIGITAL_CORE);
+	} else {
+		sensor->core_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no core voltage reg found\n",
+			__func__);
+	}
+
+	sensor->analog_regulator = devm_regulator_get(sensor->dev, "AVDD");
+	if (!IS_ERR(sensor->analog_regulator)) {
+		regulator_set_voltage(sensor->analog_regulator,
+				      OV5642_VOLTAGE_ANALOG,
+				      OV5642_VOLTAGE_ANALOG);
+	} else {
+		sensor->analog_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no analog voltage reg found\n",
+			__func__);
+	}
+}
+
+/*!
+ * ov5642 I2C probe function
+ *
+ * @param adapter            struct i2c_adapter *
+ * @return  Error code indicating success or failure
+ */
+static int ov5642_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *endpoint;
+	struct ov5642_dev *sensor;
+	int i, xclk, ret;
+
+	sensor = devm_kzalloc(dev, sizeof(struct ov5642_dev),
+			      GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->i2c_client = client;
+	sensor->dev = dev;
+	sensor->fmt.code = MEDIA_BUS_FMT_YUYV8_2X8;
+	sensor->fmt.width = 640;
+	sensor->fmt.height = 480;
+	sensor->fmt.field = V4L2_FIELD_NONE;
+	sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY |
+					   V4L2_CAP_TIMEPERFRAME;
+	sensor->streamcap.capturemode = 0;
+	sensor->streamcap.timeperframe.denominator = DEFAULT_FPS;
+	sensor->streamcap.timeperframe.numerator = 1;
+
+	sensor->current_mode = ov5642_mode_VGA_640_480;
+	sensor->current_fr = ov5642_30_fps;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	v4l2_of_parse_endpoint(endpoint, &sensor->ep);
+	if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL) {
+		dev_err(dev, "invalid bus type, must be parallel\n");
+		return -EINVAL;
+	}
+	of_node_put(endpoint);
+
+	/* get system clock (xclk) frequency */
+	ret = of_property_read_u32(dev->of_node, "xclk", &xclk);
+	if (!ret) {
+		if (xclk < OV5642_XCLK_MIN || xclk > OV5642_XCLK_MAX) {
+			dev_err(dev, "invalid xclk frequency\n");
+			return -EINVAL;
+		}
+		sensor->xclk_freq = xclk;
+	}
+
+	/* get system clock (xclk) */
+	sensor->xclk = devm_clk_get(dev, "xclk");
+	if (!IS_ERR(sensor->xclk)) {
+		if (!sensor->xclk_freq) {
+			dev_err(dev,
+				"xclk requires xclk frequency!\n");
+			return -EINVAL;
+		}
+		clk_set_rate(sensor->xclk, sensor->xclk_freq);
+	} else
+		sensor->xclk = NULL;
+
+	ret = of_get_named_gpio(dev->of_node, "pwdn-gpios", 0);
+	if (!gpio_is_valid(ret)) {
+		dev_err(dev, "no sensor pwdn pin available");
+		return ret;
+	}
+	sensor->pwdn_gpio = ret;
+	ret = devm_gpio_request_one(dev, sensor->pwdn_gpio,
+				    GPIOF_OUT_INIT_HIGH, "ov5642_pwdn");
+	if (ret < 0) {
+		dev_err(dev, "request for power down gpio failed\n");
+		return ret;
+	}
+
+	ret = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
+	if (!gpio_is_valid(ret)) {
+		dev_err(dev, "no reset pin available");
+		return ret;
+	}
+	sensor->reset_gpio = ret;
+	ret = devm_gpio_request_one(dev, sensor->reset_gpio,
+				    GPIOF_OUT_INIT_HIGH, "ov5642_reset");
+	if (ret < 0) {
+		dev_err(dev, "request for reset gpio failed\n");
+		return ret;
+	}
+
+	/*
+	 * No idea what this "gp" gpio to the sensor is used for,
+	 * but acquire it and set it high.
+	 */
+	ret = of_get_named_gpio(dev->of_node, "gp-gpios", 0);
+	if (gpio_is_valid(ret)) {
+		sensor->gp_gpio = ret;
+		ret = devm_gpio_request_one(dev, sensor->gp_gpio,
+					    GPIOF_OUT_INIT_HIGH, "ov5642_gp");
+		if (ret < 0) {
+			dev_err(dev, "request for gp gpio failed\n");
+			return ret;
+		}
+		gpio_set_value(sensor->gp_gpio, 1);
+	}
+
+	dev_info(dev, "gpios: reset %d, power-down %d, gp %d\n",
+		 sensor->reset_gpio, sensor->pwdn_gpio, sensor->gp_gpio);
+
+	/* initialize the cached controls to their defaults */
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		struct ov5642_control *c = &ov5642_ctrls[i];
+
+		sensor->ctrl_cache[i] = c->ctrl.default_value;
+	}
+	sensor->awb_on = sensor->agc_on = true;
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &ov5642_subdev_ops);
+
+	ov5642_get_regulators(sensor);
+
+	ret = ov5642_s_power(&sensor->sd, 1);
+	if (ret)
+		goto reg_off;
+	ret = ov5642_init_controls(sensor);
+	if (ret)
+		goto reg_off;
+	ret = ov5642_s_power(&sensor->sd, 0);
+	if (ret)
+		goto free_ctrls;
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret)
+		goto free_ctrls;
+
+	return 0;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+reg_off:
+	ov5642_regulators_off(sensor);
+	return ret;
+}
+
+/*!
+ * ov5642 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  Error code indicating success or failure
+ */
+static int ov5642_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	ov5642_regulators_off(sensor);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov5642_id[] = {
+	{ "ov5642", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ov5642_id);
+
+static const struct of_device_id ov5642_dt_ids[] = {
+	{ .compatible = "ovti,ov5642" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov5642_dt_ids);
+
+static struct i2c_driver ov5642_driver = {
+	.driver = {
+		.name	= "ov5642",
+		.owner	= THIS_MODULE,
+		.of_match_table	= ov5642_dt_ids,
+	},
+	.id_table	= ov5642_id,
+	.probe		= ov5642_probe,
+	.remove		= ov5642_remove,
+};
+
+module_i2c_driver(ov5642_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5642 Camera Subdev Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
-- 
1.9.1


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

* [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (32 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 33/38] media: imx: Add support for Parallel OV5642 Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-16 11:33   ` Lars-Peter Clausen
  2016-06-14 22:49 ` [PATCH 35/38] media: adv7180: add power pin control Steve Longerbeam
                   ` (6 subsequent siblings)
  40 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This driver is based on adv7180.c from Freescale imx_3.10.17_1.0.0_beta
branch, modified heavily for code cleanup and converted from int-device
to subdev.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig   |    7 +
 drivers/staging/media/imx/capture/Makefile  |    1 +
 drivers/staging/media/imx/capture/adv7180.c | 1533 +++++++++++++++++++++++++++
 include/uapi/linux/v4l2-controls.h          |    4 +
 include/uapi/media/Kbuild                   |    1 +
 include/uapi/media/adv718x.h                |   42 +
 6 files changed, 1588 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/adv7180.c
 create mode 100644 include/uapi/media/adv718x.h

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index 42d87db..6897192 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -32,4 +32,11 @@ config IMX_CAMERA_OV5640_MIPI
        ---help---
          MIPI CSI-2 OV5640 Camera support.
 
+config IMX_CAMERA_ADV7180
+       tristate "Analog Devices ADV7180 Video Decoder support"
+       depends on VIDEO_IMX_CAMERA
+       default y
+       ---help---
+         Analog Devices ADV7180 Video Decoder support.
+
 endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index 07633be..c8f891a 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
 obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
 obj-$(CONFIG_IMX_CAMERA_OV5640_MIPI) += ov5640-mipi.o
 obj-$(CONFIG_IMX_CAMERA_OV5642) += ov5642.o
+obj-$(CONFIG_IMX_CAMERA_ADV7180) += adv7180.o
diff --git a/drivers/staging/media/imx/capture/adv7180.c b/drivers/staging/media/imx/capture/adv7180.c
new file mode 100644
index 0000000..297c543
--- /dev/null
+++ b/drivers/staging/media/imx/capture/adv7180.c
@@ -0,0 +1,1533 @@
+/*
+ * Analog Device ADV7180 video decoder driver
+ *
+ * Copyright (c) 2012-2014 Mentor Graphics Inc.
+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/semaphore.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/wait.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/adv718x.h>
+
+struct adv7180_dev {
+	struct i2c_client *i2c_client;
+	struct device *dev;
+	struct v4l2_subdev sd;
+	struct v4l2_of_endpoint ep; /* the parsed DT endpoint info */
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_mbus_framefmt fmt;
+	struct v4l2_captureparm streamcap;
+	int rev_id;
+	bool on;
+
+	bool locked;             /* locked to signal */
+
+	struct regulator *dvddio;
+	struct regulator *dvdd;
+	struct regulator *avdd;
+	struct regulator *pvdd;
+	int pwdn_gpio;
+
+	v4l2_std_id std_id;
+
+	/* Standard index of ADV7180. */
+	int video_idx;
+
+	/* Current analog input mux */
+	int current_input;
+
+	struct mutex mutex;
+};
+
+static inline struct adv7180_dev *to_adv7180_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct adv7180_dev, sd);
+}
+
+static inline struct adv7180_dev *ctrl_to_adv7180_dev(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct adv7180_dev, ctrl_hdl);
+}
+
+/*! List of input video formats supported. The video formats is corresponding
+ * with v4l2 id in video_fmt_t
+ */
+enum {
+	ADV7180_NTSC = 0,	/*!< Locked on (M) NTSC video signal. */
+	ADV7180_PAL,		/*!< (B, G, H, I, N)PAL video signal. */
+};
+
+/*! Number of video standards supported (including 'not locked' signal). */
+#define ADV7180_STD_MAX		(ADV7180_PAL + 1)
+
+/* video standard info */
+struct adv7180_std_info {
+	unsigned long width;
+	unsigned long height;
+	struct v4l2_standard std;
+};
+
+static struct adv7180_std_info adv7180_std[] = {
+	[ADV7180_NTSC] = {
+		.width = 720,
+		.height = 480,
+		.std = {
+			.id = V4L2_STD_NTSC,
+			.name = "NTSC",
+			.framelines = 525,
+			.frameperiod = {
+				.numerator = 1001,
+				.denominator = 30000,
+			},
+		},
+	},
+	[ADV7180_PAL] = {
+		.width = 720,
+		.height = 576,
+		.std = {
+			.id = V4L2_STD_PAL,
+			.name = "PAL",
+			.framelines = 625,
+			.frameperiod = {
+				.numerator = 1,
+				.denominator = 25,
+			},
+		},
+	},
+};
+
+#define IF_NAME                    "adv7180"
+
+/* Main Register Map */
+#define ADV7180_INPUT_CTL              0x00	/* Input Control */
+#define ADV7180_EXT_OUT_CTL            0x04
+#define ADV7180_STATUS_1               0x10	/* Status #1 */
+#define   ADV7180_IN_LOCK              (1 << 0)
+#define   ADV7180_LOST_LOCK            (1 << 1)
+#define   ADV7180_FSC_LOCK             (1 << 2)
+#define   ADV7180_AD_RESULT_BIT        4
+#define   ADV7180_AD_RESULT_MASK       (0x7 << ADV7180_AD_RESULT_BIT)
+#define   ADV7180_AD_NTSC              0
+#define   ADV7180_AD_NTSC_4_43         1
+#define   ADV7180_AD_PAL_M             2
+#define   ADV7180_AD_PAL_60            3
+#define   ADV7180_AD_PAL               4
+#define   ADV7180_AD_SECAM             5
+#define   ADV7180_AD_PAL_N             6
+#define   ADV7180_AD_SECAM_525         7
+#define ADV7180_CONTRAST               0x08	/* Contrast */
+#define ADV7180_BRIGHTNESS             0x0a	/* Brightness */
+#define ADV7180_HUE_REG                0x0b	/* Signed, inverted */
+#define ADV7180_DEF_Y                  0x0c
+#define   ADV7180_DEF_VAL_EN           (1 << 0)
+#define   ADV7180_DEF_VAL_AUTO_EN      (1 << 1)
+#define   ADV7180_DEF_Y_BIT            2
+#define   ADV7180_DEF_Y_MASK           (0x3f << ADV7180_DEF_Y_BIT)
+#define ADV7180_DEF_C                  0x0d
+#define   ADV7180_DEF_CB_BIT           0
+#define   ADV7180_DEF_CB_MASK          (0xf << ADV7180_DEF_CB_BIT)
+#define   ADV7180_DEF_CR_BIT           4
+#define   ADV7180_DEF_CR_MASK          (0xf << ADV7180_DEF_CR_BIT)
+#define ADV7180_ADI_CTL_1              0x0e
+#define ADV7180_PWR_MNG                0x0f     /* Power Management */
+#define ADV7180_IDENT                  0x11	/* IDENT */
+#define ADV7180_ANALOG_CLAMP_CTL       0x14
+#define ADV7180_SHAP_FILTER_CTL_1      0x17
+#define ADV7180_VSYNC_FIELD_CTL_1      0x31	/* VSYNC Field Control #1 */
+#define ADV7180_MANUAL_WIN_CTL_1       0x3d	/* Manual Window Control 1 */
+#define ADV7180_MANUAL_WIN_CTL_2       0x3e     /* Manual Window Control 2 */
+#define ADV7180_MANUAL_WIN_CTL_3       0x3f     /* Manual Window Control 3 */
+#define ADV7180_CTI_DNR                0x4d
+#define   ADV7180_CTI_EN               (1 << 0) /* CTI enable */
+#define   ADV7180_CTI_AB_EN            (1 << 1) /* CTI Alpha Blend enable */
+#define   ADV7180_CTI_AB_BIT           2 /* CTI Alpha Blend sharpness */
+#define   ADV7180_CTI_AB_MASK          (0x3 << ADV7180_CTI_AB_BIT)
+#define   ADV7180_DNR_EN               (1 << 5) /* DNR enable */
+#define ADV7180_CTI_THRESH             0x4e /* CTI Chroma Amplitude Threshold */
+#define ADV7180_DNR_THRESH1            0x50
+#define ADV7180_LOCK_COUNT             0x51
+#define ADV7180_CVBS_TRIM              0x52
+#define ADV7180_SD_OFFSET_CB           0xe1	/* SD Offset Cb */
+#define ADV7180_SD_OFFSET_CR           0xe2	/* SD Offset Cr */
+#define ADV7180_SD_SATURATION_CB       0xe3	/* SD Saturation Cb */
+#define ADV7180_SD_SATURATION_CR       0xe4	/* SD Saturation Cr */
+#define ADV7180_DRIVE_STRENGTH         0xf4
+#define ADV7180_LUMA_PEAK_GAIN         0xfb
+#define ADV7180_DNR_THRESH2            0xfc
+/* Interrupt Register Map */
+#define ADV7180_INT_CONFIG_1           0x40     /* Interrupt Config 1 */
+#define ADV7180_INT_STATUS_1           0x42     /* Interrupt Status 1 (r/o) */
+#define   ADV7180_INT_SD_LOCK          (1 << 0)
+#define   ADV7180_INT_SD_UNLOCK        (1 << 1)
+#define ADV7180_INT_CLEAR_1            0x43     /* Interrupt Clear 1 (w/o) */
+#define ADV7180_INT_MASK_1             0x44     /* Interrupt Mask 1 */
+#define ADV7180_INT_RAW_STATUS_2       0x45   /* Interrupt Raw Status 2 (r/o) */
+#define  ADV7180_INT_SD_FIELD          (1 << 4)
+#define ADV7180_INT_STATUS_2           0x46     /* Interrupt Status 2 (r/o) */
+#define  ADV7180_INT_SD_FIELD_CHNG     (1 << 4)
+#define ADV7180_INT_CLEAR_2            0x47     /* Interrupt Clear 2 (w/o) */
+#define ADV7180_INT_MASK_2             0x48     /* Interrupt Mask 2 */
+#define ADV7180_INT_RAW_STATUS_3       0x49   /* Interrupt Raw Status 3 (r/o) */
+#define   ADV7180_INT_SD_V_LOCK        (1 << 1)
+#define ADV7180_INT_STATUS_3           0x4a   /* Interrupt Status 3 (r/o) */
+#define   ADV7180_INT_SD_V_LOCK_CHNG   (1 << 1)
+#define   ADV7180_INT_SD_H_LOCK_CHNG   (1 << 2)
+#define   ADV7180_INT_SD_AD_CHNG       (1 << 3)
+#define ADV7180_INT_CLEAR_3            0x4b     /* Interrupt Clear 3 (w/o) */
+#define ADV7180_INT_MASK_3             0x4c     /* Interrupt Mask 3 */
+
+struct adv7180_inputs_t {
+	const char *desc;   /* Analog input description */
+	u8 insel;           /* insel bits to select this input */
+};
+
+/* Analog Inputs on 64-Lead and 48-Lead LQFP */
+static const struct adv7180_inputs_t adv7180_inputs_64_48[] = {
+	{ .insel = 0x00, .desc = "ADV7180 Composite on Ain1" },
+	{ .insel = 0x01, .desc = "ADV7180 Composite on Ain2" },
+	{ .insel = 0x02, .desc = "ADV7180 Composite on Ain3" },
+	{ .insel = 0x03, .desc = "ADV7180 Composite on Ain4" },
+	{ .insel = 0x04, .desc = "ADV7180 Composite on Ain5" },
+	{ .insel = 0x05, .desc = "ADV7180 Composite on Ain6" },
+	{ .insel = 0x06, .desc = "ADV7180 Y/C on Ain1/4" },
+	{ .insel = 0x07, .desc = "ADV7180 Y/C on Ain2/5" },
+	{ .insel = 0x08, .desc = "ADV7180 Y/C on Ain3/6" },
+	{ .insel = 0x09, .desc = "ADV7180 YPbPr on Ain1/4/5" },
+	{ .insel = 0x0a, .desc = "ADV7180 YPbPr on Ain2/3/6" },
+};
+
+#define NUM_INPUTS_64_48 ARRAY_SIZE(adv7180_inputs_64_48)
+
+#if 0
+/*
+ * FIXME: there is no way to distinguish LQFP vs LFCSP chips, so
+ * we will just have to assume LQFP.
+ */
+/* Analog Inputs on 40-Lead and 32-Lead LFCSP */
+static const struct adv7180_inputs_t adv7180_inputs_40_32[] = {
+	{ .insel = 0x00, .desc = "ADV7180 Composite on Ain1" },
+	{ .insel = 0x03, .desc = "ADV7180 Composite on Ain2" },
+	{ .insel = 0x04, .desc = "ADV7180 Composite on Ain3" },
+	{ .insel = 0x06, .desc = "ADV7180 Y/C on Ain1/2" },
+	{ .insel = 0x09, .desc = "ADV7180 YPbPr on Ain1/2/3" },
+};
+
+#define NUM_INPUTS_40_32 ARRAY_SIZE(adv7180_inputs_40_32)
+#endif
+
+struct adv7180_reg_tbl_t {
+	u8 reg;
+	u8 val;
+};
+
+/*
+ * this is a special reg value inserted into a register table
+ * indicating a delay is required in msec given in val
+ */
+#define ADV_DELAY  255
+
+/*
+ * This is the original register set from Freescale's adv7180
+ * driver. Most of these registers and their values are undocumented,
+ * and do not conform with Analog Device's Register Settings
+ * Recommendations document for CVBS autodetect mode.
+ * Later edit: This table has been stripped of the entries specifying
+ * the same value as the register default value after reset (according
+ * to ADV7180 datasheet rev J), in order to decrease the device
+ * initialization time.
+ */
+static const struct adv7180_reg_tbl_t adv7180_fsl_init_reg[] = {
+	{ 0x02, 0x04 }, { 0x03, 0x00 }, { 0x04, 0x45 }, { 0x05, 0x00 },
+	{ 0x06, 0x02 }, { 0x13, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+	{ 0xF1, 0x19 }, { 0x1A, 0x00 }, { 0x1B, 0x00 }, { 0x1C, 0x00 },
+	{ 0x1D, 0x40 }, { 0x1E, 0x00 }, { 0x1F, 0x00 }, { 0x20, 0x00 },
+	{ 0x21, 0x00 }, { 0x22, 0x00 }, { 0x23, 0xC0 }, { 0x24, 0x00 },
+	{ 0x25, 0x00 }, { 0x26, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x00 },
+	{ 0x2A, 0x00 }, { 0x2D, 0xF4 }, { 0x2E, 0x00 }, { 0x2F, 0xF0 },
+	{ 0x30, 0x00 }, { 0x31, 0x02 }, { 0x3B, 0x05 }, { 0x3C, 0x58 },
+	{ 0x3E, 0x64 }, { 0x3F, 0xE4 }, { 0x40, 0x90 }, { 0x42, 0x7E },
+	{ 0x43, 0xA4 }, { 0x44, 0xFF }, { 0x45, 0xB6 }, { 0x46, 0x12 },
+	{ 0x4F, 0x08 }, { 0x51, 0xA4 }, { 0x53, 0x4E }, { 0x54, 0x80 },
+	{ 0x55, 0x00 }, { 0x56, 0x10 }, { 0x57, 0x00 }, { 0x5A, 0x00 },
+	{ 0x5B, 0x00 }, { 0x5C, 0x00 }, { 0x5D, 0x00 }, { 0x5E, 0x00 },
+	{ 0x5F, 0x00 }, { 0x60, 0x00 }, { 0x61, 0x00 }, { 0x62, 0x20 },
+	{ 0x63, 0x00 }, { 0x64, 0x00 }, { 0x65, 0x00 }, { 0x66, 0x00 },
+	{ 0x67, 0x03 }, { 0x68, 0x01 }, { 0x69, 0x00 }, { 0x6A, 0x00 },
+	{ 0x6B, 0xC0 }, { 0x6C, 0x00 }, { 0x6D, 0x00 }, { 0x6E, 0x00 },
+	{ 0x6F, 0x00 }, { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 },
+	{ 0x73, 0x10 }, { 0x74, 0x04 }, { 0x75, 0x01 }, { 0x76, 0x00 },
+	{ 0x77, 0x3F }, { 0x78, 0xFF }, { 0x79, 0xFF }, { 0x7A, 0xFF },
+	{ 0x7B, 0x1E }, { 0x7C, 0xC0 }, { 0x7D, 0x00 }, { 0x7E, 0x00 },
+	{ 0x7F, 0x00 }, { 0x80, 0x00 }, { 0x81, 0xC0 }, { 0x82, 0x04 },
+	{ 0x83, 0x00 }, { 0x84, 0x0C }, { 0x85, 0x02 }, { 0x86, 0x03 },
+	{ 0x87, 0x63 }, { 0x88, 0x5A }, { 0x89, 0x08 }, { 0x8A, 0x10 },
+	{ 0x8B, 0x00 }, { 0x8C, 0x40 }, { 0x8D, 0x00 }, { 0x8E, 0x40 },
+	{ 0x90, 0x00 }, { 0x91, 0x50 }, { 0x92, 0x00 }, { 0x93, 0x00 },
+	{ 0x94, 0x00 }, { 0x95, 0x00 }, { 0x96, 0x00 }, { 0x97, 0xF0 },
+	{ 0x98, 0x00 }, { 0x99, 0x00 }, { 0x9A, 0x00 }, { 0x9B, 0x00 },
+	{ 0x9C, 0x00 }, { 0x9D, 0x00 }, { 0x9E, 0x00 }, { 0x9F, 0x00 },
+	{ 0xA0, 0x00 }, { 0xA1, 0x00 }, { 0xA2, 0x00 }, { 0xA3, 0x00 },
+	{ 0xA4, 0x00 }, { 0xA5, 0x00 }, { 0xA6, 0x00 }, { 0xA7, 0x00 },
+	{ 0xA8, 0x00 }, { 0xA9, 0x00 }, { 0xAA, 0x00 }, { 0xAB, 0x00 },
+	{ 0xAC, 0x00 }, { 0xAD, 0x00 }, { 0xAE, 0x60 }, { 0xAF, 0x00 },
+	{ 0xB0, 0x00 }, { 0xB1, 0x60 }, { 0xB3, 0x54 }, { 0xB4, 0x00 },
+	{ 0xB5, 0x00 }, { 0xB6, 0x00 }, { 0xB7, 0x13 }, { 0xB8, 0x03 },
+	{ 0xB9, 0x33 }, { 0xBF, 0x02 }, { 0xC0, 0x00 }, { 0xC1, 0x00 },
+	{ 0xC2, 0x00 }, { 0xC4, 0x00 }, { 0xC5, 0x81 }, { 0xC6, 0x00 },
+	{ 0xC7, 0x00 }, { 0xC8, 0x00 }, { 0xC9, 0x04 }, { 0xCC, 0x69 },
+	{ 0xCD, 0x00 }, { 0xCE, 0x01 }, { 0xCF, 0xB4 }, { 0xD0, 0x00 },
+	{ 0xD1, 0x10 }, { 0xD2, 0xFF }, { 0xD3, 0xFF }, { 0xD4, 0x7F },
+	{ 0xD5, 0x7F }, { 0xD6, 0x3E }, { 0xD7, 0x08 }, { 0xD8, 0x3C },
+	{ 0xD9, 0x08 }, { 0xDA, 0x3C }, { 0xDB, 0x9B }, { 0xDE, 0x00 },
+	{ 0xDF, 0x00 }, { 0xE0, 0x14 }, { 0xEE, 0x00 }, { 0xEF, 0x4A },
+	{ 0xF0, 0x44 }, { 0xF1, 0x0C }, { 0xF2, 0x32 }, { 0xF4, 0x3F },
+	{ 0xF5, 0xE0 }, { 0xF6, 0x69 }, { 0xF7, 0x10 }, { 0xFA, 0xFA }
+};
+
+/*
+ * This is Analog Device's Register Settings Recommendation for CVBS
+ * autodetect mode. It is here for future reference only, we don't use
+ * it yet because autodetect doesn't work very well when the chip is
+ * initialized with this set.
+ */
+static const struct adv7180_reg_tbl_t __maybe_unused
+adv7180_cvbs_autodetect[] = {
+	/* Set analog mux for Composite Ain1 */
+	{ ADV7180_INPUT_CTL, 0x00 },
+	/* ADI Required Write: Reset Clamp Circuitry */
+	{ ADV7180_ANALOG_CLAMP_CTL, 0x30 },
+	/* Enable SFL Output */
+	{ ADV7180_EXT_OUT_CTL, 0x57 },
+	/* Select SH1 Chroma Shaping Filter */
+	{ ADV7180_SHAP_FILTER_CTL_1, 0x41 },
+	/* Enable NEWAVMODE */
+	{ ADV7180_VSYNC_FIELD_CTL_1, 0x02 },
+	/* ADI Required Write: optimize windowing function Step 1,2,3 */
+	{ ADV7180_MANUAL_WIN_CTL_1, 0xA2 },
+	{ ADV7180_MANUAL_WIN_CTL_2, 0x6A },
+	{ ADV7180_MANUAL_WIN_CTL_3, 0xA0 },
+	/* ADI Required Write: Enable ADC step 1,2,3 */
+	{ ADV7180_ADI_CTL_1, 0x80 }, /* undocumented bit 7 */
+	{ 0x55, 0x81 },              /* undocumented register 0x55 */
+	{ ADV7180_ADI_CTL_1, 0x00 },
+	/* Recommended AFE I BIAS Setting for CVBS mode */
+	{ ADV7180_CVBS_TRIM, 0x0D },
+};
+
+#define ADV7180_VOLTAGE_ANALOG               1800000
+#define ADV7180_VOLTAGE_DIGITAL_CORE         1800000
+#define ADV7180_VOLTAGE_DIGITAL_IO           3300000
+#define ADV7180_VOLTAGE_PLL                  1800000
+
+static int adv7180_regulator_enable(struct adv7180_dev *sensor)
+{
+	struct device *dev = sensor->dev;
+	int ret = 0;
+
+	sensor->dvddio = devm_regulator_get(dev, "DOVDD");
+	if (!IS_ERR(sensor->dvddio)) {
+		regulator_set_voltage(sensor->dvddio,
+				      ADV7180_VOLTAGE_DIGITAL_IO,
+				      ADV7180_VOLTAGE_DIGITAL_IO);
+		ret = regulator_enable(sensor->dvddio);
+		if (ret) {
+			v4l2_err(&sensor->sd, "set io voltage failed\n");
+			return ret;
+		}
+	} else {
+		v4l2_warn(&sensor->sd, "cannot get io voltage\n");
+	}
+
+	sensor->dvdd = devm_regulator_get(dev, "DVDD");
+	if (!IS_ERR(sensor->dvdd)) {
+		regulator_set_voltage(sensor->dvdd,
+				      ADV7180_VOLTAGE_DIGITAL_CORE,
+				      ADV7180_VOLTAGE_DIGITAL_CORE);
+		ret = regulator_enable(sensor->dvdd);
+		if (ret) {
+			v4l2_err(&sensor->sd, "set core voltage failed\n");
+			return ret;
+		}
+	} else {
+		v4l2_warn(&sensor->sd, "cannot get core voltage\n");
+	}
+
+	sensor->avdd = devm_regulator_get(dev, "AVDD");
+	if (!IS_ERR(sensor->avdd)) {
+		regulator_set_voltage(sensor->avdd,
+				      ADV7180_VOLTAGE_ANALOG,
+				      ADV7180_VOLTAGE_ANALOG);
+		ret = regulator_enable(sensor->avdd);
+		if (ret) {
+			v4l2_err(&sensor->sd, "set analog voltage failed\n");
+			return ret;
+		}
+	} else {
+		v4l2_warn(&sensor->sd, "cannot get analog voltage\n");
+	}
+
+	sensor->pvdd = devm_regulator_get(dev, "PVDD");
+	if (!IS_ERR(sensor->pvdd)) {
+		regulator_set_voltage(sensor->pvdd,
+				      ADV7180_VOLTAGE_PLL,
+				      ADV7180_VOLTAGE_PLL);
+		ret = regulator_enable(sensor->pvdd);
+		if (ret) {
+			v4l2_err(&sensor->sd, "set pll voltage failed\n");
+			return ret;
+		}
+	} else {
+		v4l2_warn(&sensor->sd, "cannot get pll voltage\n");
+	}
+
+	return ret;
+}
+
+static void adv7180_regulator_disable(struct adv7180_dev *sensor)
+{
+	if (sensor->dvddio)
+		regulator_disable(sensor->dvddio);
+
+	if (sensor->dvdd)
+		regulator_disable(sensor->dvdd);
+
+	if (sensor->avdd)
+		regulator_disable(sensor->avdd);
+
+	if (sensor->pvdd)
+		regulator_disable(sensor->pvdd);
+}
+
+/***********************************************************************
+ * I2C transfer.
+ ***********************************************************************/
+
+/*! Read one register from a ADV7180 i2c slave device.
+ *
+ *  @param *reg		register in the device we wish to access.
+ *
+ *  @return		       0 if success, an error code otherwise.
+ */
+static int adv7180_read_reg(struct adv7180_dev *sensor, u8 reg, u8 *val)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(sensor->i2c_client, reg);
+	if (ret < 0) {
+		v4l2_err(&sensor->sd, "%s: read reg error: reg=%2x\n",
+			 __func__, reg);
+		return ret;
+	}
+
+	*val = ret;
+	return 0;
+}
+
+/*! Write one register of a ADV7180 i2c slave device.
+ *
+ *  @param *reg		register in the device we wish to access.
+ *
+ *  @return		       0 if success, an error code otherwise.
+ */
+static int adv7180_write_reg(struct adv7180_dev *sensor, u8 reg, u8 val)
+{
+	int ret = i2c_smbus_write_byte_data(sensor->i2c_client, reg, val);
+
+	if (ret < 0)
+		v4l2_err(&sensor->sd, "%s: write reg error:reg=%2x,val=%2x\n",
+			 __func__, reg, val);
+	return ret;
+}
+
+#define ADV7180_READ_REG(s, r, v) {				\
+		ret = adv7180_read_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+#define ADV7180_WRITE_REG(s, r, v) {				\
+		ret = adv7180_write_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int adv7180_load_reg_tbl(struct adv7180_dev *sensor,
+				const struct adv7180_reg_tbl_t *tbl, int n)
+{
+	int ret, i;
+
+	for (i = 0; i < n; i++) {
+		if (tbl[i].reg == ADV_DELAY) {
+			unsigned long usec = (unsigned long)tbl[i].val * 1000;
+
+			usleep_range(usec, usec + 1);
+			continue;
+		}
+
+		ADV7180_WRITE_REG(sensor, tbl[i].reg, tbl[i].val);
+	}
+
+	return 0;
+}
+
+/* Read AD_RESULT to get the autodetected video standard */
+static int adv7180_get_autodetect_std(struct adv7180_dev *sensor,
+				      u8 stat1, bool *status_change)
+{
+	int ad_result, idx = ADV7180_PAL;
+	v4l2_std_id std = V4L2_STD_PAL;
+
+	*status_change = false;
+
+	/*
+	 * When the chip loses lock, it continues to send data at whatever
+	 * standard was detected before, so leave the standard at the last
+	 * detected standard.
+	 */
+	if (!sensor->locked)
+		return 0; /* no status change */
+
+	ad_result = (stat1 & ADV7180_AD_RESULT_MASK) >> ADV7180_AD_RESULT_BIT;
+
+	switch (ad_result) {
+	case ADV7180_AD_PAL:
+		std = V4L2_STD_PAL;
+		idx = ADV7180_PAL;
+		break;
+	case ADV7180_AD_PAL_M:
+		std = V4L2_STD_PAL_M;
+		/* PAL M is very similar to NTSC (same lines/field) */
+		idx = ADV7180_NTSC;
+		break;
+	case ADV7180_AD_PAL_N:
+		std = V4L2_STD_PAL_N;
+		idx = ADV7180_PAL;
+		break;
+	case ADV7180_AD_PAL_60:
+		std = V4L2_STD_PAL_60;
+		/* PAL 60 has same lines as NTSC */
+		idx = ADV7180_NTSC;
+		break;
+	case ADV7180_AD_NTSC:
+		std = V4L2_STD_NTSC;
+		idx = ADV7180_NTSC;
+		break;
+	case ADV7180_AD_NTSC_4_43:
+		std = V4L2_STD_NTSC_443;
+		idx = ADV7180_NTSC;
+		break;
+	case ADV7180_AD_SECAM:
+		std = V4L2_STD_SECAM;
+		idx = ADV7180_PAL;
+		break;
+	case ADV7180_AD_SECAM_525:
+		/*
+		 * FIXME: could not find any info on "SECAM 525", assume
+		 * it is SECAM but with NTSC line standard.
+		 */
+		std = V4L2_STD_SECAM;
+		idx = ADV7180_NTSC;
+		break;
+	}
+
+	if (std != sensor->std_id) {
+		sensor->video_idx = idx;
+		sensor->std_id = std;
+		sensor->streamcap.timeperframe =
+			adv7180_std[sensor->video_idx].std.frameperiod;
+		sensor->fmt.width = adv7180_std[sensor->video_idx].width;
+		sensor->fmt.height = adv7180_std[sensor->video_idx].height;
+		*status_change = true;
+	}
+
+	return 0;
+}
+
+/* Update lock status */
+static int adv7180_update_lock_status(struct adv7180_dev *sensor,
+				      u8 stat1, bool *status_change)
+{
+	u8 int_stat1, int_stat3, int_raw_stat3;
+	u8 int_mask1, int_mask3;
+	int ret;
+
+	/* Switch to interrupt register map */
+	ADV7180_WRITE_REG(sensor, ADV7180_ADI_CTL_1, 0x20);
+
+	ADV7180_READ_REG(sensor, ADV7180_INT_MASK_1, &int_mask1);
+	ADV7180_READ_REG(sensor, ADV7180_INT_MASK_3, &int_mask3);
+
+	ADV7180_READ_REG(sensor, ADV7180_INT_STATUS_1, &int_stat1);
+	ADV7180_READ_REG(sensor, ADV7180_INT_STATUS_3, &int_stat3);
+
+	int_stat1 &= int_mask1;
+	int_stat3 &= int_mask3;
+
+	/* clear the interrupts */
+	ADV7180_WRITE_REG(sensor, ADV7180_INT_CLEAR_1, int_stat1);
+	ADV7180_WRITE_REG(sensor, ADV7180_INT_CLEAR_1, 0);
+	ADV7180_WRITE_REG(sensor, ADV7180_INT_CLEAR_3, int_stat3);
+	ADV7180_WRITE_REG(sensor, ADV7180_INT_CLEAR_3, 0);
+
+	ADV7180_READ_REG(sensor, ADV7180_INT_RAW_STATUS_3, &int_raw_stat3);
+
+	/* Switch back to normal register map */
+	ADV7180_WRITE_REG(sensor, ADV7180_ADI_CTL_1, 0x00);
+
+	*status_change = ((stat1 & ADV7180_LOST_LOCK) || int_stat1 ||
+			  int_stat3);
+
+	sensor->locked = ((stat1 & ADV7180_IN_LOCK) &&
+			  (stat1 & ADV7180_FSC_LOCK) &&
+			  (int_raw_stat3 & ADV7180_INT_SD_V_LOCK));
+
+	return 0;
+}
+
+static void adv7180_power(struct adv7180_dev *sensor, bool enable)
+{
+	if (enable && !sensor->on) {
+		if (gpio_is_valid(sensor->pwdn_gpio))
+			gpio_set_value_cansleep(sensor->pwdn_gpio, 1);
+
+		usleep_range(5000, 5001);
+		adv7180_write_reg(sensor, ADV7180_PWR_MNG, 0);
+	} else if (!enable && sensor->on) {
+		adv7180_write_reg(sensor, ADV7180_PWR_MNG, 0x24);
+
+		if (gpio_is_valid(sensor->pwdn_gpio))
+			gpio_set_value_cansleep(sensor->pwdn_gpio, 0);
+	}
+
+	sensor->on = enable;
+}
+
+/* threaded irq handler */
+static irqreturn_t adv7180_interrupt(int irq, void *dev_id)
+{
+	struct adv7180_dev *sensor = dev_id;
+	bool std_change, lock_status_change;
+	struct v4l2_event ev = {
+		.type = V4L2_EVENT_SOURCE_CHANGE,
+		.u.src_change.changes = 0,
+	};
+	u8 stat1;
+
+	mutex_lock(&sensor->mutex);
+
+	adv7180_read_reg(sensor, ADV7180_STATUS_1, &stat1);
+
+	adv7180_update_lock_status(sensor, stat1, &lock_status_change);
+	adv7180_get_autodetect_std(sensor, stat1, &std_change);
+
+	mutex_unlock(&sensor->mutex);
+
+	if (lock_status_change)
+		ev.u.src_change.changes |= V4L2_EVENT_SRC_CH_LOCK_STATUS;
+	if (std_change)
+		ev.u.src_change.changes |= V4L2_EVENT_SRC_CH_RESOLUTION;
+
+	if (ev.u.src_change.changes)
+		v4l2_subdev_notify_event(&sensor->sd, &ev);
+
+	return IRQ_HANDLED;
+}
+
+static const struct adv7180_inputs_t *
+adv7180_find_input(struct adv7180_dev *sensor, u32 insel)
+{
+	int i;
+
+	for (i = 0; i < NUM_INPUTS_64_48; i++) {
+		if (insel == adv7180_inputs_64_48[i].insel)
+			return &adv7180_inputs_64_48[i];
+	}
+
+	return NULL;
+}
+
+/* --------------- Subdev Operations --------------- */
+
+static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+	bool std_change, lsc;
+	int ret = 0;
+	u8 stat1;
+
+	mutex_lock(&sensor->mutex);
+
+	/*
+	 * If we have the ADV7180 irq, we can just return the currently
+	 * detected standard. Otherwise we have to poll the AD_RESULT
+	 * bits every time querystd() is called.
+	 */
+	if (!sensor->i2c_client->irq) {
+		adv7180_read_reg(sensor, ADV7180_STATUS_1, &stat1);
+
+		ret = adv7180_update_lock_status(sensor, stat1, &lsc);
+		if (ret)
+			goto unlock;
+		ret = adv7180_get_autodetect_std(sensor, stat1, &std_change);
+		if (ret)
+			goto unlock;
+	}
+
+	*std = sensor->std_id;
+
+unlock:
+	mutex_unlock(&sensor->mutex);
+	return ret;
+}
+
+static int adv7180_s_power(struct v4l2_subdev *sd, int on)
+{
+	return 0;
+}
+
+static int adv7180_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	cparm->capability = sensor->streamcap.capability;
+	cparm->timeperframe = sensor->streamcap.timeperframe;
+	cparm->capturemode = sensor->streamcap.capturemode;
+
+	return 0;
+}
+
+static int adv7180_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	return 0;
+}
+
+static int adv7180_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	if (format->pad)
+		return -EINVAL;
+
+	*mf = sensor->fmt;
+
+	return 0;
+}
+
+/*
+ * This driver autodetects a standard video mode, so we don't allow
+ * setting a mode, just return the current autodetected mode.
+ */
+static int adv7180_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *format)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+	struct v4l2_mbus_framefmt *mf = &format->format;
+
+	if (format->pad)
+		return -EINVAL;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+		mf = &cfg->try_fmt;
+
+	*mf = sensor->fmt;
+
+	return 0;
+}
+
+
+/* Controls */
+
+static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct adv7180_dev *sensor = ctrl_to_adv7180_dev(ctrl);
+	int ret = 0;
+	u8 tmp;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		tmp = ctrl->val;
+		ADV7180_WRITE_REG(sensor, ADV7180_BRIGHTNESS, tmp);
+		break;
+	case V4L2_CID_CONTRAST:
+		tmp = ctrl->val;
+		ADV7180_WRITE_REG(sensor, ADV7180_CONTRAST, tmp);
+		break;
+	case V4L2_CID_SATURATION:
+		tmp = ctrl->val;
+		ADV7180_WRITE_REG(sensor, ADV7180_SD_SATURATION_CB, tmp);
+		ADV7180_WRITE_REG(sensor, ADV7180_SD_SATURATION_CR, tmp);
+		break;
+	case V4L2_CID_HUE:
+		tmp = ctrl->val;
+		/* Hue is inverted according to HSL chart */
+		ADV7180_WRITE_REG(sensor, ADV7180_HUE_REG, -tmp);
+		break;
+	case V4L2_CID_ADV718x_OFF_CR:
+		tmp = ctrl->val + 128;
+		ADV7180_WRITE_REG(sensor, ADV7180_SD_OFFSET_CR, tmp);
+		break;
+	case V4L2_CID_ADV718x_OFF_CB:
+		tmp = ctrl->val + 128;
+		ADV7180_WRITE_REG(sensor, ADV7180_SD_OFFSET_CB, tmp);
+		break;
+	case V4L2_CID_ADV718x_FREERUN_ENABLE:
+		ADV7180_READ_REG(sensor, ADV7180_DEF_Y, &tmp);
+		tmp &= ~ADV7180_DEF_VAL_AUTO_EN;
+		if (ctrl->val)
+			tmp |= ADV7180_DEF_VAL_AUTO_EN;
+		ADV7180_WRITE_REG(sensor, ADV7180_DEF_Y, tmp);
+		break;
+	case V4L2_CID_ADV718x_FORCE_FREERUN:
+		ADV7180_READ_REG(sensor, ADV7180_DEF_Y, &tmp);
+		tmp &= ~ADV7180_DEF_VAL_EN;
+		if (ctrl->val)
+			tmp |= ADV7180_DEF_VAL_EN;
+		ADV7180_WRITE_REG(sensor, ADV7180_DEF_Y, tmp);
+		break;
+	case V4L2_CID_ADV718x_FREERUN_Y:
+		ADV7180_READ_REG(sensor, ADV7180_DEF_Y, &tmp);
+		tmp &= ~ADV7180_DEF_Y_MASK;
+		tmp |= (ctrl->val << ADV7180_DEF_Y_BIT);
+		ADV7180_WRITE_REG(sensor, ADV7180_DEF_Y, tmp);
+		break;
+	case V4L2_CID_ADV718x_FREERUN_CB:
+		ADV7180_READ_REG(sensor, ADV7180_DEF_C, &tmp);
+		tmp &= ~ADV7180_DEF_CB_MASK;
+		tmp |= (ctrl->val << ADV7180_DEF_CB_BIT);
+		ADV7180_WRITE_REG(sensor, ADV7180_DEF_C, tmp);
+		break;
+	case V4L2_CID_ADV718x_FREERUN_CR:
+		ADV7180_READ_REG(sensor, ADV7180_DEF_C, &tmp);
+		tmp &= ~ADV7180_DEF_CR_MASK;
+		tmp |= (ctrl->val << ADV7180_DEF_CR_BIT);
+		ADV7180_WRITE_REG(sensor, ADV7180_DEF_C, tmp);
+		break;
+	case V4L2_CID_ADV718x_CTI_ENABLE:
+		ADV7180_READ_REG(sensor, ADV7180_CTI_DNR, &tmp);
+		tmp &= ~ADV7180_CTI_EN;
+		if (ctrl->val)
+			tmp |= ADV7180_CTI_EN;
+		ADV7180_WRITE_REG(sensor, ADV7180_CTI_DNR, tmp);
+		break;
+	case V4L2_CID_ADV718x_CTI_AB_ENABLE:
+		ADV7180_READ_REG(sensor, ADV7180_CTI_DNR, &tmp);
+		tmp &= ~ADV7180_CTI_AB_EN;
+		if (ctrl->val)
+			tmp |= ADV7180_CTI_AB_EN;
+		ADV7180_WRITE_REG(sensor, ADV7180_CTI_DNR, tmp);
+		break;
+	case V4L2_CID_ADV718x_CTI_AB:
+		ADV7180_READ_REG(sensor, ADV7180_CTI_DNR, &tmp);
+		tmp &= ~ADV7180_CTI_AB_MASK;
+		tmp |= (ctrl->val << ADV7180_CTI_AB_BIT);
+		ADV7180_WRITE_REG(sensor, ADV7180_CTI_DNR, tmp);
+		break;
+	case V4L2_CID_ADV718x_CTI_THRESH:
+		tmp = ctrl->val;
+		ADV7180_WRITE_REG(sensor, ADV7180_CTI_THRESH, tmp);
+		break;
+	case V4L2_CID_ADV718x_DNR_ENABLE:
+		ADV7180_READ_REG(sensor, ADV7180_CTI_DNR, &tmp);
+		tmp &= ~ADV7180_DNR_EN;
+		if (ctrl->val)
+			tmp |= ADV7180_DNR_EN;
+		ADV7180_WRITE_REG(sensor, ADV7180_CTI_DNR, tmp);
+		break;
+	case V4L2_CID_ADV718x_DNR_THRESH1:
+		tmp = ctrl->val;
+		ADV7180_WRITE_REG(sensor, ADV7180_DNR_THRESH1, tmp);
+		break;
+	case V4L2_CID_ADV718x_LUMA_PEAK_GAIN:
+		tmp = ctrl->val;
+		ADV7180_WRITE_REG(sensor, ADV7180_LUMA_PEAK_GAIN, tmp);
+		break;
+	case V4L2_CID_ADV718x_DNR_THRESH2:
+		tmp = ctrl->val;
+		ADV7180_WRITE_REG(sensor, ADV7180_DNR_THRESH2, tmp);
+		break;
+	default:
+		ret = -EPERM;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
+	.s_ctrl = adv7180_s_ctrl,
+};
+
+/* supported standard controls */
+static const struct v4l2_ctrl_config adv7180_std_ctrl[] = {
+	{
+		.id = V4L2_CID_BRIGHTNESS,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Brightness",
+		.def =   0,
+		.min =   0,
+		.max = 255,
+		.step =  1,
+	}, {
+		.id = V4L2_CID_SATURATION,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Saturation",
+		.def = 128,
+		.min =   0,
+		.max = 255,
+		.step =  1,
+	}, {
+		.id = V4L2_CID_CONTRAST,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Contrast",
+		.def = 128,
+		.min =   0,
+		.max = 255,
+		.step =  1,
+	}, {
+		.id = V4L2_CID_HUE,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Hue",
+		.def =    0,
+		.min = -127,
+		.max =  128,
+		.step =   1,
+	},
+};
+
+#define ADV7180_NUM_STD_CONTROLS ARRAY_SIZE(adv7180_std_ctrl)
+
+/* supported custom controls */
+static const struct v4l2_ctrl_config adv7180_custom_ctrl[] = {
+	{
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_OFF_CR,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Offset Cr",
+		.def =    0, /*    0 mV offset applied to the Cr channel */
+		.min = -128, /* −312 mV offset applied to the Cr channel */
+		.max =  127, /* +312 mV offset applied to the Cr channel */
+		.step =   1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_OFF_CB,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Offset Cb",
+		.def =    0, /*    0 mV offset applied to the Cb channel */
+		.min = -128, /* −312 mV offset applied to the Cb channel */
+		.max =  127, /* +312 mV offset applied to the Cb channel */
+		.step =   1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_FREERUN_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "Freerun Enable",
+		.def =  1,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_FORCE_FREERUN,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "Force Freerun",
+		.def =  0,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_FREERUN_Y,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Freerun Y",
+		.def =  13,
+		.min =   0,
+		.max =  63,
+		.step =  1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_FREERUN_CB,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Freerun Cb",
+		.def =  12,
+		.min =   0,
+		.max =  15,
+		.step =  1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_FREERUN_CR,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Freerun Cr",
+		.def =   7,
+		.min =   0,
+		.max =  15,
+		.step =  1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_CTI_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "CTI Enable",
+		.def =  0,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_CTI_AB_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "CTI AB Enable",
+		.def =  0,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_CTI_AB,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "CTI AB Sharpness",
+		.def =  3,
+		.min =  0,
+		.max =  3,
+		.step = 1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_CTI_THRESH,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "CTI Threshold",
+		.def =   8,
+		.min =   0,
+		.max = 255,
+		.step =  1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_DNR_ENABLE,
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.name = "DNR Enable",
+		.def =  1,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_DNR_THRESH1,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "DNR1 Threshold",
+		.def =   8,
+		.min =   0,
+		.max = 255,
+		.step =  1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_LUMA_PEAK_GAIN,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "Luma Peak Gain",
+		.def =  64,
+		.min =   0,
+		.max = 255,
+		.step =  1,
+	}, {
+		.ops = &adv7180_ctrl_ops,
+		.id = V4L2_CID_ADV718x_DNR_THRESH2,
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.name = "DNR2 Threshold",
+		.def =   4,
+		.min =   0,
+		.max = 255,
+		.step =  1,
+	},
+};
+
+#define ADV7180_NUM_CUSTOM_CONTROLS ARRAY_SIZE(adv7180_custom_ctrl)
+
+#define ADV7180_NUM_CONTROLS \
+	(ADV7180_NUM_STD_CONTROLS + ADV7180_NUM_CUSTOM_CONTROLS)
+
+static int adv7180_init_controls(struct adv7180_dev *sensor)
+{
+	const struct v4l2_ctrl_config *c;
+	int i, err;
+
+	v4l2_ctrl_handler_init(&sensor->ctrl_hdl, ADV7180_NUM_CONTROLS);
+
+	for (i = 0; i < ADV7180_NUM_STD_CONTROLS; i++) {
+		c = &adv7180_std_ctrl[i];
+
+		v4l2_ctrl_new_std(&sensor->ctrl_hdl, &adv7180_ctrl_ops,
+				  c->id, c->min, c->max, c->step, c->def);
+	}
+
+	for (i = 0; i < ADV7180_NUM_CUSTOM_CONTROLS; i++) {
+		c = &adv7180_custom_ctrl[i];
+
+		v4l2_ctrl_new_custom(&sensor->ctrl_hdl, c, NULL);
+	}
+
+	sensor->sd.ctrl_handler = &sensor->ctrl_hdl;
+	if (sensor->ctrl_hdl.error) {
+		err = sensor->ctrl_hdl.error;
+		goto error;
+	}
+
+	err = v4l2_ctrl_handler_setup(&sensor->ctrl_hdl);
+	if (err)
+		goto error;
+
+	return 0;
+
+ error:
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+	v4l2_err(&sensor->sd, "%s: error %d\n", __func__, err);
+
+	return err;
+}
+
+static int adv7180_enum_frame_size(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+
+	if (fse->pad)
+		return -EINVAL;
+	if (fse->index || fse->code != sensor->fmt.code)
+		return -EINVAL;
+
+	fse->min_width = adv7180_std[sensor->video_idx].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = adv7180_std[sensor->video_idx].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+
+	mutex_lock(&sensor->mutex);
+
+	*status = 0;
+
+	if (sensor->on) {
+		if (!sensor->locked)
+			*status = V4L2_IN_ST_NO_SIGNAL | V4L2_IN_ST_NO_SYNC;
+	} else {
+		*status = V4L2_IN_ST_NO_POWER;
+	}
+
+	mutex_unlock(&sensor->mutex);
+
+	return 0;
+}
+
+static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
+			     u32 output, u32 config)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+	const struct adv7180_inputs_t *advinput;
+	struct v4l2_event ev = {
+		.type = V4L2_EVENT_SOURCE_CHANGE,
+		.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+	};
+
+	advinput = adv7180_find_input(sensor, input);
+	if (!advinput)
+		return -EINVAL;
+
+	mutex_lock(&sensor->mutex);
+
+	if (input == sensor->current_input)
+		goto out;
+
+	adv7180_write_reg(sensor, ADV7180_INPUT_CTL, advinput->insel);
+
+	/* ADI Required Write: Reset Clamp Circuitry */
+	adv7180_write_reg(sensor, ADV7180_ANALOG_CLAMP_CTL, 0x30);
+
+	sensor->current_input = input;
+
+	v4l2_subdev_notify_event(&sensor->sd, &ev);
+out:
+	mutex_unlock(&sensor->mutex);
+	return 0;
+}
+
+static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+
+	if (code->pad)
+		return -EINVAL;
+	if (code->index != 0)
+		return -EINVAL;
+
+	code->code = sensor->fmt.code;
+
+	return 0;
+}
+
+static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
+				 struct v4l2_mbus_config *cfg)
+{
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+
+	cfg->type = V4L2_MBUS_BT656;
+	cfg->flags = sensor->ep.bus.parallel.flags;
+
+	return 0;
+}
+
+static int adv7180_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static int adv7180_subscribe_event(struct v4l2_subdev *sd,
+				   struct v4l2_fh *fh,
+				   struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_SOURCE_CHANGE:
+		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
+	case V4L2_EVENT_CTRL:
+		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
+	default:
+		return -EINVAL;
+	}
+}
+
+static struct v4l2_subdev_core_ops adv7180_core_ops = {
+	.subscribe_event = adv7180_subscribe_event,
+	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
+	.s_power = adv7180_s_power,
+	.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,
+};
+
+static struct v4l2_subdev_video_ops adv7180_video_ops = {
+	.s_parm = adv7180_s_parm,
+	.g_parm = adv7180_g_parm,
+	.g_input_status = adv7180_g_input_status,
+	.s_routing = adv7180_s_routing,
+	.querystd = adv7180_querystd,
+	.g_mbus_config  = adv7180_g_mbus_config,
+	.s_stream = adv7180_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops adv7180_pad_ops = {
+	.enum_mbus_code = adv7180_enum_mbus_code,
+	.get_fmt = adv7180_get_fmt,
+	.set_fmt = adv7180_set_fmt,
+	.enum_frame_size = adv7180_enum_frame_size,
+};
+
+static struct v4l2_subdev_ops adv7180_subdev_ops = {
+	.core = &adv7180_core_ops,
+	.video = &adv7180_video_ops,
+	.pad = &adv7180_pad_ops,
+};
+
+/***********************************************************************
+ * I2C client and driver.
+ ***********************************************************************/
+
+/*! ADV7180 Reset function.
+ *
+ *  @return		None.
+ */
+static int adv7180_hard_reset(struct adv7180_dev *sensor)
+{
+	/* assert reset bit */
+	adv7180_write_reg(sensor, ADV7180_PWR_MNG, 0x80);
+	usleep_range(5000, 5001);
+
+	return adv7180_load_reg_tbl(sensor, adv7180_fsl_init_reg,
+				    ARRAY_SIZE(adv7180_fsl_init_reg));
+}
+
+/*
+ * Enable the SD_UNLOCK and SD_AD_CHNG interrupts.
+ */
+static int adv7180_enable_interrupts(struct adv7180_dev *sensor)
+{
+	int ret;
+
+	/* Switch to interrupt register map */
+	ADV7180_WRITE_REG(sensor, ADV7180_ADI_CTL_1, 0x20);
+	/* INTRQ active low, active until cleared */
+	ADV7180_WRITE_REG(sensor, ADV7180_INT_CONFIG_1, 0xd5);
+	/* unmask SD_UNLOCK and SD_LOCK */
+	ADV7180_WRITE_REG(sensor, ADV7180_INT_MASK_1,
+			  ADV7180_INT_SD_UNLOCK | ADV7180_INT_SD_LOCK);
+	/* unmask SD_AD_CHNG and SD_V_LOCK_CHNG */
+	ADV7180_WRITE_REG(sensor, ADV7180_INT_MASK_3,
+			  ADV7180_INT_SD_AD_CHNG | ADV7180_INT_SD_V_LOCK_CHNG);
+	/* Switch back to normal register map */
+	ADV7180_WRITE_REG(sensor, ADV7180_ADI_CTL_1, 0x00);
+
+	return 0;
+}
+
+/*!
+ * ADV7180 I2C probe function.
+ * Function set in i2c_driver struct.
+ * Called by insmod.
+ *
+ *  @param *adapter	I2C adapter descriptor.
+ *
+ *  @return		Error code indicating success or failure.
+ */
+static int adv7180_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device_node *endpoint;
+	struct adv7180_dev *sensor;
+	struct device_node *np;
+	const char *norm = "pal";
+	bool std_change, lsc;
+	u8 stat1, rev_id;
+	int ret = 0;
+
+	sensor = devm_kzalloc(&client->dev, sizeof(struct adv7180_dev),
+			      GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->dev = &client->dev;
+	np = sensor->dev->of_node;
+
+	ret = of_property_read_string(np, "default-std", &norm);
+	if (ret < 0 && ret != -EINVAL) {
+		dev_err(sensor->dev, "error reading default-std property!\n");
+		return ret;
+	}
+	if (!strcasecmp(norm, "pal")) {
+		sensor->std_id = V4L2_STD_PAL;
+		sensor->video_idx = ADV7180_PAL;
+		dev_info(sensor->dev, "defaulting to PAL!\n");
+	} else if (!strcasecmp(norm, "ntsc")) {
+		sensor->std_id = V4L2_STD_NTSC;
+		sensor->video_idx = ADV7180_NTSC;
+		dev_info(sensor->dev, "defaulting to NTSC!\n");
+	} else {
+		dev_err(sensor->dev, "invalid default-std value: '%s'!\n",
+			norm);
+		return -EINVAL;
+	}
+
+	/* Set initial values for the sensor struct. */
+	sensor->i2c_client = client;
+	sensor->streamcap.timeperframe =
+		adv7180_std[sensor->video_idx].std.frameperiod;
+	sensor->fmt.width = adv7180_std[sensor->video_idx].width;
+	sensor->fmt.height = adv7180_std[sensor->video_idx].height;
+	sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
+	sensor->fmt.field = V4L2_FIELD_SEQ_BT;
+
+	mutex_init(&sensor->mutex);
+
+	endpoint = of_graph_get_next_endpoint(np, NULL);
+	if (!endpoint) {
+		dev_err(sensor->dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	v4l2_of_parse_endpoint(endpoint, &sensor->ep);
+	if (sensor->ep.bus_type != V4L2_MBUS_BT656) {
+		dev_err(sensor->dev, "invalid bus type, must be bt.656\n");
+		return -EINVAL;
+	}
+	of_node_put(endpoint);
+
+	ret = of_get_named_gpio(np, "pwdn-gpio", 0);
+	if (gpio_is_valid(ret)) {
+		sensor->pwdn_gpio = ret;
+		ret = devm_gpio_request_one(sensor->dev,
+					    sensor->pwdn_gpio,
+					    GPIOF_OUT_INIT_HIGH,
+					    "adv7180_pwdn");
+		if (ret < 0) {
+			dev_err(sensor->dev,
+				"request for power down gpio failed\n");
+			return ret;
+		}
+	} else {
+		if (ret == -EPROBE_DEFER)
+			return ret;
+		/* assume a power-down gpio is not required */
+		sensor->pwdn_gpio = -1;
+	}
+
+	adv7180_regulator_enable(sensor);
+
+	/* Power on the chip */
+	adv7180_power(sensor, true);
+
+	/*! ADV7180 initialization. */
+	ret = adv7180_hard_reset(sensor);
+	if (ret) {
+		dev_err(sensor->dev, "hard reset failed!\n");
+		goto cleanup;
+	}
+
+	/*! Read the revision ID of the chip */
+	ret = adv7180_read_reg(sensor, ADV7180_IDENT, &rev_id);
+	if (ret < 0) {
+		dev_err(sensor->dev,
+			"failed to read ADV7180 IDENT register!\n");
+		ret = -ENODEV;
+		goto cleanup;
+	}
+	sensor->rev_id = rev_id;
+
+	dev_info(sensor->dev, "Analog Devices ADV7180 Rev 0x%02X detected!\n",
+		 sensor->rev_id);
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &adv7180_subdev_ops);
+
+	/* see if there is a signal lock already */
+	adv7180_read_reg(sensor, ADV7180_STATUS_1, &stat1);
+
+	ret = adv7180_update_lock_status(sensor, stat1, &lsc);
+	if (ret < 0)
+		goto cleanup;
+	ret = adv7180_get_autodetect_std(sensor, stat1, &std_change);
+	if (ret < 0)
+		goto cleanup;
+
+	if (sensor->i2c_client->irq) {
+		ret = request_threaded_irq(sensor->i2c_client->irq,
+					   NULL, adv7180_interrupt,
+					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					   IF_NAME, sensor);
+		if (ret < 0) {
+			dev_err(sensor->dev, "Failed to register irq %d\n",
+				sensor->i2c_client->irq);
+			goto cleanup;
+		}
+
+		adv7180_enable_interrupts(sensor);
+
+		dev_info(sensor->dev, "Registered irq %d\n",
+			 sensor->i2c_client->irq);
+	}
+
+	ret = adv7180_init_controls(sensor);
+	if (ret)
+		goto irqfree;
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret)
+		goto free_ctrls;
+
+	return 0;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+irqfree:
+	if (sensor->i2c_client->irq)
+		free_irq(sensor->i2c_client->irq, sensor);
+cleanup:
+	adv7180_power(sensor, false);
+	adv7180_regulator_disable(sensor);
+	return ret;
+}
+
+/*!
+ * ADV7180 I2C detach function.
+ * Called on rmmod.
+ *
+ *  @param *client	struct i2c_client*.
+ *
+ *  @return		Error code indicating success or failure.
+ */
+static int adv7180_detach(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct adv7180_dev *sensor = to_adv7180_dev(sd);
+
+	if (sensor->i2c_client->irq)
+		free_irq(sensor->i2c_client->irq, sensor);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+	/* Power off the chip */
+	adv7180_power(sensor, false);
+
+	adv7180_regulator_disable(sensor);
+
+	return 0;
+}
+
+static const struct i2c_device_id adv7180_id[] = {
+	{ "adv7180", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
+
+static const struct of_device_id adv7180_dt_ids[] = {
+	{ .compatible = "adi,adv7180" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, adv7180_dt_ids);
+
+static struct i2c_driver adv7180_driver = {
+	.driver = {
+		.name	= "adv7180",
+		.owner	= THIS_MODULE,
+		.of_match_table	= adv7180_dt_ids,
+	},
+	.id_table	= adv7180_id,
+	.probe		= adv7180_probe,
+	.remove		= adv7180_detach,
+};
+
+module_i2c_driver(adv7180_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Analog Devices ADV7180 Subdev driver");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 9343950..005c82b 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -184,6 +184,10 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x1090)
 
+/* The base for the ADV718x sensor controls.
+ * We reserve 32 controls for this driver. */
+#define V4L2_CID_USER_ADV718X_BASE		(V4L2_CID_USER_BASE + 0x10a0)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild
index fa78958..5b064a9 100644
--- a/include/uapi/media/Kbuild
+++ b/include/uapi/media/Kbuild
@@ -1,2 +1,3 @@
 # UAPI Header export list
 header-y += imx.h
+header-y += adv718x.h
diff --git a/include/uapi/media/adv718x.h b/include/uapi/media/adv718x.h
new file mode 100644
index 0000000..79abb7d
--- /dev/null
+++ b/include/uapi/media/adv718x.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014-2015 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef __UAPI_MEDIA_ADV718X_H__
+#define __UAPI_MEDIA_ADV718X_H__
+
+enum adv718x_ctrl_id {
+	V4L2_CID_ADV718x_OFF_CB = (V4L2_CID_USER_ADV718X_BASE + 0),
+	V4L2_CID_ADV718x_OFF_CR,
+	V4L2_CID_ADV718x_FREERUN_ENABLE,
+	V4L2_CID_ADV718x_FORCE_FREERUN,
+	V4L2_CID_ADV718x_FREERUN_Y,
+	V4L2_CID_ADV718x_FREERUN_CB,
+	V4L2_CID_ADV718x_FREERUN_CR,
+	/* Chroma Transient Improvement Controls */
+	V4L2_CID_ADV718x_CTI_ENABLE,
+	V4L2_CID_ADV718x_CTI_AB_ENABLE,
+	V4L2_CID_ADV718x_CTI_AB,
+	V4L2_CID_ADV718x_CTI_THRESH,
+	/* Digital Noise Reduction and Lumanance Peaking Gain Controls */
+	V4L2_CID_ADV718x_DNR_ENABLE,
+	V4L2_CID_ADV718x_DNR_THRESH1,
+	V4L2_CID_ADV718x_LUMA_PEAK_GAIN,
+	V4L2_CID_ADV718x_DNR_THRESH2,
+	/* ADV7182 specific controls */
+	V4L2_CID_ADV7182_FREERUN_PAT_SEL,
+	V4L2_CID_ADV7182_ACE_ENABLE,
+	V4L2_CID_ADV7182_ACE_LUMA_GAIN,
+	V4L2_CID_ADV7182_ACE_RESPONSE_SPEED,
+	V4L2_CID_ADV7182_ACE_CHROMA_GAIN,
+	V4L2_CID_ADV7182_ACE_CHROMA_MAX,
+	V4L2_CID_ADV7182_ACE_GAMMA_GAIN,
+	V4L2_CID_ADV7182_DITHER_ENABLE,
+};
+
+#endif
-- 
1.9.1


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

* [PATCH 35/38] media: adv7180: add power pin control
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (33 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-15 16:05   ` Lars-Peter Clausen
  2016-06-14 22:49 ` [PATCH 36/38] media: adv7180: implement g_parm Steve Longerbeam
                   ` (5 subsequent siblings)
  40 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Some targets control the ADV7180 power pin via a gpio, so add
support for "pwdn-gpio" device node and pin control.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/media/i2c/adv7180.c | 51 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index b77b0a4..b3bb19f 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -26,6 +26,7 @@
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/of_gpio.h>
 #include <linux/videodev2.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-event.h>
@@ -192,6 +193,7 @@ struct adv7180_state {
 	struct media_pad	pad;
 	struct mutex		mutex; /* mutual excl. when accessing chip */
 	int			irq;
+	int			pwdn_gpio;
 	v4l2_std_id		curr_norm;
 	bool			powered;
 	bool			streaming;
@@ -442,6 +444,19 @@ static int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
 	return 0;
 }
 
+static void adv7180_set_power_pin(struct adv7180_state *state, bool on)
+{
+	if (!gpio_is_valid(state->pwdn_gpio))
+		return;
+
+	if (on) {
+		gpio_set_value_cansleep(state->pwdn_gpio, 1);
+		usleep_range(5000, 5001);
+	} else {
+		gpio_set_value_cansleep(state->pwdn_gpio, 0);
+	}
+}
+
 static int adv7180_set_power(struct adv7180_state *state, bool on)
 {
 	u8 val;
@@ -1185,6 +1200,8 @@ static int init_device(struct adv7180_state *state)
 
 	mutex_lock(&state->mutex);
 
+	adv7180_set_power_pin(state, true);
+
 	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
 	usleep_range(5000, 10000);
 
@@ -1232,6 +1249,34 @@ out_unlock:
 	return ret;
 }
 
+static int adv7180_of_parse(struct adv7180_state *state)
+{
+	struct i2c_client *client = state->client;
+	struct device_node *np = client->dev.of_node;
+	int ret;
+
+	ret = of_get_named_gpio(np, "pwdn-gpio", 0);
+
+	if (gpio_is_valid(ret)) {
+		state->pwdn_gpio = ret;
+		ret = devm_gpio_request_one(&client->dev,
+					    state->pwdn_gpio,
+					    GPIOF_OUT_INIT_HIGH,
+					    "adv7180_pwdn");
+		if (ret < 0) {
+			v4l_err(client, "request for power pin failed\n");
+			return ret;
+		}
+	} else {
+		if (ret == -EPROBE_DEFER)
+			return ret;
+		/* assume a power-down gpio is not required */
+		state->pwdn_gpio = -1;
+	}
+
+	return 0;
+}
+
 static int adv7180_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
@@ -1254,6 +1299,10 @@ static int adv7180_probe(struct i2c_client *client,
 	state->field = V4L2_FIELD_INTERLACED;
 	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
 
+	ret = adv7180_of_parse(state);
+	if (ret)
+		return ret;
+
 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
 		state->csi_client = i2c_new_dummy(client->adapter,
 				ADV7180_DEFAULT_CSI_I2C_ADDR);
@@ -1345,6 +1394,8 @@ static int adv7180_remove(struct i2c_client *client)
 	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
 		i2c_unregister_device(state->csi_client);
 
+	adv7180_set_power_pin(state, false);
+
 	mutex_destroy(&state->mutex);
 
 	return 0;
-- 
1.9.1


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

* [PATCH 36/38] media: adv7180: implement g_parm
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (34 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 35/38] media: adv7180: add power pin control Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 37/38] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
                   ` (4 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Implement g_parm to return the current standard's frame period.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/media/i2c/adv7180.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index b3bb19f..9e40632 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -740,6 +740,27 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int adv7180_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct adv7180_state *state = to_state(sd);
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	if (state->curr_norm & V4L2_STD_525_60) {
+		cparm->timeperframe.numerator = 1001;
+		cparm->timeperframe.denominator = 30000;
+	} else {
+		cparm->timeperframe.numerator = 1;
+		cparm->timeperframe.denominator = 25;
+	}
+
+	return 0;
+}
+
 static int adv7180_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *cropcap)
 {
 	struct adv7180_state *state = to_state(sd);
@@ -798,6 +819,7 @@ static int adv7180_subscribe_event(struct v4l2_subdev *sd,
 static const struct v4l2_subdev_video_ops adv7180_video_ops = {
 	.s_std = adv7180_s_std,
 	.g_std = adv7180_g_std,
+	.g_parm = adv7180_g_parm,
 	.querystd = adv7180_querystd,
 	.g_input_status = adv7180_g_input_status,
 	.s_routing = adv7180_s_routing,
-- 
1.9.1


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

* [PATCH 37/38] media: Add i.MX5/6 mem2mem driver
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (35 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 36/38] media: adv7180: implement g_parm Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-14 22:49 ` [PATCH 38/38] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers Steve Longerbeam
                   ` (3 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds a V4L2 mem2mem driver for i.MX5/6 SoC. Uses the IPU IC image
converter (post-processor task) to perform scaling, rotation, and
color space conversion.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 Documentation/devicetree/bindings/media/imx.txt |   25 +-
 drivers/staging/media/imx/Kconfig               |   12 +
 drivers/staging/media/imx/Makefile              |    1 +
 drivers/staging/media/imx/m2m/Makefile          |    1 +
 drivers/staging/media/imx/m2m/imx-m2m.c         | 1049 +++++++++++++++++++++++
 5 files changed, 1087 insertions(+), 1 deletion(-)
 create mode 100644 drivers/staging/media/imx/m2m/Makefile
 create mode 100644 drivers/staging/media/imx/m2m/imx-m2m.c

diff --git a/Documentation/devicetree/bindings/media/imx.txt b/Documentation/devicetree/bindings/media/imx.txt
index 1f2ca4b..12ef06c 100644
--- a/Documentation/devicetree/bindings/media/imx.txt
+++ b/Documentation/devicetree/bindings/media/imx.txt
@@ -1,4 +1,27 @@
-Freescale i.MX Video Capture
+Freescale i.MX Video Capture, Mem2Mem
+
+Video Mem2Mem node
+------------------
+
+This is the imx video mem2mem device node. The mem2mem node is an IPU
+client and uses the register-level primitives of the IPU, so it does
+not require reg or interrupt properties. Only a compatible property
+and the ipu phandle is required.
+
+Required properties:
+- compatible	: "fsl,imx-video-mem2mem";
+- ipu           : the ipu phandle;
+
+Example:
+
+/ {
+	ipum2m0: ipum2m@ipu1 {
+		compatible = "fsl,imx-video-mem2mem";
+		ipu = <&ipu1>;
+		status = "okay";
+	};
+};
+
 
 Video Capture node
 ------------------
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
index 65e1645..3305daa 100644
--- a/drivers/staging/media/imx/Kconfig
+++ b/drivers/staging/media/imx/Kconfig
@@ -21,3 +21,15 @@ config VIDEO_IMX_CAMERA
 if VIDEO_IMX_CAMERA
 source "drivers/staging/media/imx/capture/Kconfig"
 endif
+
+config VIDEO_IMX_M2M
+	tristate "i.MX5/6 Mem2Mem driver"
+	depends on VIDEO_IMX && VIDEO_DEV
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	default y
+	---help---
+	  Use the IPU IC Post-processor on the i.MX5/6 SoC for mem2mem
+	  processing of buffers. Operations include scaling, rotation,
+	  and color space conversion. The driver implements tiling to
+	  support scaling up to 4096x4096.
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index 7c97629..b9e31d8 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += capture/
+obj-$(CONFIG_VIDEO_IMX_M2M) += m2m/
diff --git a/drivers/staging/media/imx/m2m/Makefile b/drivers/staging/media/imx/m2m/Makefile
new file mode 100644
index 0000000..287d258
--- /dev/null
+++ b/drivers/staging/media/imx/m2m/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_IMX_M2M) += imx-m2m.o
diff --git a/drivers/staging/media/imx/m2m/imx-m2m.c b/drivers/staging/media/imx/m2m/imx-m2m.c
new file mode 100644
index 0000000..599f20e
--- /dev/null
+++ b/drivers/staging/media/imx/m2m/imx-m2m.c
@@ -0,0 +1,1049 @@
+/*
+ * This is a mem2mem driver for the Freescale i.MX5/6 SOC. It carries out
+ * color-space conversion, downsizing, resizing, and rotation transformations
+ * on input buffers using the IPU Image Converter's Post-Processing task.
+ *
+ * Based on mem2mem_testdev.c by Pawel Osciak.
+ *
+ * Copyright (c) 2012-2013 Mentor Graphics Inc.
+ * Steve Longerbeam <steve_longerbeam@mentor.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-dma-contig.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+
+MODULE_DESCRIPTION("i.MX5/6 Post-Processing mem2mem device");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+static int instrument;
+module_param(instrument, int, 0);
+MODULE_PARM_DESC(instrument, "1 = enable conversion time measurement");
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE BIT(0)
+#define MEM2MEM_OUTPUT  BIT(1)
+
+#define MEM2MEM_NAME		"imx-m2m"
+
+/* Per queue */
+#define MEM2MEM_DEF_NUM_BUFS	VIDEO_MAX_FRAME
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT	SZ_256M
+
+#define dprintk(dev, fmt, arg...) \
+	v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+struct m2mx_ctx;
+
+struct m2mx_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vfd;
+	struct device           *ipu_dev;
+	struct ipu_soc          *ipu;
+
+	struct mutex		dev_mutex;
+
+	struct v4l2_m2m_dev	*m2m_dev;
+	struct vb2_alloc_ctx    *alloc_ctx;
+};
+
+struct m2mx_ctx {
+	struct v4l2_fh          fh;
+	struct m2mx_dev	*dev;
+
+	struct ipu_ic           *ic;
+	struct image_converter_ctx *ic_ctx;
+
+	/* The rotation controls */
+	struct v4l2_ctrl_handler ctrl_hdlr;
+	int                     rotation; /* degrees */
+	bool                    hflip;
+	bool                    vflip;
+
+	/* derived from rotation, hflip, vflip controls */
+	enum ipu_rotate_mode    rot_mode;
+
+	/* has the IC image converter been preapred */
+	bool                    image_converter_ready;
+
+	/* Abort requested by m2m */
+	bool			aborting;
+
+	/* Source and destination image data */
+	struct ipu_image  image[2];
+
+	/* for instrumenting */
+	struct timespec   start;
+};
+
+#define fh_to_ctx(p) container_of(p, struct m2mx_ctx, fh)
+
+enum {
+	V4L2_M2M_SRC = 0,
+	V4L2_M2M_DST = 1,
+};
+
+static const struct v4l2_ctrl_config m2mx_std_ctrl[] = {
+	{
+		.id	= V4L2_CID_HFLIP,
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.name	= "Horizontal Flip",
+		.def	= 0,
+		.min	= 0,
+		.max	= 1,
+		.step	= 1,
+	}, {
+		.id	= V4L2_CID_VFLIP,
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.name	= "Vertical Flip",
+		.def	= 0,
+		.min	= 0,
+		.max	= 1,
+		.step	= 1,
+	}, {
+		.id	= V4L2_CID_ROTATE,
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.name	= "Rotation",
+		.def	= 0,
+		.min	= 0,
+		.max	= 270,
+		.step	= 90,
+	},
+};
+
+#define M2MX_NUM_STD_CONTROLS ARRAY_SIZE(m2mx_std_ctrl)
+
+static struct ipu_image *get_image_data(struct m2mx_ctx *ctx,
+					enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		return &ctx->image[V4L2_M2M_SRC];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &ctx->image[V4L2_M2M_DST];
+	default:
+		break;
+	}
+	return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+static void m2mx_job_abort(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+
+	/* Will cancel the transaction in the next interrupt handler */
+	ipu_image_convert_abort(ctx->ic_ctx);
+	ctx->aborting = true;
+}
+
+static void m2mx_lock(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+	struct m2mx_dev *dev = ctx->dev;
+
+	mutex_lock(&dev->dev_mutex);
+}
+
+static void m2mx_unlock(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+	struct m2mx_dev *dev = ctx->dev;
+
+	mutex_unlock(&dev->dev_mutex);
+}
+
+static void m2mx_device_run(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+	struct m2mx_dev *dev = ctx->dev;
+	struct vb2_v4l2_buffer *src_buf, *dst_buf;
+	dma_addr_t s_phys, d_phys;
+
+	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+	s_phys = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+	d_phys = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+	if (!s_phys || !d_phys) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Acquiring kernel pointers to buffers failed\n");
+		return;
+	}
+
+	if (instrument)
+		getnstimeofday(&ctx->start);
+
+	ipu_image_convert_run(ctx->ic_ctx, s_phys, d_phys);
+}
+
+static void m2mx_convert_complete(void *data,
+				   struct image_converter_run *run,
+				   int err)
+{
+	struct m2mx_ctx *ctx = data;
+	struct m2mx_dev *dev = ctx->dev;
+	struct vb2_v4l2_buffer *src_vb, *dst_vb;
+
+	if (!ctx->aborting) {
+		src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+
+		if (instrument) {
+			struct timespec ts, diff;
+			unsigned long interval;
+
+			getnstimeofday(&ts);
+			diff = timespec_sub(ts, ctx->start);
+			interval = diff.tv_sec * 1000 * 1000 +
+				diff.tv_nsec / 1000;
+			v4l2_info(&ctx->dev->v4l2_dev,
+				  "buf%d completed in %lu usec\n",
+				  dst_vb->vb2_buf.index, interval);
+		}
+	}
+
+	v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *fh,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+	const char *desc;
+	u32 fourcc;
+	int ret;
+
+	ret = ipu_image_convert_enum_format(f->index, &desc, &fourcc);
+	if (ret)
+		return ret;
+
+	strncpy(f->description, desc, sizeof(f->description) - 1);
+	f->pixelformat = fourcc;
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+				   struct v4l2_fmtdesc *f)
+{
+	return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
+				   struct v4l2_fmtdesc *f)
+{
+	return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int m2mx_g_fmt(struct m2mx_ctx *ctx, struct v4l2_format *f)
+{
+	struct vb2_queue *vq;
+	struct ipu_image *image;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	image = get_image_data(ctx, f->type);
+	if (!image)
+		return -EINVAL;
+
+	f->fmt.pix = image->pix;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_g_fmt(ctx, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_g_fmt(ctx, f);
+}
+
+static int m2mx_try_fmt(struct m2mx_ctx *ctx, struct v4l2_format *f)
+{
+	struct ipu_image test_in, test_out;
+	struct ipu_image *pin, *pout;
+	int ret;
+
+	pin = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	pout = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		test_out.pix = f->fmt.pix;
+		test_in = *pin;
+	} else {
+		test_in.pix = f->fmt.pix;
+		test_out = *pout;
+	}
+
+	ret = ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
+	if (ret)
+		return ret;
+
+	f->fmt.pix = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
+		test_out.pix : test_in.pix;
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_try_fmt(ctx, f);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_try_fmt(ctx, f);
+}
+
+static int m2mx_s_fmt(struct m2mx_ctx *ctx, struct v4l2_format *f)
+{
+	struct m2mx_dev *dev = ctx->dev;
+	struct ipu_image test_in, test_out;
+	struct ipu_image *pin, *pout;
+	struct vb2_queue *vq;
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	if (vb2_is_busy(vq)) {
+		v4l2_err(&dev->v4l2_dev, "%s: queue busy\n", __func__);
+		return -EBUSY;
+	}
+
+	pin = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	pout = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		test_out.pix = f->fmt.pix;
+		test_in = *pin;
+	} else {
+		test_in.pix = f->fmt.pix;
+		test_out = *pout;
+	}
+
+	ret = ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
+	if (ret)
+		return ret;
+
+	f->fmt.pix = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
+		test_out.pix : test_in.pix;
+	*pin = test_in;
+	*pout = test_out;
+
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_s_fmt(ctx, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_s_fmt(ctx, f);
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+			  struct v4l2_requestbuffers *reqbufs)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+			   struct v4l2_buffer *buf)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_querybuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static int vidioc_expbuf(struct file *file, void *fh,
+			 struct v4l2_exportbuffer *eb)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_expbuf(file, ctx->fh.m2m_ctx, eb);
+}
+
+static int vidioc_streamon(struct file *file, void *fh,
+			   enum v4l2_buf_type type)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *fh,
+			    enum v4l2_buf_type type)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, type);
+}
+
+static int m2mx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct m2mx_ctx *ctx = container_of(ctrl->handler,
+					     struct m2mx_ctx, ctrl_hdlr);
+	struct m2mx_dev *dev = ctx->dev;
+	enum ipu_rotate_mode rot_mode;
+	struct ipu_image *in, *out;
+	struct vb2_queue *vq;
+	bool hflip, vflip;
+	int rotation;
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	if (!vq)
+		return -EINVAL;
+
+	/* can't change rotation mid-streaming */
+	if (vb2_is_streaming(vq)) {
+		v4l2_err(&dev->v4l2_dev, "%s: not allowed while streaming\n",
+			 __func__);
+		return -EBUSY;
+	}
+
+	in = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	out = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	rotation = ctx->rotation;
+	hflip = ctx->hflip;
+	vflip = ctx->vflip;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		hflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_VFLIP:
+		vflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_ROTATE:
+		rotation = ctrl->val;
+		break;
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+
+	ret = ipu_degrees_to_rot_mode(&rot_mode, rotation, hflip, vflip);
+	if (ret)
+		return ret;
+
+	/*
+	 * make sure this rotation will work with current src/dest
+	 * rectangles before setting
+	 */
+	ret = ipu_image_convert_verify(in, out, rot_mode);
+	if (ret)
+		return ret;
+
+	ctx->rotation = rotation;
+	ctx->hflip = hflip;
+	ctx->vflip = vflip;
+	ctx->rot_mode = rot_mode;
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops m2mx_ctrl_ops = {
+	.s_ctrl = m2mx_s_ctrl,
+};
+
+static int m2mx_init_controls(struct m2mx_ctx *ctx)
+{
+	struct v4l2_ctrl_handler *hdlr = &ctx->ctrl_hdlr;
+	const struct v4l2_ctrl_config *c;
+	int i, ret;
+
+	v4l2_ctrl_handler_init(hdlr, M2MX_NUM_STD_CONTROLS);
+
+	for (i = 0; i < M2MX_NUM_STD_CONTROLS; i++) {
+		c = &m2mx_std_ctrl[i];
+		v4l2_ctrl_new_std(hdlr, &m2mx_ctrl_ops,
+				  c->id, c->min, c->max, c->step, c->def);
+	}
+
+	if (hdlr->error) {
+		ret = hdlr->error;
+		goto free_ctrls;
+	}
+
+	ctx->fh.ctrl_handler = hdlr;
+	ret = v4l2_ctrl_handler_setup(hdlr);
+	if (ret)
+		goto free_ctrls;
+
+	return 0;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(hdlr);
+	return ret;
+}
+
+static const struct v4l2_ioctl_ops m2mx_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt_vid_cap,
+
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_out	= vidioc_g_fmt_vid_out,
+	.vidioc_try_fmt_vid_out	= vidioc_try_fmt_vid_out,
+	.vidioc_s_fmt_vid_out	= vidioc_s_fmt_vid_out,
+
+	.vidioc_reqbufs		= vidioc_reqbufs,
+	.vidioc_querybuf	= vidioc_querybuf,
+
+	.vidioc_qbuf		= vidioc_qbuf,
+	.vidioc_dqbuf		= vidioc_dqbuf,
+	.vidioc_expbuf		= vidioc_expbuf,
+
+	.vidioc_streamon	= vidioc_streamon,
+	.vidioc_streamoff	= vidioc_streamoff,
+};
+
+/*
+ * Queue operations
+ */
+
+static int m2mx_queue_setup(struct vb2_queue *vq,
+			     unsigned int *nbuffers, unsigned int *nplanes,
+			     unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(vq);
+	struct ipu_image *image;
+	unsigned int count = *nbuffers;
+
+	image = get_image_data(ctx, vq->type);
+	if (!image)
+		return -EINVAL;
+
+	while (image->pix.sizeimage * count > MEM2MEM_VID_MEM_LIMIT)
+		count--;
+
+	*nplanes = 1;
+	*nbuffers = count;
+	sizes[0] = image->pix.sizeimage;
+
+	alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+	return 0;
+}
+
+static int m2mx_buf_prepare(struct vb2_buffer *vb)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct m2mx_dev *dev = ctx->dev;
+	struct ipu_image *image;
+
+	image = get_image_data(ctx, vb->vb2_queue->type);
+	if (!image)
+		return -EINVAL;
+
+	if (vb2_plane_size(vb, 0) < image->pix.sizeimage) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: data will not fit into plane (%lu < %lu)\n",
+			 __func__, vb2_plane_size(vb, 0),
+			 (long)image->pix.sizeimage);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, image->pix.sizeimage);
+
+	return 0;
+}
+
+static void m2mx_buf_queue(struct vb2_buffer *vb)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
+}
+
+static void m2mx_wait_prepare(struct vb2_queue *q)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+
+	m2mx_unlock(ctx);
+}
+
+static void m2mx_wait_finish(struct vb2_queue *q)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+
+	m2mx_lock(ctx);
+}
+
+static int m2mx_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+	struct ipu_image *pin, *pout;
+
+	/*
+	 * mem2mem requires streamon at both sides, capture and output,
+	 * and we only want to prepare the ipu-ic image converter once.
+	 */
+	if (ctx->image_converter_ready)
+		return 0;
+
+	pin = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	pout = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	ctx->ic_ctx = ipu_image_convert_prepare(ctx->ic, pin, pout,
+						ctx->rot_mode,
+						m2mx_convert_complete, ctx);
+	if (IS_ERR(ctx->ic_ctx))
+		return PTR_ERR(ctx->ic_ctx);
+
+	ctx->image_converter_ready = true;
+	return 0;
+}
+
+static void m2mx_stop_streaming(struct vb2_queue *q)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+
+	if (ctx->image_converter_ready) {
+		ipu_image_convert_unprepare(ctx->ic_ctx);
+		ctx->image_converter_ready = false;
+	}
+}
+
+static struct vb2_ops m2mx_qops = {
+	.queue_setup	 = m2mx_queue_setup,
+	.buf_prepare	 = m2mx_buf_prepare,
+	.buf_queue	 = m2mx_buf_queue,
+	.wait_prepare	 = m2mx_wait_prepare,
+	.wait_finish	 = m2mx_wait_finish,
+	.start_streaming = m2mx_start_streaming,
+	.stop_streaming  = m2mx_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct m2mx_ctx *ctx = priv;
+	int ret;
+
+	memset(src_vq, 0, sizeof(*src_vq));
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	src_vq->ops = &m2mx_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	memset(dst_vq, 0, sizeof(*dst_vq));
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	dst_vq->ops = &m2mx_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+	return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int m2mx_open(struct file *file)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct ipu_image *in, *out;
+	struct m2mx_ctx *ctx;
+	int ret = 0;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+	ctx->dev = dev;
+
+	ctx->ic = ipu_ic_get(dev->ipu, IC_TASK_POST_PROCESSOR);
+	if (IS_ERR(ctx->ic)) {
+		v4l2_err(&dev->v4l2_dev, "could not get IC PP\n");
+		ret = PTR_ERR(ctx->ic);
+		goto error_free;
+	}
+
+	v4l2_fh_init(&ctx->fh, dev->vfd);
+	file->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		ret = PTR_ERR(ctx->fh.m2m_ctx);
+		goto error_fh;
+	}
+
+	/*
+	 * set some defaults for output and capture image formats.
+	 * default for both is 640x480 RGB565.
+	 */
+	in = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	out = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	in->pix.width = out->pix.width = 640;
+	in->pix.height = out->pix.height = 480;
+	in->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+	out->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+	in->pix.sizeimage =
+		(in->pix.width * in->pix.height * 16) >> 3;
+	out->pix.sizeimage =
+		(out->pix.width * out->pix.height * 16) >> 3;
+
+	ret = m2mx_init_controls(ctx);
+	if (ret)
+		goto error_m2m_ctx;
+
+	mutex_unlock(&dev->dev_mutex);
+	return 0;
+
+error_m2m_ctx:
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+error_fh:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	ipu_ic_put(ctx->ic);
+error_free:
+	kfree(ctx);
+unlock:
+	mutex_unlock(&dev->dev_mutex);
+	return ret;
+}
+
+static int m2mx_release(struct file *file)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct m2mx_ctx *ctx = fh_to_ctx(file->private_data);
+
+	mutex_lock(&dev->dev_mutex);
+
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdlr);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	ipu_ic_put(ctx->ic);
+	kfree(ctx);
+
+	mutex_unlock(&dev->dev_mutex);
+	return 0;
+}
+
+static unsigned int m2mx_poll(struct file *file,
+			       struct poll_table_struct *wait)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct m2mx_ctx *ctx = fh_to_ctx(file->private_data);
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+
+	ret = v4l2_m2m_poll(file, ctx->fh.m2m_ctx, wait);
+
+	mutex_unlock(&dev->dev_mutex);
+	return ret;
+}
+
+static int m2mx_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct m2mx_ctx *ctx = fh_to_ctx(file->private_data);
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+
+	ret = v4l2_m2m_mmap(file, ctx->fh.m2m_ctx, vma);
+
+	mutex_unlock(&dev->dev_mutex);
+	return ret;
+}
+
+static const struct v4l2_file_operations m2mx_fops = {
+	.owner		= THIS_MODULE,
+	.open		= m2mx_open,
+	.release	= m2mx_release,
+	.poll		= m2mx_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= m2mx_mmap,
+};
+
+static struct video_device m2mx_videodev = {
+	.name		= MEM2MEM_NAME,
+	.fops		= &m2mx_fops,
+	.ioctl_ops	= &m2mx_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release,
+	.vfl_dir	= VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+	.device_run	= m2mx_device_run,
+	.job_abort	= m2mx_job_abort,
+	.lock		= m2mx_lock,
+	.unlock		= m2mx_unlock,
+};
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static struct ipu_soc *m2mx_get_ipu(struct m2mx_dev *dev,
+				     struct device_node *node)
+{
+	struct device_node *ipu_node;
+	struct device *ipu_dev;
+	struct ipu_soc *ipu;
+
+	ipu_node = of_parse_phandle(node, "ipu", 0);
+	if (!ipu_node) {
+		v4l2_err(&dev->v4l2_dev, "missing ipu phandle!\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	ipu_dev = bus_find_device(&platform_bus_type, NULL,
+				  ipu_node, of_dev_node_match);
+	of_node_put(ipu_node);
+
+	if (!ipu_dev) {
+		v4l2_err(&dev->v4l2_dev, "failed to find ipu device!\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	device_lock(ipu_dev);
+
+	if (!ipu_dev->driver || !try_module_get(ipu_dev->driver->owner)) {
+		ipu = ERR_PTR(-EPROBE_DEFER);
+		v4l2_warn(&dev->v4l2_dev, "IPU driver not loaded\n");
+		device_unlock(ipu_dev);
+		goto dev_put;
+	}
+
+	dev->ipu_dev = ipu_dev;
+	ipu = dev_get_drvdata(ipu_dev);
+
+	device_unlock(ipu_dev);
+	return ipu;
+dev_put:
+	put_device(ipu_dev);
+	return ipu;
+}
+
+static void m2mx_put_ipu(struct m2mx_dev *dev)
+{
+	if (!IS_ERR_OR_NULL(dev->ipu_dev)) {
+		module_put(dev->ipu_dev->driver->owner);
+		put_device(dev->ipu_dev);
+	}
+}
+
+static int m2mx_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct m2mx_dev *dev;
+	struct video_device *vfd;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		return ret;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+	/* get our IPU */
+	dev->ipu = m2mx_get_ipu(dev, node);
+	if (IS_ERR(dev->ipu)) {
+		v4l2_err(&dev->v4l2_dev, "could not get ipu\n");
+		ret = PTR_ERR(dev->ipu);
+		goto unreg_dev;
+	}
+
+	mutex_init(&dev->dev_mutex);
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto unreg_dev;
+	}
+
+	*vfd = m2mx_videodev;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+	vfd->lock = &dev->dev_mutex;
+
+	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR(dev->m2m_dev)) {
+		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(dev->m2m_dev);
+		video_device_release(vfd);
+		goto unreg_dev;
+	}
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		video_device_release(vfd);
+		goto rel_m2m;
+	}
+
+	video_set_drvdata(vfd, dev);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", m2mx_videodev.name);
+	dev->vfd = vfd;
+	v4l2_info(&dev->v4l2_dev,
+		  "Device registered as /dev/video%d, on ipu%d\n",
+		  vfd->num, ipu_get_num(dev->ipu));
+
+	platform_set_drvdata(pdev, dev);
+
+	dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+	if (IS_ERR(dev->alloc_ctx)) {
+		v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n");
+		ret = PTR_ERR(dev->alloc_ctx);
+		goto unreg_vdev;
+	}
+
+	return 0;
+
+unreg_vdev:
+	video_unregister_device(dev->vfd);
+rel_m2m:
+	v4l2_m2m_release(dev->m2m_dev);
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+	return ret;
+}
+
+static int m2mx_remove(struct platform_device *pdev)
+{
+	struct m2mx_dev *dev =
+		(struct m2mx_dev *)platform_get_drvdata(pdev);
+
+	v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME "\n");
+	m2mx_put_ipu(dev);
+	v4l2_m2m_release(dev->m2m_dev);
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+	video_unregister_device(dev->vfd);
+
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	return 0;
+}
+
+static const struct of_device_id m2mx_dt_ids[] = {
+	{ .compatible = "fsl,imx-video-mem2mem" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, m2mx_dt_ids);
+
+static struct platform_driver m2mx_pdrv = {
+	.probe		= m2mx_probe,
+	.remove		= m2mx_remove,
+	.driver		= {
+		.name	= MEM2MEM_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table	= m2mx_dt_ids,
+	},
+};
+
+module_platform_driver(m2mx_pdrv);
-- 
1.9.1


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

* [PATCH 38/38] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (36 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 37/38] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
@ 2016-06-14 22:49 ` Steve Longerbeam
  2016-06-15 10:43 ` [PATCH 00/38] i.MX5/6 Video Capture Jack Mitchell
                   ` (2 subsequent siblings)
  40 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-14 22:49 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Enable imx v4l2 staging drivers. For video capture on
the SabreAuto, the ADV7180 video decoder also requires the
i2c-mux-gpio and the max7310 port expander.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/configs/imx_v6_v7_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
index 21339ce..8b1590a 100644
--- a/arch/arm/configs/imx_v6_v7_defconfig
+++ b/arch/arm/configs/imx_v6_v7_defconfig
@@ -327,6 +327,8 @@ CONFIG_FSL_EDMA=y
 CONFIG_IMX_SDMA=y
 CONFIG_MXS_DMA=y
 CONFIG_STAGING=y
+CONFIG_STAGING_MEDIA=y
+CONFIG_VIDEO_IMX=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_IIO=y
 CONFIG_VF610_ADC=y
-- 
1.9.1


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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (37 preceding siblings ...)
  2016-06-14 22:49 ` [PATCH 38/38] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers Steve Longerbeam
@ 2016-06-15 10:43 ` Jack Mitchell
  2016-06-15 10:47   ` Hans Verkuil
  2016-06-16  1:37   ` Steve Longerbeam
  2016-06-28 18:54 ` Tim Harvey
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
  40 siblings, 2 replies; 105+ messages in thread
From: Jack Mitchell @ 2016-06-15 10:43 UTC (permalink / raw)
  To: Steve Longerbeam, linux-media; +Cc: Steve Longerbeam

On 14/06/16 23:48, Steve Longerbeam wrote:
> Tested on imx6q SabreAuto with ADV7180, and imx6q SabreSD with
> mipi-csi2 OV5640. There is device-tree support also for imx6qdl
> SabreLite, but that is not tested. Also, this driver should
> theoretically work on i.MX5 targets, but that is also untested.

I tested this on a sabrelite with ov5640 MIPI camera. I managed to 
capture multiple images of a few different resolutions without issue. I 
did get a kernel splat at one point though:

[  905.469680] WARNING: CPU: 3 PID: 349 at 
drivers/media/v4l2-core/v4l2-ioctl.c:2174 v4l_cropcap+0x15c/0x190
[  905.482308] Modules linked in: dw_hdmi_ahb_audio evbug
[  905.489079] CPU: 3 PID: 349 Comm: capture-example Tainted: G        W 
       4.7.0-rc1-00095-g921736c0-dirty #7
[  905.502069] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[  905.510116] Backtrace:
[  905.514066] [<c010c1b0>] (dump_backtrace) from [<c010c3a8>] 
(show_stack+0x18/0x1c)
[  905.523161]  r7:00000000 r6:600f0013 r5:00000000 r4:c0e21bbc
[  905.530442] [<c010c390>] (show_stack) from [<c03e9468>] 
(dump_stack+0xb4/0xe8)
[  905.539210] [<c03e93b4>] (dump_stack) from [<c012473c>] 
(__warn+0xd8/0x104)
[  905.547680]  r9:c06219b0 r8:0000087e r7:00000009 r6:c0c51b10 
r5:00000000 r4:00000000
[  905.557086] [<c0124664>] (__warn) from [<c012481c>] 
(warn_slowpath_null+0x28/0x30)
[  905.566172]  r9:c5e09680 r8:00000000 r7:c63c0e00 r6:c5e09680 
r5:c0a7b2fc r4:c6223e18
[  905.575577] [<c01247f4>] (warn_slowpath_null) from [<c06219b0>] 
(v4l_cropcap+0x15c/0x190)
[  905.585296] [<c0621854>] (v4l_cropcap) from [<c0623678>] 
(__video_do_ioctl+0x280/0x300)
[  905.594824]  r8:00000000 r7:c6248000 r6:c02c563a r5:00000003 r4:c0621854
[  905.603176] [<c06233f8>] (__video_do_ioctl) from [<c06230c4>] 
(video_usercopy+0x208/0x520)
[  905.612994]  r10:c6223e18 r9:c5e09680 r8:bef3fb60 r7:00000000 
r6:00000000 r5:0000002c
[  905.622542]  r4:c02c563a
[  905.626692] [<c0622ebc>] (video_usercopy) from [<c06233f0>] 
(video_ioctl2+0x14/0x1c)
[  905.636129]  r10:00000000 r9:c6222000 r8:c62e0088 r7:bef3fb60 
r6:c02c563a r5:c5e09680
[  905.645837]  r4:c6248000
[  905.650146] [<c06233dc>] (video_ioctl2) from [<c061e098>] 
(v4l2_ioctl+0xac/0xe8)
[  905.659402] [<c061dfec>] (v4l2_ioctl) from [<c022c2c8>] 
(do_vfs_ioctl+0x9c/0xa28)
[  905.668768]  r9:c6222000 r8:00000003 r7:c022cc90 r6:c5e09680 
r5:c61fcc68 r4:bef3fb60
[  905.678598] [<c022c22c>] (do_vfs_ioctl) from [<c022cc90>] 
(SyS_ioctl+0x3c/0x64)
[  905.687898]  r10:00000000 r9:c6222000 r8:bef3fb60 r7:c02c563a 
r6:c5e09680 r5:00000003
[  905.697911]  r4:c5e09680
[  905.702505] [<c022cc54>] (SyS_ioctl) from [<c0107c20>] 
(ret_fast_syscall+0x0/0x1c)
[  905.712135]  r9:c6222000 r8:c0107dc4 r7:00000036 r6:000107cc 
r5:00000000 r4:00012170
[  905.722143] ---[ end trace 772a5fdfa424cbd1 ]---

Trying to use a user pointer rather than mmap also fails and causes a 
kernel splat.

Apart from that and a few v4l2-compliance tests failing which you 
already mentioned, it seems to work OK. I'll try and do some more 
testing and see if I can come back with some more feedback.

Tested-by: Jack Mitchell <jack@tuxable.co.uk>

Cheers,
Jack.

>
> Not run through v4l2-compliance yet, but that is in my queue.
>
>
> Philipp Zabel (2):
>   ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their
>     connections
>   media: imx: Add video switch
>
> Steve Longerbeam (35):
>   gpu: ipu-v3: Add Video Deinterlacer unit
>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>   gpu: ipu-v3: Add ipu_get_num()
>   gpu: ipu-v3: Add IDMA channel linking support
>   gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>   gpu: ipu-v3: Add VDI input IDMAC channels
>   gpu: ipu-v3: Add ipu_csi_set_src()
>   gpu: ipu-v3: Add ipu_ic_set_src()
>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>   gpu: ipu-v3: Fix IRT usage
>   gpu: ipu-ic: Add complete image conversion support with tiling
>   gpu: ipu-ic: allow multiple handles to ic
>   gpu: ipu-v3: rename CSI client device
>   ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
>   ARM: dts: imx6-sabrelite: add video capture ports and connections
>   ARM: dts: imx6-sabresd: add video capture ports and connections
>   ARM: dts: imx6-sabreauto: create i2cmux for i2c3
>   ARM: dts: imx6-sabreauto: add reset-gpios property for max7310
>   ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
>   ARM: dts: imx6-sabreauto: add video capture ports and connections
>   ARM: dts: imx6qdl: add mem2mem device for sabre* boards
>   gpio: pca953x: Add reset-gpios property
>   clocksource/drivers/imx: add input capture support
>   v4l: Add signal lock status to source change events
>   media: Add camera interface driver for i.MX5/6
>   media: imx: Add MIPI CSI-2 Receiver driver
>   media: imx: Add support for MIPI CSI-2 OV5640
>   media: imx: Add support for Parallel OV5642
>   media: imx: Add support for ADV7180 Video Decoder
>   media: adv7180: add power pin control
>   media: adv7180: implement g_parm
>   media: Add i.MX5/6 mem2mem driver
>   ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
>
> Suresh Dhandapani (1):
>   gpu: ipu-v3: Fix CSI0 blur in NTSC format
>
>  Documentation/DocBook/media/v4l/vidioc-dqevent.xml |   12 +-
>  Documentation/devicetree/bindings/media/imx.txt    |  449 ++
>  Documentation/video4linux/imx_camera.txt           |  243 ++
>  arch/arm/boot/dts/imx6dl-sabresd.dts               |   44 +
>  arch/arm/boot/dts/imx6dl.dtsi                      |  183 +
>  arch/arm/boot/dts/imx6q-sabreauto.dts              |    7 +
>  arch/arm/boot/dts/imx6q-sabrelite.dts              |    6 +
>  arch/arm/boot/dts/imx6q-sabresd.dts                |   22 +
>  arch/arm/boot/dts/imx6q.dtsi                       |  120 +
>  arch/arm/boot/dts/imx6qdl-sabreauto.dtsi           |  166 +-
>  arch/arm/boot/dts/imx6qdl-sabrelite.dtsi           |   95 +
>  arch/arm/boot/dts/imx6qdl-sabresd.dtsi             |  145 +-
>  arch/arm/boot/dts/imx6qdl.dtsi                     |   13 +
>  arch/arm/configs/imx_v6_v7_defconfig               |    2 +
>  drivers/clocksource/timer-imx-gpt.c                |  463 ++-
>  drivers/gpio/gpio-pca953x.c                        |   28 +
>  drivers/gpu/ipu-v3/Makefile                        |    2 +-
>  drivers/gpu/ipu-v3/ipu-common.c                    |  155 +-
>  drivers/gpu/ipu-v3/ipu-cpmem.c                     |   13 +
>  drivers/gpu/ipu-v3/ipu-csi.c                       |   36 +-
>  drivers/gpu/ipu-v3/ipu-ic.c                        | 1769 +++++++-
>  drivers/gpu/ipu-v3/ipu-prv.h                       |    7 +
>  drivers/gpu/ipu-v3/ipu-vdi.c                       |  266 ++
>  drivers/media/i2c/adv7180.c                        |   73 +
>  drivers/staging/media/Kconfig                      |    2 +
>  drivers/staging/media/Makefile                     |    1 +
>  drivers/staging/media/imx/Kconfig                  |   35 +
>  drivers/staging/media/imx/Makefile                 |    2 +
>  drivers/staging/media/imx/capture/Kconfig          |   42 +
>  drivers/staging/media/imx/capture/Makefile         |   10 +
>  drivers/staging/media/imx/capture/adv7180.c        | 1533 +++++++
>  drivers/staging/media/imx/capture/imx-camif.c      | 2496 +++++++++++
>  drivers/staging/media/imx/capture/imx-camif.h      |  281 ++
>  drivers/staging/media/imx/capture/imx-csi.c        |  195 +
>  drivers/staging/media/imx/capture/imx-ic-prpenc.c  |  660 +++
>  drivers/staging/media/imx/capture/imx-of.c         |  354 ++
>  drivers/staging/media/imx/capture/imx-of.h         |   18 +
>  drivers/staging/media/imx/capture/imx-smfc.c       |  505 +++
>  drivers/staging/media/imx/capture/imx-vdic.c       |  994 +++++
>  .../staging/media/imx/capture/imx-video-switch.c   |  348 ++
>  drivers/staging/media/imx/capture/mipi-csi2.c      |  373 ++
>  drivers/staging/media/imx/capture/ov5640-mipi.c    | 2318 +++++++++++
>  drivers/staging/media/imx/capture/ov5642.c         | 4333 ++++++++++++++++++++
>  drivers/staging/media/imx/m2m/Makefile             |    1 +
>  drivers/staging/media/imx/m2m/imx-m2m.c            | 1049 +++++
>  include/linux/mxc_icap.h                           |   20 +
>  include/media/imx.h                                |   15 +
>  include/uapi/Kbuild                                |    1 +
>  include/uapi/linux/v4l2-controls.h                 |    8 +
>  include/uapi/linux/videodev2.h                     |    1 +
>  include/uapi/media/Kbuild                          |    3 +
>  include/uapi/media/adv718x.h                       |   42 +
>  include/uapi/media/imx.h                           |   22 +
>  include/video/imx-ipu-v3.h                         |   96 +-
>  54 files changed, 19946 insertions(+), 131 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/media/imx.txt
>  create mode 100644 Documentation/video4linux/imx_camera.txt
>  create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c
>  create mode 100644 drivers/staging/media/imx/Kconfig
>  create mode 100644 drivers/staging/media/imx/Makefile
>  create mode 100644 drivers/staging/media/imx/capture/Kconfig
>  create mode 100644 drivers/staging/media/imx/capture/Makefile
>  create mode 100644 drivers/staging/media/imx/capture/adv7180.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-camif.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-camif.h
>  create mode 100644 drivers/staging/media/imx/capture/imx-csi.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-ic-prpenc.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-of.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-of.h
>  create mode 100644 drivers/staging/media/imx/capture/imx-smfc.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-vdic.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c
>  create mode 100644 drivers/staging/media/imx/capture/mipi-csi2.c
>  create mode 100644 drivers/staging/media/imx/capture/ov5640-mipi.c
>  create mode 100644 drivers/staging/media/imx/capture/ov5642.c
>  create mode 100644 drivers/staging/media/imx/m2m/Makefile
>  create mode 100644 drivers/staging/media/imx/m2m/imx-m2m.c
>  create mode 100644 include/linux/mxc_icap.h
>  create mode 100644 include/media/imx.h
>  create mode 100644 include/uapi/media/Kbuild
>  create mode 100644 include/uapi/media/adv718x.h
>  create mode 100644 include/uapi/media/imx.h
>

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-15 10:43 ` [PATCH 00/38] i.MX5/6 Video Capture Jack Mitchell
@ 2016-06-15 10:47   ` Hans Verkuil
  2016-06-16  1:37   ` Steve Longerbeam
  1 sibling, 0 replies; 105+ messages in thread
From: Hans Verkuil @ 2016-06-15 10:47 UTC (permalink / raw)
  To: Jack Mitchell, Steve Longerbeam, linux-media; +Cc: Steve Longerbeam

On 06/15/16 12:43, Jack Mitchell wrote:
> On 14/06/16 23:48, Steve Longerbeam wrote:
>> Tested on imx6q SabreAuto with ADV7180, and imx6q SabreSD with
>> mipi-csi2 OV5640. There is device-tree support also for imx6qdl
>> SabreLite, but that is not tested. Also, this driver should
>> theoretically work on i.MX5 targets, but that is also untested.
> 
> I tested this on a sabrelite with ov5640 MIPI camera. I managed to capture multiple images of a few different resolutions without issue. I did get a kernel splat at one point though:
> 
> [  905.469680] WARNING: CPU: 3 PID: 349 at drivers/media/v4l2-core/v4l2-ioctl.c:2174 v4l_cropcap+0x15c/0x190

Known bug. Fix is waiting to be merged.

Regards,

	Hans

> [  905.482308] Modules linked in: dw_hdmi_ahb_audio evbug
> [  905.489079] CPU: 3 PID: 349 Comm: capture-example Tainted: G        W       4.7.0-rc1-00095-g921736c0-dirty #7
> [  905.502069] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
> [  905.510116] Backtrace:
> [  905.514066] [<c010c1b0>] (dump_backtrace) from [<c010c3a8>] (show_stack+0x18/0x1c)
> [  905.523161]  r7:00000000 r6:600f0013 r5:00000000 r4:c0e21bbc
> [  905.530442] [<c010c390>] (show_stack) from [<c03e9468>] (dump_stack+0xb4/0xe8)
> [  905.539210] [<c03e93b4>] (dump_stack) from [<c012473c>] (__warn+0xd8/0x104)
> [  905.547680]  r9:c06219b0 r8:0000087e r7:00000009 r6:c0c51b10 r5:00000000 r4:00000000
> [  905.557086] [<c0124664>] (__warn) from [<c012481c>] (warn_slowpath_null+0x28/0x30)
> [  905.566172]  r9:c5e09680 r8:00000000 r7:c63c0e00 r6:c5e09680 r5:c0a7b2fc r4:c6223e18
> [  905.575577] [<c01247f4>] (warn_slowpath_null) from [<c06219b0>] (v4l_cropcap+0x15c/0x190)
> [  905.585296] [<c0621854>] (v4l_cropcap) from [<c0623678>] (__video_do_ioctl+0x280/0x300)
> [  905.594824]  r8:00000000 r7:c6248000 r6:c02c563a r5:00000003 r4:c0621854
> [  905.603176] [<c06233f8>] (__video_do_ioctl) from [<c06230c4>] (video_usercopy+0x208/0x520)
> [  905.612994]  r10:c6223e18 r9:c5e09680 r8:bef3fb60 r7:00000000 r6:00000000 r5:0000002c
> [  905.622542]  r4:c02c563a
> [  905.626692] [<c0622ebc>] (video_usercopy) from [<c06233f0>] (video_ioctl2+0x14/0x1c)
> [  905.636129]  r10:00000000 r9:c6222000 r8:c62e0088 r7:bef3fb60 r6:c02c563a r5:c5e09680
> [  905.645837]  r4:c6248000
> [  905.650146] [<c06233dc>] (video_ioctl2) from [<c061e098>] (v4l2_ioctl+0xac/0xe8)
> [  905.659402] [<c061dfec>] (v4l2_ioctl) from [<c022c2c8>] (do_vfs_ioctl+0x9c/0xa28)
> [  905.668768]  r9:c6222000 r8:00000003 r7:c022cc90 r6:c5e09680 r5:c61fcc68 r4:bef3fb60
> [  905.678598] [<c022c22c>] (do_vfs_ioctl) from [<c022cc90>] (SyS_ioctl+0x3c/0x64)
> [  905.687898]  r10:00000000 r9:c6222000 r8:bef3fb60 r7:c02c563a r6:c5e09680 r5:00000003
> [  905.697911]  r4:c5e09680
> [  905.702505] [<c022cc54>] (SyS_ioctl) from [<c0107c20>] (ret_fast_syscall+0x0/0x1c)
> [  905.712135]  r9:c6222000 r8:c0107dc4 r7:00000036 r6:000107cc r5:00000000 r4:00012170
> [  905.722143] ---[ end trace 772a5fdfa424cbd1 ]---
> 
> Trying to use a user pointer rather than mmap also fails and causes a kernel splat.
> 
> Apart from that and a few v4l2-compliance tests failing which you already mentioned, it seems to work OK. I'll try and do some more testing and see if I can come back with some more feedback.
> 
> Tested-by: Jack Mitchell <jack@tuxable.co.uk>
> 
> Cheers,
> Jack.
> 
>>
>> Not run through v4l2-compliance yet, but that is in my queue.
>>
>>
>> Philipp Zabel (2):
>>   ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their
>>     connections
>>   media: imx: Add video switch
>>
>> Steve Longerbeam (35):
>>   gpu: ipu-v3: Add Video Deinterlacer unit
>>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>>   gpu: ipu-v3: Add ipu_get_num()
>>   gpu: ipu-v3: Add IDMA channel linking support
>>   gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>>   gpu: ipu-v3: Add VDI input IDMAC channels
>>   gpu: ipu-v3: Add ipu_csi_set_src()
>>   gpu: ipu-v3: Add ipu_ic_set_src()
>>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>>   gpu: ipu-v3: Fix IRT usage
>>   gpu: ipu-ic: Add complete image conversion support with tiling
>>   gpu: ipu-ic: allow multiple handles to ic
>>   gpu: ipu-v3: rename CSI client device
>>   ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
>>   ARM: dts: imx6-sabrelite: add video capture ports and connections
>>   ARM: dts: imx6-sabresd: add video capture ports and connections
>>   ARM: dts: imx6-sabreauto: create i2cmux for i2c3
>>   ARM: dts: imx6-sabreauto: add reset-gpios property for max7310
>>   ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
>>   ARM: dts: imx6-sabreauto: add video capture ports and connections
>>   ARM: dts: imx6qdl: add mem2mem device for sabre* boards
>>   gpio: pca953x: Add reset-gpios property
>>   clocksource/drivers/imx: add input capture support
>>   v4l: Add signal lock status to source change events
>>   media: Add camera interface driver for i.MX5/6
>>   media: imx: Add MIPI CSI-2 Receiver driver
>>   media: imx: Add support for MIPI CSI-2 OV5640
>>   media: imx: Add support for Parallel OV5642
>>   media: imx: Add support for ADV7180 Video Decoder
>>   media: adv7180: add power pin control
>>   media: adv7180: implement g_parm
>>   media: Add i.MX5/6 mem2mem driver
>>   ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
>>
>> Suresh Dhandapani (1):
>>   gpu: ipu-v3: Fix CSI0 blur in NTSC format
>>
>>  Documentation/DocBook/media/v4l/vidioc-dqevent.xml |   12 +-
>>  Documentation/devicetree/bindings/media/imx.txt    |  449 ++
>>  Documentation/video4linux/imx_camera.txt           |  243 ++
>>  arch/arm/boot/dts/imx6dl-sabresd.dts               |   44 +
>>  arch/arm/boot/dts/imx6dl.dtsi                      |  183 +
>>  arch/arm/boot/dts/imx6q-sabreauto.dts              |    7 +
>>  arch/arm/boot/dts/imx6q-sabrelite.dts              |    6 +
>>  arch/arm/boot/dts/imx6q-sabresd.dts                |   22 +
>>  arch/arm/boot/dts/imx6q.dtsi                       |  120 +
>>  arch/arm/boot/dts/imx6qdl-sabreauto.dtsi           |  166 +-
>>  arch/arm/boot/dts/imx6qdl-sabrelite.dtsi           |   95 +
>>  arch/arm/boot/dts/imx6qdl-sabresd.dtsi             |  145 +-
>>  arch/arm/boot/dts/imx6qdl.dtsi                     |   13 +
>>  arch/arm/configs/imx_v6_v7_defconfig               |    2 +
>>  drivers/clocksource/timer-imx-gpt.c                |  463 ++-
>>  drivers/gpio/gpio-pca953x.c                        |   28 +
>>  drivers/gpu/ipu-v3/Makefile                        |    2 +-
>>  drivers/gpu/ipu-v3/ipu-common.c                    |  155 +-
>>  drivers/gpu/ipu-v3/ipu-cpmem.c                     |   13 +
>>  drivers/gpu/ipu-v3/ipu-csi.c                       |   36 +-
>>  drivers/gpu/ipu-v3/ipu-ic.c                        | 1769 +++++++-
>>  drivers/gpu/ipu-v3/ipu-prv.h                       |    7 +
>>  drivers/gpu/ipu-v3/ipu-vdi.c                       |  266 ++
>>  drivers/media/i2c/adv7180.c                        |   73 +
>>  drivers/staging/media/Kconfig                      |    2 +
>>  drivers/staging/media/Makefile                     |    1 +
>>  drivers/staging/media/imx/Kconfig                  |   35 +
>>  drivers/staging/media/imx/Makefile                 |    2 +
>>  drivers/staging/media/imx/capture/Kconfig          |   42 +
>>  drivers/staging/media/imx/capture/Makefile         |   10 +
>>  drivers/staging/media/imx/capture/adv7180.c        | 1533 +++++++
>>  drivers/staging/media/imx/capture/imx-camif.c      | 2496 +++++++++++
>>  drivers/staging/media/imx/capture/imx-camif.h      |  281 ++
>>  drivers/staging/media/imx/capture/imx-csi.c        |  195 +
>>  drivers/staging/media/imx/capture/imx-ic-prpenc.c  |  660 +++
>>  drivers/staging/media/imx/capture/imx-of.c         |  354 ++
>>  drivers/staging/media/imx/capture/imx-of.h         |   18 +
>>  drivers/staging/media/imx/capture/imx-smfc.c       |  505 +++
>>  drivers/staging/media/imx/capture/imx-vdic.c       |  994 +++++
>>  .../staging/media/imx/capture/imx-video-switch.c   |  348 ++
>>  drivers/staging/media/imx/capture/mipi-csi2.c      |  373 ++
>>  drivers/staging/media/imx/capture/ov5640-mipi.c    | 2318 +++++++++++
>>  drivers/staging/media/imx/capture/ov5642.c         | 4333 ++++++++++++++++++++
>>  drivers/staging/media/imx/m2m/Makefile             |    1 +
>>  drivers/staging/media/imx/m2m/imx-m2m.c            | 1049 +++++
>>  include/linux/mxc_icap.h                           |   20 +
>>  include/media/imx.h                                |   15 +
>>  include/uapi/Kbuild                                |    1 +
>>  include/uapi/linux/v4l2-controls.h                 |    8 +
>>  include/uapi/linux/videodev2.h                     |    1 +
>>  include/uapi/media/Kbuild                          |    3 +
>>  include/uapi/media/adv718x.h                       |   42 +
>>  include/uapi/media/imx.h                           |   22 +
>>  include/video/imx-ipu-v3.h                         |   96 +-
>>  54 files changed, 19946 insertions(+), 131 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/media/imx.txt
>>  create mode 100644 Documentation/video4linux/imx_camera.txt
>>  create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c
>>  create mode 100644 drivers/staging/media/imx/Kconfig
>>  create mode 100644 drivers/staging/media/imx/Makefile
>>  create mode 100644 drivers/staging/media/imx/capture/Kconfig
>>  create mode 100644 drivers/staging/media/imx/capture/Makefile
>>  create mode 100644 drivers/staging/media/imx/capture/adv7180.c
>>  create mode 100644 drivers/staging/media/imx/capture/imx-camif.c
>>  create mode 100644 drivers/staging/media/imx/capture/imx-camif.h
>>  create mode 100644 drivers/staging/media/imx/capture/imx-csi.c
>>  create mode 100644 drivers/staging/media/imx/capture/imx-ic-prpenc.c
>>  create mode 100644 drivers/staging/media/imx/capture/imx-of.c
>>  create mode 100644 drivers/staging/media/imx/capture/imx-of.h
>>  create mode 100644 drivers/staging/media/imx/capture/imx-smfc.c
>>  create mode 100644 drivers/staging/media/imx/capture/imx-vdic.c
>>  create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c
>>  create mode 100644 drivers/staging/media/imx/capture/mipi-csi2.c
>>  create mode 100644 drivers/staging/media/imx/capture/ov5640-mipi.c
>>  create mode 100644 drivers/staging/media/imx/capture/ov5642.c
>>  create mode 100644 drivers/staging/media/imx/m2m/Makefile
>>  create mode 100644 drivers/staging/media/imx/m2m/imx-m2m.c
>>  create mode 100644 include/linux/mxc_icap.h
>>  create mode 100644 include/media/imx.h
>>  create mode 100644 include/uapi/media/Kbuild
>>  create mode 100644 include/uapi/media/adv718x.h
>>  create mode 100644 include/uapi/media/imx.h
>>
> -- 
> 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] 105+ messages in thread

* Re: [PATCH 35/38] media: adv7180: add power pin control
  2016-06-14 22:49 ` [PATCH 35/38] media: adv7180: add power pin control Steve Longerbeam
@ 2016-06-15 16:05   ` Lars-Peter Clausen
  2016-06-16  1:34     ` Steve Longerbeam
  0 siblings, 1 reply; 105+ messages in thread
From: Lars-Peter Clausen @ 2016-06-15 16:05 UTC (permalink / raw)
  To: Steve Longerbeam, linux-media; +Cc: Steve Longerbeam

On 06/15/2016 12:49 AM, Steve Longerbeam wrote:
> +		usleep_range(5000, 5001);

That's kind of not how usleep_range() (the emphasis is on range) is supposed
to be used. You typically dont care too much about the upper limit here so
something like maybe 10000 is more appropriate.


> +static int adv7180_of_parse(struct adv7180_state *state)
> +{
> +	struct i2c_client *client = state->client;
> +	struct device_node *np = client->dev.of_node;
> +	int ret;
> +
> +	ret = of_get_named_gpio(np, "pwdn-gpio", 0);
> +
> +	if (gpio_is_valid(ret)) {
> +		state->pwdn_gpio = ret;
> +		ret = devm_gpio_request_one(&client->dev,
> +					    state->pwdn_gpio,
> +					    GPIOF_OUT_INIT_HIGH,
> +					    "adv7180_pwdn");


This should use the new GPIO descriptor API. That will also make the code
devicetree independent. Otherwise patch looks OK.

> +		if (ret < 0) {
> +			v4l_err(client, "request for power pin failed\n");
> +			return ret;
> +		}
> +	} else {
> +		if (ret == -EPROBE_DEFER)
> +			return ret;
> +		/* assume a power-down gpio is not required */
> +		state->pwdn_gpio = -1;
> +	}
> +
> +	return 0;
> +}


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

* Re: [PATCH 35/38] media: adv7180: add power pin control
  2016-06-15 16:05   ` Lars-Peter Clausen
@ 2016-06-16  1:34     ` Steve Longerbeam
  0 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-16  1:34 UTC (permalink / raw)
  To: Lars-Peter Clausen, Steve Longerbeam, linux-media

Hi Lars,

On 06/15/2016 09:05 AM, Lars-Peter Clausen wrote:
> On 06/15/2016 12:49 AM, Steve Longerbeam wrote:
>> +		usleep_range(5000, 5001);
> That's kind of not how usleep_range() (the emphasis is on range) is supposed
> to be used. You typically dont care too much about the upper limit here so
> something like maybe 10000 is more appropriate.

Good point, I fixed this as well as all other instances of
usleep_range() in the patch set.

>
>> +static int adv7180_of_parse(struct adv7180_state *state)
>> +{
>> +	struct i2c_client *client = state->client;
>> +	struct device_node *np = client->dev.of_node;
>> +	int ret;
>> +
>> +	ret = of_get_named_gpio(np, "pwdn-gpio", 0);
>> +
>> +	if (gpio_is_valid(ret)) {
>> +		state->pwdn_gpio = ret;
>> +		ret = devm_gpio_request_one(&client->dev,
>> +					    state->pwdn_gpio,
>> +					    GPIOF_OUT_INIT_HIGH,
>> +					    "adv7180_pwdn");
>
> This should use the new GPIO descriptor API. That will also make the code
> devicetree independent. Otherwise patch looks OK.

Thanks for the heads-up. I converted to gpiod here, and in all other
patches in the set that were using the deprecated API. Also took the
time to review the active low/high flags in the device tree, and made
sure they are correct and are using the explicit flags GPIO_ACTIVE_*.

The changes are in a new branch mx6-media-staging-v2.1 in my fork
on github (git@github.com:slongerbeam/mediatree.git).

Retested on SabreSD and SabreAuto, still working as before.

Steve


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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-15 10:43 ` [PATCH 00/38] i.MX5/6 Video Capture Jack Mitchell
  2016-06-15 10:47   ` Hans Verkuil
@ 2016-06-16  1:37   ` Steve Longerbeam
  2016-06-16  9:49     ` Jack Mitchell
  1 sibling, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-16  1:37 UTC (permalink / raw)
  To: Jack Mitchell, Steve Longerbeam, linux-media

Hi Jack,

On 06/15/2016 03:43 AM, Jack Mitchell wrote:
> <snip>
> Trying to use a user pointer rather than mmap also fails and causes a kernel splat.
>

Hmm, I've tested userptr with the mem2mem driver, but maybe never
with video capture. I tried "v4l2-ctl -d/dev/video0 --stream-user=8" but
that returns "VIDIOC_QBUF: failed: Invalid argument", haven't tracked
down why (could be a bug in v4l2-ctl). Can you share the splat?


> Apart from that and a few v4l2-compliance tests failing which you already mentioned, it seems to work OK. I'll try and do some more testing and see if I can come back with some more feedback.

Thanks!


Steve


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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-14 22:49 ` [PATCH 19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections Steve Longerbeam
@ 2016-06-16  8:32   ` Gary Bisson
  2016-06-17 15:18     ` Gary Bisson
  0 siblings, 1 reply; 105+ messages in thread
From: Gary Bisson @ 2016-06-16  8:32 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-media, Steve Longerbeam

Steve, All,

On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
> Defines the host video capture device node and an OV5642 camera sensor
> node on i2c2. The host capture device connects to the OV5642 via the
> parallel-bus mux input on the ipu1_csi0_mux.
> 
> Note there is a pin conflict with GPIO6. This pin functions as a power
> input pin to the OV5642, but ENET requires it to wake-up the ARM cores
> on normal RX and TX packet done events (see 6261c4c8). So by default,
> capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
> Ethernet will still work just not quite as well.

Actually the following patch fixes this issue and has already been
applied on Shawn's tree:
https://patchwork.kernel.org/patch/9153523/

Also, this follow-up patch declared the HW workaround for SabreLite:
https://patchwork.kernel.org/patch/9153525/

So ideally, once those two patches land on your base tree, you could get
rid of the #define and remove the HW workaround declaration.

Finally, I'll test the series on Sabre-Lite this week.

Regards,
Gary

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-16  1:37   ` Steve Longerbeam
@ 2016-06-16  9:49     ` Jack Mitchell
  2016-06-16 17:02       ` Steve Longerbeam
  0 siblings, 1 reply; 105+ messages in thread
From: Jack Mitchell @ 2016-06-16  9:49 UTC (permalink / raw)
  To: Steve Longerbeam, Steve Longerbeam, linux-media


On 16/06/16 02:37, Steve Longerbeam wrote:
> Hi Jack,
>
> On 06/15/2016 03:43 AM, Jack Mitchell wrote:
>> <snip>
>> Trying to use a user pointer rather than mmap also fails and causes a kernel splat.
>>
>
> Hmm, I've tested userptr with the mem2mem driver, but maybe never
> with video capture. I tried "v4l2-ctl -d/dev/video0 --stream-user=8" but
> that returns "VIDIOC_QBUF: failed: Invalid argument", haven't tracked
> down why (could be a bug in v4l2-ctl). Can you share the splat?
>

On re-checking the splat was the same v4l_cropcap that was mentioned 
before so I don't think it's related. The error I get back is:

VIDIOC_QBUF error 22, Invalid argument

I'm using the example program the the v4l2 docs [1].

Cheers,
Jack

[1] https://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html

>
>> Apart from that and a few v4l2-compliance tests failing which you already mentioned, it seems to work OK. I'll try and do some more testing and see if I can come back with some more feedback.
>
> Thanks!
>
>
> Steve
>

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

* Re: [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder
  2016-06-14 22:49 ` [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder Steve Longerbeam
@ 2016-06-16 11:33   ` Lars-Peter Clausen
  2016-06-17 20:33     ` Steve Longerbeam
  0 siblings, 1 reply; 105+ messages in thread
From: Lars-Peter Clausen @ 2016-06-16 11:33 UTC (permalink / raw)
  To: Steve Longerbeam, linux-media; +Cc: Steve Longerbeam

On 06/15/2016 12:49 AM, Steve Longerbeam wrote:
> This driver is based on adv7180.c from Freescale imx_3.10.17_1.0.0_beta
> branch, modified heavily for code cleanup and converted from int-device
> to subdev.

We already have a driver for the adv7180 upstream, also using the subdev
API. Is there anything that can be done with this new driver that can't be
done with the other one. And if it is are there any blockers that would
prevent us from adding the missing features to the upstream adv7180?

I know that the driver in the Freescale tree used to have bits that made it
specially tailored to the iMX6. But these bits seem to be mostly gone in
this version of the driver.

I'm slightly concerned about the conflicting nature of these drivers. Both
attach to the same device ID/DT compatible string and register slightly
different userspace ABIs. I'd like to avoid ending up in a situation where
we have dependencies on both ABIs and can no longer converge.

- Lars


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

* Re: [PATCH 31/38] media: imx: Add video switch
  2016-06-14 22:49 ` [PATCH 31/38] media: imx: Add video switch Steve Longerbeam
@ 2016-06-16 16:13   ` Ian Arkver
  2016-06-17 20:14     ` Steve Longerbeam
  0 siblings, 1 reply; 105+ messages in thread
From: Ian Arkver @ 2016-06-16 16:13 UTC (permalink / raw)
  To: Steve Longerbeam, linux-media
  Cc: Philipp Zabel, Sascha Hauer, Steve Longerbeam

For me this fails when I try to enable both video muxes (mx6dl, though 
mx6q should be the same).

I get a sysfs duplicate name failure for 34.videomux. I realise passing 
the GPR13 register offset and a bitfield mask as a tuple in the reg 
value of the of_node is handy, but how should we account for multiple 
devices with the same name and address?

A quick and dirty hack would be to have of_get_reg_field do something like

	field->reg = reg_bit_mask[0] & 0xff;

and then use values in the DT that differ in the bits masked off, but 
there must be a nicer way.

Trace below, fyi. This is from the v2 patches posted here, not your v2.1 
tree.

Regards,
IanJ


[    0.096004] ------------[ cut here ]------------
[    0.096035] WARNING: CPU: 0 PID: 1 at 
/home/ian/tx6/yoctomaster/build/tmp/work-shared/tx6u-vid/kernel-source/fs/sysfs/dir.c:31 
sysfs_warn_dup+0x70/0x80
[    0.096046] sysfs: cannot create duplicate filename 
'/devices/soc0/34.videomux'
[    0.096053] Modules linked in:
[    0.096071] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 
4.7.0-rc1-yocto-standard #1
[    0.096079] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[    0.096116] [<80018d8c>] (unwind_backtrace) from [<8001427c>] 
(show_stack+0x18/0x1c)
[    0.096138] [<8001427c>] (show_stack) from [<802c85fc>] 
(dump_stack+0x88/0x9c)
[    0.096157] [<802c85fc>] (dump_stack) from [<80024d90>] 
(__warn+0xf4/0x10c)
[    0.096175] [<80024d90>] (__warn) from [<80024de8>] 
(warn_slowpath_fmt+0x40/0x50)
[    0.096194] [<80024de8>] (warn_slowpath_fmt) from [<80176118>] 
(sysfs_warn_dup+0x70/0x80)
[    0.096212] [<80176118>] (sysfs_warn_dup) from [<80176204>] 
(sysfs_create_dir_ns+0x8c/0x9c)
[    0.096231] [<80176204>] (sysfs_create_dir_ns) from [<802cafd0>] 
(kobject_add_internal+0xc0/0x360)
[    0.096249] [<802cafd0>] (kobject_add_internal) from [<802cb2b8>] 
(kobject_add+0x48/0x98)
[    0.096269] [<802cb2b8>] (kobject_add) from [<803b90e0>] 
(device_add+0xf0/0x5a0)
[    0.096295] [<803b90e0>] (device_add) from [<8051ae20>] 
(of_platform_device_create_pdata+0x8c/0xc4)
[    0.096316] [<8051ae20>] (of_platform_device_create_pdata) from 
[<8051af7c>] (of_platform_bus_create+0x110/0x2a0)
[    0.096333] [<8051af7c>] (of_platform_bus_create) from [<8051b29c>] 
(of_platform_populate+0x64/0xb4)
[    0.096358] [<8051b29c>] (of_platform_populate) from [<808f1a88>] 
(imx6q_init_machine+0x104/0x2b4)
[    0.096377] [<808f1a88>] (imx6q_init_machine) from [<808eba7c>] 
(customize_machine+0x24/0x44)
[    0.096395] [<808eba7c>] (customize_machine) from [<8000980c>] 
(do_one_initcall+0x4c/0x174)
[    0.096414] [<8000980c>] (do_one_initcall) from [<808ead60>] 
(kernel_init_freeable+0x158/0x1e8)
[    0.096435] [<808ead60>] (kernel_init_freeable) from [<8062b4b0>] 
(kernel_init+0x14/0x100)
[    0.096457] [<8062b4b0>] (kernel_init) from [<800104b8>] 
(ret_from_fork+0x14/0x3c)
[    0.096482] ---[ end trace 394e7b4d22c2be44 ]---
[    0.096491] ------------[ cut here ]------------
[    0.096507] WARNING: CPU: 0 PID: 1 at 
/home/ian/tx6/yoctomaster/build/tmp/work-shared/tx6u-vid/kernel-source/lib/kobject.c:240 
kobject_add_internal+0x2e0/0x360
[    0.096518] kobject_add_internal failed for 34.videomux with -EEXIST, 
don't try to register things with the same name in the same directory.
[    0.096525] Modules linked in:
[    0.096539] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W       
4.7.0-rc1-yocto-standard #1
[    0.096548] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[    0.096570] [<80018d8c>] (unwind_backtrace) from [<8001427c>] 
(show_stack+0x18/0x1c)
[    0.096585] [<8001427c>] (show_stack) from [<802c85fc>] 
(dump_stack+0x88/0x9c)
[    0.096601] [<802c85fc>] (dump_stack) from [<80024d90>] 
(__warn+0xf4/0x10c)
[    0.096616] [<80024d90>] (__warn) from [<80024de8>] 
(warn_slowpath_fmt+0x40/0x50)
[    0.096632] [<80024de8>] (warn_slowpath_fmt) from [<802cb1f0>] 
(kobject_add_internal+0x2e0/0x360)
[    0.096647] [<802cb1f0>] (kobject_add_internal) from [<802cb2b8>] 
(kobject_add+0x48/0x98)
[    0.096664] [<802cb2b8>] (kobject_add) from [<803b90e0>] 
(device_add+0xf0/0x5a0)
[    0.096681] [<803b90e0>] (device_add) from [<8051ae20>] 
(of_platform_device_create_pdata+0x8c/0xc4)
[    0.096700] [<8051ae20>] (of_platform_device_create_pdata) from 
[<8051af7c>] (of_platform_bus_create+0x110/0x2a0)
[    0.096716] [<8051af7c>] (of_platform_bus_create) from [<8051b29c>] 
(of_platform_populate+0x64/0xb4)
[    0.096735] [<8051b29c>] (of_platform_populate) from [<808f1a88>] 
(imx6q_init_machine+0x104/0x2b4)
[    0.096752] [<808f1a88>] (imx6q_init_machine) from [<808eba7c>] 
(customize_machine+0x24/0x44)
[    0.096767] [<808eba7c>] (customize_machine) from [<8000980c>] 
(do_one_initcall+0x4c/0x174)
[    0.096782] [<8000980c>] (do_one_initcall) from [<808ead60>] 
(kernel_init_freeable+0x158/0x1e8)
[    0.096798] [<808ead60>] (kernel_init_freeable) from [<8062b4b0>] 
(kernel_init+0x14/0x100)
[    0.096814] [<8062b4b0>] (kernel_init) from [<800104b8>] 
(ret_from_fork+0x14/0x3c)
[    0.096822] ---[ end trace 394e7b4d22c2be45 ]---


On 14/06/16 23:49, Steve Longerbeam wrote:
> From: Philipp Zabel <p.zabel@pengutronix.de>
>
> This driver can handle SoC internal and extern video bus multiplexers,
> controlled either by register bit fields or by GPIO.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>   drivers/staging/media/imx/capture/Kconfig          |   9 +
>   drivers/staging/media/imx/capture/Makefile         |   1 +
>   .../staging/media/imx/capture/imx-video-switch.c   | 348 +++++++++++++++++++++
>   3 files changed, 358 insertions(+)
>   create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c
>
> diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
> index ac6fce0..ecd09abe 100644
> --- a/drivers/staging/media/imx/capture/Kconfig
> +++ b/drivers/staging/media/imx/capture/Kconfig
> @@ -8,4 +8,13 @@ config IMX_MIPI_CSI2
>            MIPI CSI-2 Receiver driver support. This driver is required
>   	 for sensor drivers with a MIPI CSI2 interface.
>   
> +config IMX_VIDEO_SWITCH
> +	tristate "i.MX5/6 Video Bus Multiplexer"
> +	depends on VIDEO_IMX_CAMERA
> +	default y
> +	---help---
> +	  This driver provides support for the i.MX5/6 internal video bus
> +	  multiplexer controlled by register bitfields as well as
> +	  external multiplexers controller by a GPIO.
> +
>   endmenu
> diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
> index 8961a4f..f17b199 100644
> --- a/drivers/staging/media/imx/capture/Makefile
> +++ b/drivers/staging/media/imx/capture/Makefile
> @@ -4,3 +4,4 @@ imx-camera-objs := imx-camif.o imx-ic-prpenc.o imx-of.o \
>   obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
>   obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
>   obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
> +obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
> diff --git a/drivers/staging/media/imx/capture/imx-video-switch.c b/drivers/staging/media/imx/capture/imx-video-switch.c
> new file mode 100644
> index 0000000..0c86679
> --- /dev/null
> +++ b/drivers/staging/media/imx/capture/imx-video-switch.c
> @@ -0,0 +1,348 @@
> +/*
> + * devicetree probed mediacontrol video multiplexer.
> + *
> + * Copyright (C) 2013 Sascha Hauer, Pengutronix
> + * Copyright (c) 2014-2016 Mentor Graphics Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + * 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.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/gpio.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/of_graph.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-of.h>
> +
> +struct vidsw {
> +	struct device *dev;
> +	struct v4l2_subdev subdev;
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	struct media_pad *pads;
> +#endif
> +	struct v4l2_mbus_framefmt *format_mbus;
> +	struct v4l2_of_endpoint *endpoint;
> +	struct regmap_field *field;
> +	unsigned int gpio;
> +	int output_pad;
> +	int numpads;
> +	int active;
> +};
> +
> +#define to_vidsw(sd) container_of(sd, struct vidsw, subdev)
> +
> +static int vidsw_set_mux(struct vidsw *vidsw, int input_index)
> +{
> +	if (vidsw->active >= 0) {
> +		if (vidsw->active == input_index)
> +			return 0;
> +		else
> +			return -EBUSY;
> +	}
> +
> +	vidsw->active = input_index;
> +
> +	dev_dbg(vidsw->dev, "setting %d active\n", vidsw->active);
> +
> +	if (vidsw->field)
> +		regmap_field_write(vidsw->field, vidsw->active);
> +	else if (gpio_is_valid(vidsw->gpio))
> +		gpio_set_value(vidsw->gpio, vidsw->active);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +static int vidsw_link_setup(struct media_entity *entity,
> +		const struct media_pad *local,
> +		const struct media_pad *remote, u32 flags)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> +	struct vidsw *vidsw = to_vidsw(sd);
> +
> +	dev_dbg(vidsw->dev, "link setup %s -> %s", remote->entity->name,
> +		local->entity->name);
> +
> +	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
> +		if (local->index == vidsw->active) {
> +			dev_dbg(vidsw->dev, "going inactive\n");
> +			vidsw->active = -1;
> +		}
> +		return 0;
> +	}
> +
> +	return vidsw_set_mux(vidsw, local->index);
> +}
> +
> +static struct media_entity_operations vidsw_ops = {
> +	.link_setup = vidsw_link_setup,
> +};
> +#endif
> +
> +static int vidsw_s_routing(struct v4l2_subdev *sd, u32 input,
> +			   u32 output, u32 config)
> +{
> +	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
> +
> +	return vidsw_set_mux(vidsw, input);
> +}
> +
> +static int vidsw_async_init(struct vidsw *vidsw, struct device_node *node)
> +{
> +	struct v4l2_of_endpoint endpoint;
> +	struct device_node *epnode;
> +	int pad, numpads;
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	int ret;
> +#endif
> +
> +	numpads = of_get_child_count(node);
> +	if (numpads < 2) {
> +		dev_err(vidsw->dev, "Not enough ports %d\n", numpads);
> +		return -EINVAL;
> +	}
> +
> +	vidsw->numpads = numpads;
> +
> +	/*
> +	 * the last endpoint must define the mux output pad,
> +	 * the rest are the mux input pads.
> +	 */
> +	vidsw->output_pad = numpads - 1;
> +
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	vidsw->pads = devm_kzalloc(vidsw->dev,
> +				   numpads * sizeof(*vidsw->pads),
> +				   GFP_KERNEL);
> +	if (!vidsw->pads)
> +		return -ENOMEM;
> +#endif
> +
> +	vidsw->endpoint = devm_kzalloc(vidsw->dev,
> +				       numpads * sizeof(*vidsw->endpoint),
> +				       GFP_KERNEL);
> +	if (!vidsw->endpoint)
> +		return -ENOMEM;
> +
> +	vidsw->format_mbus = devm_kzalloc(vidsw->dev,
> +					  numpads * sizeof(*vidsw->format_mbus),
> +					  GFP_KERNEL);
> +	if (!vidsw->format_mbus)
> +		return -ENOMEM;
> +
> +#ifdef CONFIG_MEDIA_CONTROLLER
> +	vidsw->subdev.entity.ops = &vidsw_ops;
> +
> +	/* init the pad directions */
> +	for (pad = 0; pad < vidsw->output_pad; pad++)
> +		vidsw->pads[pad].flags = MEDIA_PAD_FL_SINK;
> +	vidsw->pads[vidsw->output_pad].flags = MEDIA_PAD_FL_SOURCE;
> +
> +	ret = media_entity_pads_init(&vidsw->subdev.entity,
> +				     vidsw->numpads, vidsw->pads);
> +	if (ret < 0)
> +		return ret;
> +#endif
> +
> +	epnode = NULL;
> +	for (pad = 0; pad < vidsw->numpads; pad++) {
> +		epnode = of_graph_get_next_endpoint(node, epnode);
> +		if (!epnode)
> +			return -EINVAL;
> +
> +		v4l2_of_parse_endpoint(epnode, &endpoint);
> +		vidsw->endpoint[pad] = endpoint;
> +		of_node_put(epnode);
> +	}
> +
> +	return 0;
> +}
> +
> +static int vidsw_registered(struct v4l2_subdev *sd)
> +{
> +	return 0;
> +}
> +
> +int vidsw_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg)
> +{
> +	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
> +
> +	dev_dbg(vidsw->dev, "reporting configration %d\n", vidsw->active);
> +
> +	/* Mirror the input side on the output side */
> +	cfg->type = vidsw->endpoint[vidsw->active].bus_type;
> +	if (cfg->type == V4L2_MBUS_PARALLEL || cfg->type == V4L2_MBUS_BT656)
> +		cfg->flags = vidsw->endpoint[vidsw->active].bus.parallel.flags;
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops vidsw_subdev_video_ops = {
> +	.g_mbus_config = vidsw_g_mbus_config,
> +	.s_routing = vidsw_s_routing,
> +};
> +
> +static int vidsw_get_format(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_subdev_format *sdformat)
> +{
> +	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
> +
> +	sdformat->format = vidsw->format_mbus[sdformat->pad];
> +
> +	return 0;
> +}
> +
> +static int vidsw_set_format(struct v4l2_subdev *sd,
> +			    struct v4l2_subdev_pad_config *cfg,
> +			    struct v4l2_subdev_format *sdformat)
> +{
> +	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
> +
> +	if (sdformat->pad >= vidsw->numpads)
> +		return -EINVAL;
> +
> +	/* Output pad mirrors active input pad, no limitations on input pads */
> +	if (sdformat->pad == vidsw->output_pad && vidsw->active >= 0)
> +		sdformat->format = vidsw->format_mbus[vidsw->active];
> +
> +	if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
> +		cfg->try_fmt = sdformat->format;
> +	else
> +		vidsw->format_mbus[sdformat->pad] = sdformat->format;
> +
> +	return 0;
> +}
> +
> +static struct v4l2_subdev_pad_ops vidsw_pad_ops = {
> +	.get_fmt = vidsw_get_format,
> +	.set_fmt = vidsw_set_format,
> +};
> +
> +static struct v4l2_subdev_ops vidsw_subdev_ops = {
> +	.pad = &vidsw_pad_ops,
> +	.video = &vidsw_subdev_video_ops,
> +};
> +
> +static struct v4l2_subdev_internal_ops vidsw_internal_ops = {
> +	.registered = vidsw_registered,
> +};
> +
> +static int of_get_reg_field(struct device_node *node, struct reg_field *field)
> +{
> +	u32 reg_bit_mask[2];
> +	int ret;
> +
> +	ret = of_property_read_u32_array(node, "reg", reg_bit_mask, 2);
> +	if (ret < 0)
> +		return ret;
> +
> +	field->reg = reg_bit_mask[0];
> +	field->lsb = __ffs(reg_bit_mask[1]);
> +	field->msb = __fls(reg_bit_mask[1]);
> +
> +	return 0;
> +}
> +
> +static int vidsw_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct reg_field field;
> +	struct vidsw *vidsw;
> +	struct regmap *map;
> +	int ret;
> +
> +	vidsw = devm_kzalloc(&pdev->dev, sizeof(*vidsw), GFP_KERNEL);
> +	if (!vidsw)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, vidsw);
> +
> +	v4l2_subdev_init(&vidsw->subdev, &vidsw_subdev_ops);
> +	v4l2_set_subdevdata(&vidsw->subdev, &pdev->dev);
> +	vidsw->subdev.internal_ops = &vidsw_internal_ops;
> +	snprintf(vidsw->subdev.name, sizeof(vidsw->subdev.name), "%s",
> +			np->name);
> +	vidsw->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	vidsw->subdev.dev = &pdev->dev;
> +	vidsw->dev = &pdev->dev;
> +	vidsw->active = -1;
> +
> +	ret = of_get_reg_field(np, &field);
> +	if (ret == 0) {
> +		map = syscon_regmap_lookup_by_phandle(np, "gpr");
> +		if (!map) {
> +			dev_err(&pdev->dev,
> +				"Failed to get syscon register map\n");
> +			return PTR_ERR(map);
> +		}
> +
> +		vidsw->field = devm_regmap_field_alloc(&pdev->dev, map, field);
> +		if (IS_ERR(vidsw->field)) {
> +			dev_err(&pdev->dev,
> +				"Failed to allocate regmap field\n");
> +			return PTR_ERR(vidsw->field);
> +		}
> +	} else {
> +		vidsw->gpio = of_get_named_gpio_flags(np, "gpios", 0, NULL);
> +		ret = gpio_request_one(vidsw->gpio,
> +				       GPIOF_OUT_INIT_LOW, np->name);
> +		if (ret < 0) {
> +			dev_warn(&pdev->dev,
> +				 "could not request control gpio %d: %d\n",
> +				 vidsw->gpio, ret);
> +			vidsw->gpio = -1;
> +		}
> +	}
> +
> +	ret = vidsw_async_init(vidsw, np);
> +	if (ret)
> +		return ret;
> +
> +	ret = v4l2_async_register_subdev(&vidsw->subdev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int vidsw_remove(struct platform_device *pdev)
> +{
> +	/* FIXME */
> +
> +	return -EBUSY;
> +}
> +
> +static const struct of_device_id vidsw_dt_ids[] = {
> +	{ .compatible = "imx-video-mux", },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, vidsw_dt_ids);
> +
> +static struct platform_driver vidsw_driver = {
> +	.probe		= vidsw_probe,
> +	.remove		= vidsw_remove,
> +	.driver		= {
> +		.of_match_table = vidsw_dt_ids,
> +		.name	= "imx-video-mux",
> +		.owner	= THIS_MODULE,
> +	},
> +};
> +
> +module_platform_driver(vidsw_driver);
> +
> +MODULE_DESCRIPTION("i.MX video stream multiplexer");
> +MODULE_AUTHOR("Sascha Hauer, Pengutronix");
> +MODULE_LICENSE("GPL");


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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-16  9:49     ` Jack Mitchell
@ 2016-06-16 17:02       ` Steve Longerbeam
  2016-06-16 17:43         ` Nicolas Dufresne
                           ` (2 more replies)
  0 siblings, 3 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-16 17:02 UTC (permalink / raw)
  To: Jack Mitchell, Steve Longerbeam, linux-media

On 06/16/2016 02:49 AM, Jack Mitchell wrote:
>
> On 16/06/16 02:37, Steve Longerbeam wrote:
>> Hi Jack,
>>
>> On 06/15/2016 03:43 AM, Jack Mitchell wrote:
>>> <snip>
>>> Trying to use a user pointer rather than mmap also fails and causes a kernel splat.
>>>
>>
>> Hmm, I've tested userptr with the mem2mem driver, but maybe never
>> with video capture. I tried "v4l2-ctl -d/dev/video0 --stream-user=8" but
>> that returns "VIDIOC_QBUF: failed: Invalid argument", haven't tracked
>> down why (could be a bug in v4l2-ctl). Can you share the splat?
>>
>
> On re-checking the splat was the same v4l_cropcap that was mentioned before so I don't think it's related. The error I get back is:
>
> VIDIOC_QBUF error 22, Invalid argument
>
> I'm using the example program the the v4l2 docs [1].

I found the cause at least in my case. After enabling dynamic debug in
videobuf2-dma-contig.c, "v4l2-ctl -d/dev/video0 --stream-user=8" gives
me

[  468.826046] user data must be aligned to 64 bytes



But even getting past that alignment issue, I've only tested userptr (in mem2mem
driver) by giving the driver a user address of a mmap'ed kernel contiguous
buffer. A true discontiguous user buffer may not work, the IPU DMA does not
support scatter-gather.

Steve


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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-16 17:02       ` Steve Longerbeam
@ 2016-06-16 17:43         ` Nicolas Dufresne
  2016-06-16 17:43         ` Nicolas Dufresne
  2016-06-17  7:10         ` Hans Verkuil
  2 siblings, 0 replies; 105+ messages in thread
From: Nicolas Dufresne @ 2016-06-16 17:43 UTC (permalink / raw)
  To: Steve Longerbeam, Jack Mitchell, Steve Longerbeam, linux-media

Le jeudi 16 juin 2016 à 10:02 -0700, Steve Longerbeam a écrit :
> I found the cause at least in my case. After enabling dynamic debug in
> videobuf2-dma-contig.c, "v4l2-ctl -d/dev/video0 --stream-user=8" gives
> me
> 
> [  468.826046] user data must be aligned to 64 bytes
> 
> 
> 
> But even getting past that alignment issue, I've only tested userptr (in mem2mem
> driver) by giving the driver a user address of a mmap'ed kernel contiguous
> buffer. A true discontiguous user buffer may not work, the IPU DMA does not
> support scatter-gather.

If it's dma-contig, you'll need page aligned and contiguous memory.
What some test application do when testing their driver with that, is
to allocate memory using another device, or m2m device, that uses the
same allocator.

regards,
Nicolas

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-16 17:02       ` Steve Longerbeam
  2016-06-16 17:43         ` Nicolas Dufresne
@ 2016-06-16 17:43         ` Nicolas Dufresne
  2016-06-17  7:10         ` Hans Verkuil
  2 siblings, 0 replies; 105+ messages in thread
From: Nicolas Dufresne @ 2016-06-16 17:43 UTC (permalink / raw)
  To: Steve Longerbeam, Jack Mitchell, Steve Longerbeam, linux-media

[-- Attachment #1: Type: text/plain, Size: 812 bytes --]

Le jeudi 16 juin 2016 à 10:02 -0700, Steve Longerbeam a écrit :
> I found the cause at least in my case. After enabling dynamic debug in
> videobuf2-dma-contig.c, "v4l2-ctl -d/dev/video0 --stream-user=8" gives
> me
> 
> [  468.826046] user data must be aligned to 64 bytes
> 
> 
> 
> But even getting past that alignment issue, I've only tested userptr (in mem2mem
> driver) by giving the driver a user address of a mmap'ed kernel contiguous
> buffer. A true discontiguous user buffer may not work, the IPU DMA does not
> support scatter-gather.

If it's dma-contig, you'll need page aligned and contiguous memory.
What some test application do when testing their driver with that, is
to allocate memory using another device, or m2m device, that uses the
same allocator.

regards,
Nicolas

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-16 17:02       ` Steve Longerbeam
  2016-06-16 17:43         ` Nicolas Dufresne
  2016-06-16 17:43         ` Nicolas Dufresne
@ 2016-06-17  7:10         ` Hans Verkuil
  2016-06-17 17:35           ` Steve Longerbeam
  2 siblings, 1 reply; 105+ messages in thread
From: Hans Verkuil @ 2016-06-17  7:10 UTC (permalink / raw)
  To: Steve Longerbeam, Jack Mitchell, Steve Longerbeam, linux-media

On 06/16/2016 07:02 PM, Steve Longerbeam wrote:
> On 06/16/2016 02:49 AM, Jack Mitchell wrote:
>>
>> On 16/06/16 02:37, Steve Longerbeam wrote:
>>> Hi Jack,
>>>
>>> On 06/15/2016 03:43 AM, Jack Mitchell wrote:
>>>> <snip>
>>>> Trying to use a user pointer rather than mmap also fails and causes a kernel splat.
>>>>
>>>
>>> Hmm, I've tested userptr with the mem2mem driver, but maybe never
>>> with video capture. I tried "v4l2-ctl -d/dev/video0 --stream-user=8" but
>>> that returns "VIDIOC_QBUF: failed: Invalid argument", haven't tracked
>>> down why (could be a bug in v4l2-ctl). Can you share the splat?
>>>
>>
>> On re-checking the splat was the same v4l_cropcap that was mentioned before so I don't think it's related. The error I get back is:
>>
>> VIDIOC_QBUF error 22, Invalid argument
>>
>> I'm using the example program the the v4l2 docs [1].
> 
> I found the cause at least in my case. After enabling dynamic debug in
> videobuf2-dma-contig.c, "v4l2-ctl -d/dev/video0 --stream-user=8" gives
> me
> 
> [  468.826046] user data must be aligned to 64 bytes
> 
> 
> 
> But even getting past that alignment issue, I've only tested userptr (in mem2mem
> driver) by giving the driver a user address of a mmap'ed kernel contiguous
> buffer. A true discontiguous user buffer may not work, the IPU DMA does not
> support scatter-gather.

I don't think VB2_USERPTR should be enabled in this case due to the DMA limitations.
It won't allow you to use malloc()ed memory and the hack that allows you to pass
contiguous memory is superseded by the DMABUF mode.

Regards,

	Hans

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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-16  8:32   ` [19/38] " Gary Bisson
@ 2016-06-17 15:18     ` Gary Bisson
  2016-06-17 16:09       ` Jack Mitchell
                         ` (2 more replies)
  0 siblings, 3 replies; 105+ messages in thread
From: Gary Bisson @ 2016-06-17 15:18 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-media, Steve Longerbeam

Steve, All,

On Thu, Jun 16, 2016 at 10:32:31AM +0200, Gary Bisson wrote:
> Steve, All,
> 
> On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
> > Defines the host video capture device node and an OV5642 camera sensor
> > node on i2c2. The host capture device connects to the OV5642 via the
> > parallel-bus mux input on the ipu1_csi0_mux.
> > 
> > Note there is a pin conflict with GPIO6. This pin functions as a power
> > input pin to the OV5642, but ENET requires it to wake-up the ARM cores
> > on normal RX and TX packet done events (see 6261c4c8). So by default,
> > capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
> > Ethernet will still work just not quite as well.
> 
> Actually the following patch fixes this issue and has already been
> applied on Shawn's tree:
> https://patchwork.kernel.org/patch/9153523/
> 
> Also, this follow-up patch declared the HW workaround for SabreLite:
> https://patchwork.kernel.org/patch/9153525/
> 
> So ideally, once those two patches land on your base tree, you could get
> rid of the #define and remove the HW workaround declaration.
> 
> Finally, I'll test the series on Sabre-Lite this week.

I've applied this series on top of Shawn tree (for-next branch) in order
not to worry about the GPIO6 workaround.

Although the camera seems to get enumerated properly, I can't seem to
get anything from it. See log:
http://pastebin.com/xnw1ujUq

In your cover letter, you said that you have not run through
v4l2-compliance. How have you tested the capture?

Also, why isn't the OV5640 MIPI camera declared on the SabreLite device
tree?

Let me know if I can help testing/updating things on the SabreLite.

Regards,
Gary

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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-17 15:18     ` Gary Bisson
@ 2016-06-17 16:09       ` Jack Mitchell
  2016-06-17 19:00       ` tchellRe: " Steve Longerbeam
  2016-06-17 19:01       ` Steve Longerbeam
  2 siblings, 0 replies; 105+ messages in thread
From: Jack Mitchell @ 2016-06-17 16:09 UTC (permalink / raw)
  To: Gary Bisson, Steve Longerbeam; +Cc: linux-media, Steve Longerbeam



On 17/06/16 16:18, Gary Bisson wrote:
> Steve, All,
>
> On Thu, Jun 16, 2016 at 10:32:31AM +0200, Gary Bisson wrote:
>> Steve, All,
>>
>> On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
>>> Defines the host video capture device node and an OV5642 camera sensor
>>> node on i2c2. The host capture device connects to the OV5642 via the
>>> parallel-bus mux input on the ipu1_csi0_mux.
>>>
>>> Note there is a pin conflict with GPIO6. This pin functions as a power
>>> input pin to the OV5642, but ENET requires it to wake-up the ARM cores
>>> on normal RX and TX packet done events (see 6261c4c8). So by default,
>>> capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
>>> Ethernet will still work just not quite as well.
>>
>> Actually the following patch fixes this issue and has already been
>> applied on Shawn's tree:
>> https://patchwork.kernel.org/patch/9153523/
>>
>> Also, this follow-up patch declared the HW workaround for SabreLite:
>> https://patchwork.kernel.org/patch/9153525/
>>
>> So ideally, once those two patches land on your base tree, you could get
>> rid of the #define and remove the HW workaround declaration.
>>
>> Finally, I'll test the series on Sabre-Lite this week.
>
> I've applied this series on top of Shawn tree (for-next branch) in order
> not to worry about the GPIO6 workaround.
>
> Although the camera seems to get enumerated properly, I can't seem to
> get anything from it. See log:
> http://pastebin.com/xnw1ujUq
>
> In your cover letter, you said that you have not run through
> v4l2-compliance. How have you tested the capture?
>
> Also, why isn't the OV5640 MIPI camera declared on the SabreLite device
> tree?
>
> Let me know if I can help testing/updating things on the SabreLite.

Hi Gary,

I have been testing the ov5640 MIPI on the Sabrelite. Patch is at [1]. 
Careful of the GPIO numbers as I am using an eCon sensor which has 
slightly different pin routing to the boundarydevices sensor. Let me 
know how you get on, I've managed to get images out of the device so 
it's working to a degree.

Cheers,
Jack.

Tuxable Ltd,
London, UK

[1] http://ix.io/TTg

>
> Regards,
> Gary
> --

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-17  7:10         ` Hans Verkuil
@ 2016-06-17 17:35           ` Steve Longerbeam
  2016-06-18  9:01             ` Hans Verkuil
  0 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-17 17:35 UTC (permalink / raw)
  To: Hans Verkuil, Steve Longerbeam, Jack Mitchell, linux-media



On 06/17/2016 12:10 AM, Hans Verkuil wrote:
> On 06/16/2016 07:02 PM, Steve Longerbeam wrote:
>> On 06/16/2016 02:49 AM, Jack Mitchell wrote:
>>> On 16/06/16 02:37, Steve Longerbeam wrote:
>>>> Hi Jack,
>>>>
>>>> On 06/15/2016 03:43 AM, Jack Mitchell wrote:
>>>>> <snip>
>>>>> Trying to use a user pointer rather than mmap also fails and causes a kernel splat.
>>>>>
>>>> Hmm, I've tested userptr with the mem2mem driver, but maybe never
>>>> with video capture. I tried "v4l2-ctl -d/dev/video0 --stream-user=8" but
>>>> that returns "VIDIOC_QBUF: failed: Invalid argument", haven't tracked
>>>> down why (could be a bug in v4l2-ctl). Can you share the splat?
>>>>
>>> On re-checking the splat was the same v4l_cropcap that was mentioned before so I don't think it's related. The error I get back is:
>>>
>>> VIDIOC_QBUF error 22, Invalid argument
>>>
>>> I'm using the example program the the v4l2 docs [1].
>> I found the cause at least in my case. After enabling dynamic debug in
>> videobuf2-dma-contig.c, "v4l2-ctl -d/dev/video0 --stream-user=8" gives
>> me
>>
>> [  468.826046] user data must be aligned to 64 bytes
>>
>>
>>
>> But even getting past that alignment issue, I've only tested userptr (in mem2mem
>> driver) by giving the driver a user address of a mmap'ed kernel contiguous
>> buffer. A true discontiguous user buffer may not work, the IPU DMA does not
>> support scatter-gather.
> I don't think VB2_USERPTR should be enabled in this case due to the DMA limitations.
> It won't allow you to use malloc()ed memory and the hack that allows you to pass
> contiguous memory is superseded by the DMABUF mode.

Hi Hans, yes, I was going to suggest that. I will remove USERPTR
from both capture and mem2mem io_modes flags. Although I can
see where userptr support is still useful, that is for legacy middleware
that are still using mmap'ed userptrs and have not yet converted to
passing around dmabuf fd's.

But I've been perplexed for while on this, why vb2_dc_get_userptr() 
resorts to
scatter-gather (when the given user buffer is found not to have valid 
pfn's).
Shouldn't it be assumed that driver users of the vb2 dma-contig allocator
only support contiguous dma as the name implies? Maybe the issue is that
users that set VB2_USERPTR are implying they support scatter/gather, but
users of vb2-dma-contig could also be implying they do not, and would be
using vb2-dma-sg if that were the case.

Steve


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

* tchellRe: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-17 15:18     ` Gary Bisson
  2016-06-17 16:09       ` Jack Mitchell
@ 2016-06-17 19:00       ` Steve Longerbeam
  2016-06-18  9:05         ` Hans Verkuil
  2016-06-17 19:01       ` Steve Longerbeam
  2 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-17 19:00 UTC (permalink / raw)
  To: Gary Bisson, Steve Longerbeam; +Cc: linux-media, Jack Mitchell

[-- Attachment #1: Type: text/plain, Size: 2315 bytes --]



On 06/17/2016 08:18 AM, Gary Bisson wrote:
> Steve, All,
>
> On Thu, Jun 16, 2016 at 10:32:31AM +0200, Gary Bisson wrote:
>> Steve, All,
>>
>> On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
>>> Defines the host video capture device node and an OV5642 camera sensor
>>> node on i2c2. The host capture device connects to the OV5642 via the
>>> parallel-bus mux input on the ipu1_csi0_mux.
>>>
>>> Note there is a pin conflict with GPIO6. This pin functions as a power
>>> input pin to the OV5642, but ENET requires it to wake-up the ARM cores
>>> on normal RX and TX packet done events (see 6261c4c8). So by default,
>>> capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
>>> Ethernet will still work just not quite as well.
>> Actually the following patch fixes this issue and has already been
>> applied on Shawn's tree:
>> https://patchwork.kernel.org/patch/9153523/
>>
>> Also, this follow-up patch declared the HW workaround for SabreLite:
>> https://patchwork.kernel.org/patch/9153525/
>>
>> So ideally, once those two patches land on your base tree, you could get
>> rid of the #define and remove the HW workaround declaration.
>>
>> Finally, I'll test the series on Sabre-Lite this week.
> I've applied this series on top of Shawn tree (for-next branch) in order
> not to worry about the GPIO6 workaround.
>
> Although the camera seems to get enumerated properly, I can't seem to
> get anything from it. See log:
> http://pastebin.com/xnw1ujUq

Hi Gary, the driver does not implement vidioc_cropcap, it has
switched to the new selection APIs and v4l2src should be using
vidioc_g_selection instead of vidioc_cropcap.

>
> In your cover letter, you said that you have not run through
> v4l2-compliance. How have you tested the capture?

I use v4l2-ctl, and have used v4l2src in the past, but that was before
switching to the selection APIs. Try the attached hack that adds
vidioc_cropcap back in, and see how far you get on SabreLite with
v4l2src. I tried  the following on SabreAuto:

gst-launch-1.0 v4l2src io_mode=4 ! 
"video/x-raw,format=RGB16,width=640,height=480" ! fbdevsink

>
> Also, why isn't the OV5640 MIPI camera declared on the SabreLite device
> tree?

See Jack Mitchell's patch at http://ix.io/TTg. Thanks Jack! I will work on
incorporating it.


Steve



[-- Attachment #2: vidioc_cropcap.diff --]
[-- Type: text/x-patch, Size: 1268 bytes --]

diff --git a/drivers/staging/media/imx/capture/imx-camif.c b/drivers/staging/media/imx/capture/imx-camif.c
index 9c247e0..2c51bc7 100644
--- a/drivers/staging/media/imx/capture/imx-camif.c
+++ b/drivers/staging/media/imx/capture/imx-camif.c
@@ -1561,6 +1561,23 @@ static int vidioc_s_parm(struct file *file, void *fh,
 	return v4l2_subdev_call(dev->sensor->sd, video, s_parm, a);
 }
 
+static int vidioc_cropcap(struct file *file, void *priv,
+			  struct v4l2_cropcap *cropcap)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	    cropcap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+		return -EINVAL;
+
+	cropcap->bounds = dev->crop_bounds;
+	cropcap->defrect = dev->crop_defrect;
+	cropcap->pixelaspect.numerator = 1;
+	cropcap->pixelaspect.denominator = 1;
+	return 0;
+}
+
 static int vidioc_g_selection(struct file *file, void *priv,
 			      struct v4l2_selection *sel)
 {
@@ -1794,6 +1811,7 @@ static const struct v4l2_ioctl_ops imxcam_ioctl_ops = {
 	.vidioc_g_parm          = vidioc_g_parm,
 	.vidioc_s_parm          = vidioc_s_parm,
 
+	.vidioc_cropcap		= vidioc_cropcap,
 	.vidioc_g_selection     = vidioc_g_selection,
 	.vidioc_s_selection     = vidioc_s_selection,
 

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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-17 15:18     ` Gary Bisson
  2016-06-17 16:09       ` Jack Mitchell
  2016-06-17 19:00       ` tchellRe: " Steve Longerbeam
@ 2016-06-17 19:01       ` Steve Longerbeam
  2016-06-20  9:33         ` Gary Bisson
  2 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-17 19:01 UTC (permalink / raw)
  To: Gary Bisson, Steve Longerbeam; +Cc: linux-media, Jack Mitchell

[-- Attachment #1: Type: text/plain, Size: 2316 bytes --]



On 06/17/2016 08:18 AM, Gary Bisson wrote:
> Steve, All,
>
> On Thu, Jun 16, 2016 at 10:32:31AM +0200, Gary Bisson wrote:
>> Steve, All,
>>
>> On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
>>> Defines the host video capture device node and an OV5642 camera sensor
>>> node on i2c2. The host capture device connects to the OV5642 via the
>>> parallel-bus mux input on the ipu1_csi0_mux.
>>>
>>> Note there is a pin conflict with GPIO6. This pin functions as a power
>>> input pin to the OV5642, but ENET requires it to wake-up the ARM cores
>>> on normal RX and TX packet done events (see 6261c4c8). So by default,
>>> capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
>>> Ethernet will still work just not quite as well.
>> Actually the following patch fixes this issue and has already been
>> applied on Shawn's tree:
>> https://patchwork.kernel.org/patch/9153523/
>>
>> Also, this follow-up patch declared the HW workaround for SabreLite:
>> https://patchwork.kernel.org/patch/9153525/
>>
>> So ideally, once those two patches land on your base tree, you could get
>> rid of the #define and remove the HW workaround declaration.
>>
>> Finally, I'll test the series on Sabre-Lite this week.
> I've applied this series on top of Shawn tree (for-next branch) in order
> not to worry about the GPIO6 workaround.
>
> Although the camera seems to get enumerated properly, I can't seem to
> get anything from it. See log:
> http://pastebin.com/xnw1ujUq

Hi Gary, the driver does not implement vidioc_cropcap, it has
switched to the new selection APIs and v4l2src should be using
vidioc_g_selection instead of vidioc_cropcap.

>
> In your cover letter, you said that you have not run through
> v4l2-compliance. How have you tested the capture?

I use v4l2-ctl, and have used v4l2src in the past, but that was before
switching to the selection APIs. Try the attached hack that adds
vidioc_cropcap back in, and see how far you get on SabreLite with
v4l2src. I tried  the following on SabreAuto:

gst-launch-1.0 v4l2src io_mode=4 ! 
"video/x-raw,format=RGB16,width=640,height=480" ! fbdevsink

>
> Also, why isn't the OV5640 MIPI camera declared on the SabreLite device
> tree?

See Jack Mitchell's patch at http://ix.io/TTg. Thanks Jack! I will work on
incorporating it.


Steve




[-- Attachment #2: vidioc_cropcap.diff --]
[-- Type: text/x-patch, Size: 1269 bytes --]

diff --git a/drivers/staging/media/imx/capture/imx-camif.c b/drivers/staging/media/imx/capture/imx-camif.c
index 9c247e0..2c51bc7 100644
--- a/drivers/staging/media/imx/capture/imx-camif.c
+++ b/drivers/staging/media/imx/capture/imx-camif.c
@@ -1561,6 +1561,23 @@ static int vidioc_s_parm(struct file *file, void *fh,
 	return v4l2_subdev_call(dev->sensor->sd, video, s_parm, a);
 }
 
+static int vidioc_cropcap(struct file *file, void *priv,
+			  struct v4l2_cropcap *cropcap)
+{
+	struct imxcam_ctx *ctx = file2ctx(file);
+	struct imxcam_dev *dev = ctx->dev;
+
+	if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+	    cropcap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+		return -EINVAL;
+
+	cropcap->bounds = dev->crop_bounds;
+	cropcap->defrect = dev->crop_defrect;
+	cropcap->pixelaspect.numerator = 1;
+	cropcap->pixelaspect.denominator = 1;
+	return 0;
+}
+
 static int vidioc_g_selection(struct file *file, void *priv,
 			      struct v4l2_selection *sel)
 {
@@ -1794,6 +1811,7 @@ static const struct v4l2_ioctl_ops imxcam_ioctl_ops = {
 	.vidioc_g_parm          = vidioc_g_parm,
 	.vidioc_s_parm          = vidioc_s_parm,
 
+	.vidioc_cropcap		= vidioc_cropcap,
 	.vidioc_g_selection     = vidioc_g_selection,
 	.vidioc_s_selection     = vidioc_s_selection,
 


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

* Re: [PATCH 31/38] media: imx: Add video switch
  2016-06-16 16:13   ` Ian Arkver
@ 2016-06-17 20:14     ` Steve Longerbeam
  0 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-17 20:14 UTC (permalink / raw)
  To: Ian Arkver, Steve Longerbeam, linux-media; +Cc: Philipp Zabel, Sascha Hauer

On 06/16/2016 09:13 AM, Ian Arkver wrote:
> For me this fails when I try to enable both video muxes (mx6dl, though 
> mx6q should be the same).
>
> I get a sysfs duplicate name failure for 34.videomux. I realise 
> passing the GPR13 register offset and a bitfield mask as a tuple in 
> the reg value of the of_node is handy, but how should we account for 
> multiple devices with the same name and address?
>
> A quick and dirty hack would be to have of_get_reg_field do something 
> like
>
>     field->reg = reg_bit_mask[0] & 0xff;
>
> and then use values in the DT that differ in the bits masked off, but 
> there must be a nicer way.
>
> Trace below, fyi. This is from the v2 patches posted here, not your 
> v2.1 tree.
>

Hi Ian,

Thanks for catching this. I found a simple and clean solution to this
problem: just rename the video-mux node from "videomux" to
"ipu[12]_csi[01]_mux". It makes the subdev names more descriptive
anyway. I've applied that change to my mx6-media-staging-v2.1
branch.

Steve


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

* Re: [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder
  2016-06-16 11:33   ` Lars-Peter Clausen
@ 2016-06-17 20:33     ` Steve Longerbeam
  0 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-17 20:33 UTC (permalink / raw)
  To: Lars-Peter Clausen, Steve Longerbeam, linux-media



On 06/16/2016 04:33 AM, Lars-Peter Clausen wrote:
> On 06/15/2016 12:49 AM, Steve Longerbeam wrote:
>> This driver is based on adv7180.c from Freescale imx_3.10.17_1.0.0_beta
>> branch, modified heavily for code cleanup and converted from int-device
>> to subdev.
> We already have a driver for the adv7180 upstream, also using the subdev
> API. Is there anything that can be done with this new driver that can't be
> done with the other one. And if it is are there any blockers that would
> prevent us from adding the missing features to the upstream adv7180?
>
> I know that the driver in the Freescale tree used to have bits that made it
> specially tailored to the iMX6. But these bits seem to be mostly gone in
> this version of the driver.
>
> I'm slightly concerned about the conflicting nature of these drivers. Both
> attach to the same device ID/DT compatible string and register slightly
> different userspace ABIs. I'd like to avoid ending up in a situation where
> we have dependencies on both ABIs and can no longer converge.

Hi Lars,

Yes, it's been my plan all along to bring the upstream adv7180 subdev
up to speed, and then remove the one included in this patch set.
The issues I see so far with the upstream driver:

- It is not auto-detecting changes to the analog input signal, such as
   loss/regain of signal lock and video standard changes. This probably
   is because interrupts are not working. I haven't debugged that further.

- The media bus format code is not right, it should be UYVY, not YUYV. But
   I'm reluctant to make that mbus code change because there are other
   users of this driver and that will likely corrupt images on the other
   targets. But I believe UYVY is the correct order according to bt.656
   standard, someone correct me if I am wrong.

- The field type should be either V4L2_FIELD_SEQ_BT or V4L2_FIELD_SEQ_TB,
   since the adv7180 transmits one complete field followed by the other.

There could be other issues. There are also the ov564x subdevs that 
really need
to be cleaned-up and moved to drivers/media/i2c (they are also based on 
old FSL
intdev drivers but with more stuff in there that probably doesn't make 
sense, such
as those really big register tables that are likely filled with reset 
default values and
were generated by doing an i2c dump at reset).

This work will take some time, so the question is, should we delay that 
work to
after an initial merge.

Steve


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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-17 17:35           ` Steve Longerbeam
@ 2016-06-18  9:01             ` Hans Verkuil
  0 siblings, 0 replies; 105+ messages in thread
From: Hans Verkuil @ 2016-06-18  9:01 UTC (permalink / raw)
  To: Steve Longerbeam, Steve Longerbeam, Jack Mitchell, linux-media

On 06/17/2016 07:35 PM, Steve Longerbeam wrote:
> 
> 
> On 06/17/2016 12:10 AM, Hans Verkuil wrote:
>> On 06/16/2016 07:02 PM, Steve Longerbeam wrote:
>>> On 06/16/2016 02:49 AM, Jack Mitchell wrote:
>>>> On 16/06/16 02:37, Steve Longerbeam wrote:
>>>>> Hi Jack,
>>>>>
>>>>> On 06/15/2016 03:43 AM, Jack Mitchell wrote:
>>>>>> <snip>
>>>>>> Trying to use a user pointer rather than mmap also fails and causes a kernel splat.
>>>>>>
>>>>> Hmm, I've tested userptr with the mem2mem driver, but maybe never
>>>>> with video capture. I tried "v4l2-ctl -d/dev/video0 --stream-user=8" but
>>>>> that returns "VIDIOC_QBUF: failed: Invalid argument", haven't tracked
>>>>> down why (could be a bug in v4l2-ctl). Can you share the splat?
>>>>>
>>>> On re-checking the splat was the same v4l_cropcap that was mentioned before so I don't think it's related. The error I get back is:
>>>>
>>>> VIDIOC_QBUF error 22, Invalid argument
>>>>
>>>> I'm using the example program the the v4l2 docs [1].
>>> I found the cause at least in my case. After enabling dynamic debug in
>>> videobuf2-dma-contig.c, "v4l2-ctl -d/dev/video0 --stream-user=8" gives
>>> me
>>>
>>> [  468.826046] user data must be aligned to 64 bytes
>>>
>>>
>>>
>>> But even getting past that alignment issue, I've only tested userptr (in mem2mem
>>> driver) by giving the driver a user address of a mmap'ed kernel contiguous
>>> buffer. A true discontiguous user buffer may not work, the IPU DMA does not
>>> support scatter-gather.
>> I don't think VB2_USERPTR should be enabled in this case due to the DMA limitations.
>> It won't allow you to use malloc()ed memory and the hack that allows you to pass
>> contiguous memory is superseded by the DMABUF mode.
> 
> Hi Hans, yes, I was going to suggest that. I will remove USERPTR
> from both capture and mem2mem io_modes flags. Although I can
> see where userptr support is still useful, that is for legacy middleware
> that are still using mmap'ed userptrs and have not yet converted to
> passing around dmabuf fd's.
> 
> But I've been perplexed for while on this, why vb2_dc_get_userptr() 
> resorts to
> scatter-gather (when the given user buffer is found not to have valid 
> pfn's).
> Shouldn't it be assumed that driver users of the vb2 dma-contig allocator
> only support contiguous dma as the name implies? Maybe the issue is that
> users that set VB2_USERPTR are implying they support scatter/gather, but
> users of vb2-dma-contig could also be implying they do not, and would be
> using vb2-dma-sg if that were the case.

At the end of the vb2_dc_get_userptr() function it checks if the result
is physically contiguous (the call to vb2_dc_get_contiguous_size). If not,
then it returns an error.

I hate it that dma_contig accepts user pointers at all, it should never
have been implemented like that. There is no way for applications to know
that even though the driver support USERPTR, the buffer still has to be
phys. contig. :-(

Regards,

	Hans

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

* Re: tchellRe: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-17 19:00       ` tchellRe: " Steve Longerbeam
@ 2016-06-18  9:05         ` Hans Verkuil
  0 siblings, 0 replies; 105+ messages in thread
From: Hans Verkuil @ 2016-06-18  9:05 UTC (permalink / raw)
  To: Steve Longerbeam, Gary Bisson, Steve Longerbeam
  Cc: linux-media, Jack Mitchell

On 06/17/2016 09:00 PM, Steve Longerbeam wrote:
> 
> 
> On 06/17/2016 08:18 AM, Gary Bisson wrote:
>> Steve, All,
>>
>> On Thu, Jun 16, 2016 at 10:32:31AM +0200, Gary Bisson wrote:
>>> Steve, All,
>>>
>>> On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
>>>> Defines the host video capture device node and an OV5642 camera sensor
>>>> node on i2c2. The host capture device connects to the OV5642 via the
>>>> parallel-bus mux input on the ipu1_csi0_mux.
>>>>
>>>> Note there is a pin conflict with GPIO6. This pin functions as a power
>>>> input pin to the OV5642, but ENET requires it to wake-up the ARM cores
>>>> on normal RX and TX packet done events (see 6261c4c8). So by default,
>>>> capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
>>>> Ethernet will still work just not quite as well.
>>> Actually the following patch fixes this issue and has already been
>>> applied on Shawn's tree:
>>> https://patchwork.kernel.org/patch/9153523/
>>>
>>> Also, this follow-up patch declared the HW workaround for SabreLite:
>>> https://patchwork.kernel.org/patch/9153525/
>>>
>>> So ideally, once those two patches land on your base tree, you could get
>>> rid of the #define and remove the HW workaround declaration.
>>>
>>> Finally, I'll test the series on Sabre-Lite this week.
>> I've applied this series on top of Shawn tree (for-next branch) in order
>> not to worry about the GPIO6 workaround.
>>
>> Although the camera seems to get enumerated properly, I can't seem to
>> get anything from it. See log:
>> http://pastebin.com/xnw1ujUq
> 
> Hi Gary, the driver does not implement vidioc_cropcap, it has
> switched to the new selection APIs and v4l2src should be using
> vidioc_g_selection instead of vidioc_cropcap.

The v4l2 core should emulate cropcap on top of the selection API,
so this should work (except for the known 4.7 regression, hopefully
that fix will be merged soon).

The only remaining need for cropcap would be to fill in the pixelaspect
ratio if the pixels aren't square.

Regards,

	Hans

> 
>>
>> In your cover letter, you said that you have not run through
>> v4l2-compliance. How have you tested the capture?
> 
> I use v4l2-ctl, and have used v4l2src in the past, but that was before
> switching to the selection APIs. Try the attached hack that adds
> vidioc_cropcap back in, and see how far you get on SabreLite with
> v4l2src. I tried  the following on SabreAuto:
> 
> gst-launch-1.0 v4l2src io_mode=4 ! 
> "video/x-raw,format=RGB16,width=640,height=480" ! fbdevsink
> 
>>
>> Also, why isn't the OV5640 MIPI camera declared on the SabreLite device
>> tree?
> 
> See Jack Mitchell's patch at http://ix.io/TTg. Thanks Jack! I will work on
> incorporating it.
> 
> 
> Steve
> 
> 

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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-17 19:01       ` Steve Longerbeam
@ 2016-06-20  9:33         ` Gary Bisson
  2016-06-20  9:44           ` Jack Mitchell
  0 siblings, 1 reply; 105+ messages in thread
From: Gary Bisson @ 2016-06-20  9:33 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-media, Jack Mitchell

Steve, Jack, All,

On Fri, Jun 17, 2016 at 12:01:41PM -0700, Steve Longerbeam wrote:
> 
> On 06/17/2016 08:18 AM, Gary Bisson wrote:
> > Steve, All,
> > 
> > On Thu, Jun 16, 2016 at 10:32:31AM +0200, Gary Bisson wrote:
> > > Steve, All,
> > > 
> > > On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
> > > > Defines the host video capture device node and an OV5642 camera sensor
> > > > node on i2c2. The host capture device connects to the OV5642 via the
> > > > parallel-bus mux input on the ipu1_csi0_mux.
> > > > 
> > > > Note there is a pin conflict with GPIO6. This pin functions as a power
> > > > input pin to the OV5642, but ENET requires it to wake-up the ARM cores
> > > > on normal RX and TX packet done events (see 6261c4c8). So by default,
> > > > capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
> > > > Ethernet will still work just not quite as well.
> > > Actually the following patch fixes this issue and has already been
> > > applied on Shawn's tree:
> > > https://patchwork.kernel.org/patch/9153523/
> > > 
> > > Also, this follow-up patch declared the HW workaround for SabreLite:
> > > https://patchwork.kernel.org/patch/9153525/
> > > 
> > > So ideally, once those two patches land on your base tree, you could get
> > > rid of the #define and remove the HW workaround declaration.
> > > 
> > > Finally, I'll test the series on Sabre-Lite this week.
> > I've applied this series on top of Shawn tree (for-next branch) in order
> > not to worry about the GPIO6 workaround.
> > 
> > Although the camera seems to get enumerated properly, I can't seem to
> > get anything from it. See log:
> > http://pastebin.com/xnw1ujUq
> 
> Hi Gary, the driver does not implement vidioc_cropcap, it has
> switched to the new selection APIs and v4l2src should be using
> vidioc_g_selection instead of vidioc_cropcap.

I confirm this was the issue, your patch fixes it.

> > In your cover letter, you said that you have not run through
> > v4l2-compliance. How have you tested the capture?
> 
> I use v4l2-ctl, and have used v4l2src in the past, but that was before
> switching to the selection APIs. Try the attached hack that adds
> vidioc_cropcap back in, and see how far you get on SabreLite with
> v4l2src. I tried  the following on SabreAuto:
> 
> gst-launch-1.0 v4l2src io_mode=4 !
> "video/x-raw,format=RGB16,width=640,height=480" ! fbdevsink

I confirm that works with OV5642 on SabreLite:
Tested-by: Gary Bisson <gary.bisson@boundarydevices.com>

> > Also, why isn't the OV5640 MIPI camera declared on the SabreLite device
> > tree?
> 
> See Jack Mitchell's patch at http://ix.io/TTg. Thanks Jack! I will work on
> incorporating it.

I've tried that patch have a some comments:
- When applied, no capture shows up any more, instead I have two m2m
  v4l2 devices [1].
- OV5640 Mipi is assigned the same address as OV5642, therefore both
  can't work at the same time right now. There's a register in the
  camera that allows to modify its I2C address, see this patch [2].
- How is the mclk working in this patch? It should be using the PWM3
  output to generate a ~22MHz clock. I would expect the use of a
  pwm-clock node [3].

Also another remark on both OV5642 and OV5640 patches, is it recommended
to use 0x80000000 pin muxing value? This leaves it to the bootloader to
properly setup the I/O. It sounds safer to properly set them up in the
device tree in my opinion in order not to be dependent on the bootloader.

All the above remarks said, thanks for this work on i.MX camera support,
that feature will be highly appreciated.

Regards,
Gary

[1] http://pastebin.com/i5MrhB1h
[2] https://github.com/boundarydevices/linux-imx6/commit/f915806d
[3] http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/clock/pwm-clock.txt

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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-20  9:33         ` Gary Bisson
@ 2016-06-20  9:44           ` Jack Mitchell
  2016-06-20 10:16             ` Gary Bisson
  0 siblings, 1 reply; 105+ messages in thread
From: Jack Mitchell @ 2016-06-20  9:44 UTC (permalink / raw)
  To: Gary Bisson, Steve Longerbeam; +Cc: linux-media



On 20/06/16 10:33, Gary Bisson wrote:
> Steve, Jack, All,
>
> On Fri, Jun 17, 2016 at 12:01:41PM -0700, Steve Longerbeam wrote:
>>
>> On 06/17/2016 08:18 AM, Gary Bisson wrote:
>>> Steve, All,
>>>
>>> On Thu, Jun 16, 2016 at 10:32:31AM +0200, Gary Bisson wrote:
>>>> Steve, All,
>>>>
>>>> On Tue, Jun 14, 2016 at 03:49:15PM -0700, Steve Longerbeam wrote:
>>>>> Defines the host video capture device node and an OV5642 camera sensor
>>>>> node on i2c2. The host capture device connects to the OV5642 via the
>>>>> parallel-bus mux input on the ipu1_csi0_mux.
>>>>>
>>>>> Note there is a pin conflict with GPIO6. This pin functions as a power
>>>>> input pin to the OV5642, but ENET requires it to wake-up the ARM cores
>>>>> on normal RX and TX packet done events (see 6261c4c8). So by default,
>>>>> capture is disabled, enable by uncommenting __OV5642_CAPTURE__ macro.
>>>>> Ethernet will still work just not quite as well.
>>>> Actually the following patch fixes this issue and has already been
>>>> applied on Shawn's tree:
>>>> https://patchwork.kernel.org/patch/9153523/
>>>>
>>>> Also, this follow-up patch declared the HW workaround for SabreLite:
>>>> https://patchwork.kernel.org/patch/9153525/
>>>>
>>>> So ideally, once those two patches land on your base tree, you could get
>>>> rid of the #define and remove the HW workaround declaration.
>>>>
>>>> Finally, I'll test the series on Sabre-Lite this week.
>>> I've applied this series on top of Shawn tree (for-next branch) in order
>>> not to worry about the GPIO6 workaround.
>>>
>>> Although the camera seems to get enumerated properly, I can't seem to
>>> get anything from it. See log:
>>> http://pastebin.com/xnw1ujUq
>>
>> Hi Gary, the driver does not implement vidioc_cropcap, it has
>> switched to the new selection APIs and v4l2src should be using
>> vidioc_g_selection instead of vidioc_cropcap.
>
> I confirm this was the issue, your patch fixes it.
>
>>> In your cover letter, you said that you have not run through
>>> v4l2-compliance. How have you tested the capture?
>>
>> I use v4l2-ctl, and have used v4l2src in the past, but that was before
>> switching to the selection APIs. Try the attached hack that adds
>> vidioc_cropcap back in, and see how far you get on SabreLite with
>> v4l2src. I tried  the following on SabreAuto:
>>
>> gst-launch-1.0 v4l2src io_mode=4 !
>> "video/x-raw,format=RGB16,width=640,height=480" ! fbdevsink
>
> I confirm that works with OV5642 on SabreLite:
> Tested-by: Gary Bisson <gary.bisson@boundarydevices.com>
>
>>> Also, why isn't the OV5640 MIPI camera declared on the SabreLite device
>>> tree?
>>
>> See Jack Mitchell's patch at http://ix.io/TTg. Thanks Jack! I will work on
>> incorporating it.

Hi Gary,

>
> I've tried that patch have a some comments:
> - When applied, no capture shows up any more, instead I have two m2m
>   v4l2 devices [1].
> - OV5640 Mipi is assigned the same address as OV5642, therefore both

Yes, I only have one device attached in my scenario.

>   can't work at the same time right now. There's a register in the
>   camera that allows to modify its I2C address, see this patch [2].
> - How is the mclk working in this patch? It should be using the PWM3

As mentioned I have an eCon sensor board [1] which generates it's own 
clock on the board and as such I don't need the PWM signal, just the two 
GPIOs.

>   output to generate a ~22MHz clock. I would expect the use of a
>   pwm-clock node [3].
>
> Also another remark on both OV5642 and OV5640 patches, is it recommended
> to use 0x80000000 pin muxing value? This leaves it to the bootloader to

I also wondered about this, but didn't know if the pinmux driver did 
this based on the define name? I tried it both ways and it worked so I 
just left it as it was.

Cheers,
Jack.

[1] https://www.e-consystems.com/iMX6-MIPI-Camera-Board.asp

> properly setup the I/O. It sounds safer to properly set them up in the
> device tree in my opinion in order not to be dependent on the bootloader.
>
> All the above remarks said, thanks for this work on i.MX camera support,
> that feature will be highly appreciated.
>
> Regards,
> Gary
>
> [1] http://pastebin.com/i5MrhB1h
> [2] https://github.com/boundarydevices/linux-imx6/commit/f915806d
> [3] http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/clock/pwm-clock.txt
> --
> 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] 105+ messages in thread

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-20  9:44           ` Jack Mitchell
@ 2016-06-20 10:16             ` Gary Bisson
  2016-06-20 10:44               ` Jack Mitchell
  0 siblings, 1 reply; 105+ messages in thread
From: Gary Bisson @ 2016-06-20 10:16 UTC (permalink / raw)
  To: Jack Mitchell; +Cc: Steve Longerbeam, linux-media

Jack, All,

On Mon, Jun 20, 2016 at 10:44:44AM +0100, Jack Mitchell wrote:
> <snip>
> > I've tried that patch have a some comments:
> > - When applied, no capture shows up any more, instead I have two m2m
> >   v4l2 devices [1].
> > - OV5640 Mipi is assigned the same address as OV5642, therefore both
> 
> Yes, I only have one device attached in my scenario.

Thanks for confirming.

> >   can't work at the same time right now. There's a register in the
> >   camera that allows to modify its I2C address, see this patch [2].
> > - How is the mclk working in this patch? It should be using the PWM3
> 
> As mentioned I have an eCon sensor board [1] which generates it's own clock
> on the board and as such I don't need the PWM signal, just the two GPIOs.

Oh ok, thanks I didn't this sensor board was different than ours [1].

But in your patch, you specifically disable pwm3, what's the reason for
it?

> >   output to generate a ~22MHz clock. I would expect the use of a
> >   pwm-clock node [3].
> > 
> > Also another remark on both OV5642 and OV5640 patches, is it recommended
> > to use 0x80000000 pin muxing value? This leaves it to the bootloader to
> 
> I also wondered about this, but didn't know if the pinmux driver did this
> based on the define name? I tried it both ways and it worked so I just left
> it as it was.

Actually my phrasing is wrong, the muxing is ok. Yes depending on the
name a pin will be muxed to one function or another. The problem is the
pad configuration (pull-up, pull-down etc..). I am not surprised that it
works, because the bootloader should properly set those. But it would be
safer IMO not to rely on it.

Regards,
Gary

[1] https://boundarydevices.com/product/nit6x_5mp_mipi/

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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-20 10:16             ` Gary Bisson
@ 2016-06-20 10:44               ` Jack Mitchell
  2016-06-21 17:17                 ` Steve Longerbeam
  0 siblings, 1 reply; 105+ messages in thread
From: Jack Mitchell @ 2016-06-20 10:44 UTC (permalink / raw)
  To: Gary Bisson; +Cc: Steve Longerbeam, linux-media



On 20/06/16 11:16, Gary Bisson wrote:
> Jack, All,
>
> On Mon, Jun 20, 2016 at 10:44:44AM +0100, Jack Mitchell wrote:
>> <snip>
>>> I've tried that patch have a some comments:
>>> - When applied, no capture shows up any more, instead I have two m2m
>>>   v4l2 devices [1].
>>> - OV5640 Mipi is assigned the same address as OV5642, therefore both
>>
>> Yes, I only have one device attached in my scenario.
>
> Thanks for confirming.
>
>>>   can't work at the same time right now. There's a register in the
>>>   camera that allows to modify its I2C address, see this patch [2].
>>> - How is the mclk working in this patch? It should be using the PWM3
>>
>> As mentioned I have an eCon sensor board [1] which generates it's own clock
>> on the board and as such I don't need the PWM signal, just the two GPIOs.
>
> Oh ok, thanks I didn't this sensor board was different than ours [1].
>
> But in your patch, you specifically disable pwm3, what's the reason for
> it?

Yes, it uses the GPIO on the PWM3 pin (beats me why...) so I had to 
specifically disable it to stop the pin muxing clash.

>
>>>   output to generate a ~22MHz clock. I would expect the use of a
>>>   pwm-clock node [3].
>>>
>>> Also another remark on both OV5642 and OV5640 patches, is it recommended
>>> to use 0x80000000 pin muxing value? This leaves it to the bootloader to
>>
>> I also wondered about this, but didn't know if the pinmux driver did this
>> based on the define name? I tried it both ways and it worked so I just left
>> it as it was.
>
> Actually my phrasing is wrong, the muxing is ok. Yes depending on the
> name a pin will be muxed to one function or another. The problem is the
> pad configuration (pull-up, pull-down etc..). I am not surprised that it
> works, because the bootloader should properly set those. But it would be
> safer IMO not to rely on it.

Ah ok, makes sense.

>
> Regards,
> Gary
>
> [1] https://boundarydevices.com/product/nit6x_5mp_mipi/
>

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

* Re: [19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections
  2016-06-20 10:44               ` Jack Mitchell
@ 2016-06-21 17:17                 ` Steve Longerbeam
  0 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-06-21 17:17 UTC (permalink / raw)
  To: Jack Mitchell, Gary Bisson; +Cc: linux-media

On 06/20/2016 03:44 AM, Jack Mitchell wrote:
>
>
> On 20/06/16 11:16, Gary Bisson wrote:
>> Jack, All,
>>
>> On Mon, Jun 20, 2016 at 10:44:44AM +0100, Jack Mitchell wrote:
>>> <snip>
>>>> I've tried that patch have a some comments:
>>>> - When applied, no capture shows up any more, instead I have two m2m
>>>>   v4l2 devices [1].
>>>> - OV5640 Mipi is assigned the same address as OV5642, therefore both
>>>
>>> Yes, I only have one device attached in my scenario.
>>
>> Thanks for confirming.
>>
>>>>   can't work at the same time right now. There's a register in the
>>>>   camera that allows to modify its I2C address, see this patch [2].
>>>> - How is the mclk working in this patch? It should be using the PWM3
>>>
>>> As mentioned I have an eCon sensor board [1] which generates it's own clock
>>> on the board and as such I don't need the PWM signal, just the two GPIOs.
>>
>> Oh ok, thanks I didn't this sensor board was different than ours [1].
>>
>> But in your patch, you specifically disable pwm3, what's the reason for
>> it?
>
> Yes, it uses the GPIO on the PWM3 pin (beats me why...) so I had to specifically disable it to stop the pin muxing clash.
>

Hi Jack, in your patch, the PWM3 pin (which is SD1_DAT1 operating as GPIO1_IO17)
is being used as the power-down pin for your OV5640 board.

Anyway, I didn't realize this was a different camera from the boundary-devices
module (https://boundarydevices.com/product/nit6x_5mp_mipi/).

I would like to add support in the DT for the BD module, but I am unable to test/debug
as I don't have one. I'm wondering if someone could lend me one, along with the
schematics. I have the OV5642 parallel interface module for the SabreLite, so I'd love
to get my hands on the OV5640 mipi module as that would allow testing of multiple
camera capture which I've never been able to do before.

In the meantime I am going to omit support for this module in the sabrelite DT (there's
also the problem of the i2c bus address conflict on the same i2c2 bus with the ov5642).

Or if someone can add support for the BD module later that would be great.


Steve

>>
>>>>   output to generate a ~22MHz clock. I would expect the use of a
>>>>   pwm-clock node [3].
>>>>
>>>> Also another remark on both OV5642 and OV5640 patches, is it recommended
>>>> to use 0x80000000 pin muxing value? This leaves it to the bootloader to
>>>
>>> I also wondered about this, but didn't know if the pinmux driver did this
>>> based on the define name? I tried it both ways and it worked so I just left
>>> it as it was.
>>
>> Actually my phrasing is wrong, the muxing is ok. Yes depending on the
>> name a pin will be muxed to one function or another. The problem is the
>> pad configuration (pull-up, pull-down etc..). I am not surprised that it
>> works, because the bootloader should properly set those. But it would be
>> safer IMO not to rely on it.
>
> Ah ok, makes sense.
>
>>
>> Regards,
>> Gary
>>
>> [1] https://boundarydevices.com/product/nit6x_5mp_mipi/
>>


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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (38 preceding siblings ...)
  2016-06-15 10:43 ` [PATCH 00/38] i.MX5/6 Video Capture Jack Mitchell
@ 2016-06-28 18:54 ` Tim Harvey
  2016-06-28 20:10   ` Hans Verkuil
  2016-07-02  3:38   ` Steve Longerbeam
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
  40 siblings, 2 replies; 105+ messages in thread
From: Tim Harvey @ 2016-06-28 18:54 UTC (permalink / raw)
  To: Steve Longerbeam
  Cc: linux-media, Steve Longerbeam, Philipp Zabel, Hans Verkuil

On Tue, Jun 14, 2016 at 3:48 PM, Steve Longerbeam <slongerbeam@gmail.com> wrote:
> Tested on imx6q SabreAuto with ADV7180, and imx6q SabreSD with
> mipi-csi2 OV5640. There is device-tree support also for imx6qdl
> SabreLite, but that is not tested. Also, this driver should
> theoretically work on i.MX5 targets, but that is also untested.
>
> Not run through v4l2-compliance yet, but that is in my queue.
>
>

Steve,

I've tested this series successfully with a Gateworks Ventana GW5300
which has an IMX6Q and an adv7180 attached to IPU2_CSI1.

First of all, a big 'thank you' for taking the time to rebase and
re-submit this series!

However based on the lack of feedback of the individual patches as
well as the fact they touch varied subsystems I think we are going to
have better luck getting this functionality accepted if you broke it
into separate series.

Here are my thoughts:

>   gpu: ipu-v3: Add Video Deinterlacer unit
>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>   gpu: ipu-v3: Add ipu_get_num()
>   gpu: ipu-v3: Add IDMA channel linking support
>   gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>   gpu: ipu-v3: Add VDI input IDMAC channels
>   gpu: ipu-v3: Add ipu_csi_set_src()
>   gpu: ipu-v3: Add ipu_ic_set_src()
>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>   gpu: ipu-v3: Fix IRT usage
>   gpu: ipu-v3: Fix CSI0 blur in NTSC format
>   gpu: ipu-ic: Add complete image conversion support with tiling
>   gpu: ipu-ic: allow multiple handles to ic
>   gpu: ipu-v3: rename CSI client device

These are all enhancements to the ipu-v3 driver shared by DRM and
maintained by Philipp Zabel and there is no way to 'stage' them.
Philipp, these have bee submitted previously with little to no changes
or feedback - can we get sign-off or comment on these from you?

Next I would submit the set of drivers that depend on the above into
staging as Hans has said he would accept those (assuming the deps are
accepted). Also, you should submit the drivers first in your series,
then the imx6q.dtsi/imx6qdl.dtsi patches following such as:

>   media: imx: Add MIPI CSI-2 Receiver driver
>   media: Add camera interface driver for i.MX5/6
>   media: Add i.MX5/6 mem2mem driver
>   media: imx: Add video switch
>   ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
>   ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections
>   ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
>   ARM: dts: imx6qdl: add mem2mem device for sabre* boards

I think this last one should add the mem2mem nodes to imx6q.dtsi and
imx6dl.dtsi to be useable by all boards with IPUs right?

After this we have a platform that many imx6 boards with capture
devices can build on.

In parallel, you have a couple of other dependencies before saber*
boards have full capture support that need to through other trees:

>   gpio: pca953x: Add reset-gpios property

This should be submitted through the linux-gpio list/subsystem.

>   clocksource/drivers/imx: add input capture support

Not sure what path this one goes through but it looks to me this patch
adds a feature that only some boards may require (input-capture).

>   media: imx: Add support for MIPI CSI-2 OV5640
>   media: imx: Add support for Parallel OV5642

shouldn't these be subdev drivers? Perhaps the can make it into
staging at least in the form you have them now.

>   v4l: Add signal lock status to source change events
>   media: imx: Add support for ADV7180 Video Decoder

These should be dropped (the 1st is a dependency of the 2nd) and
instead we should be using the subdev driver. I believe you have this
working, and I have been somewhat successful with some of your patches
as well although I still have a 'rolling image' and do not appear to
be getting signal detect interrupts after the first one (which is
likely causing the rolling).

>   media: adv7180: add power pin control
>   media: adv7180: implement g_parm

These seem very reasonable and indeed go to linux-media but perhaps
should be split out from the imx6 patchset to be able to get more
attention on them?

>   ARM: dts: imx6-sabrelite: add video capture ports and connections
>   ARM: dts: imx6-sabresd: add video capture ports and connections
>   ARM: dts: imx6-sabreauto: create i2cmux for i2c3
>   ARM: dts: imx6-sabreauto: add reset-gpios property for max7310
>   ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
>   ARM: dts: imx6-sabreauto: add video capture ports and connections

These should probably be last in a series on their own as there are
several dependencies within them for things that need to take
alternate submission paths.

Regards,

Tim

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-28 18:54 ` Tim Harvey
@ 2016-06-28 20:10   ` Hans Verkuil
  2016-07-01  7:19     ` Hans Verkuil
  2016-07-02  3:38   ` Steve Longerbeam
  1 sibling, 1 reply; 105+ messages in thread
From: Hans Verkuil @ 2016-06-28 20:10 UTC (permalink / raw)
  To: Tim Harvey, Steve Longerbeam; +Cc: linux-media, Steve Longerbeam, Philipp Zabel

On 06/28/2016 08:54 PM, Tim Harvey wrote:
> On Tue, Jun 14, 2016 at 3:48 PM, Steve Longerbeam <slongerbeam@gmail.com> wrote:
>> Tested on imx6q SabreAuto with ADV7180, and imx6q SabreSD with
>> mipi-csi2 OV5640. There is device-tree support also for imx6qdl
>> SabreLite, but that is not tested. Also, this driver should
>> theoretically work on i.MX5 targets, but that is also untested.
>>
>> Not run through v4l2-compliance yet, but that is in my queue.
>>
>>
> 
> Steve,
> 
> I've tested this series successfully with a Gateworks Ventana GW5300
> which has an IMX6Q and an adv7180 attached to IPU2_CSI1.
> 
> First of all, a big 'thank you' for taking the time to rebase and
> re-submit this series!
> 
> However based on the lack of feedback of the individual patches as

It's on my TODO list, but the series was a lot larger than I expected (and
touched on a lot of subsystems as well), so I postponed looking at this
until I have a bit more time.

> well as the fact they touch varied subsystems I think we are going to
> have better luck getting this functionality accepted if you broke it
> into separate series.
> 
> Here are my thoughts:
> 
>>   gpu: ipu-v3: Add Video Deinterlacer unit
>>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>>   gpu: ipu-v3: Add ipu_get_num()
>>   gpu: ipu-v3: Add IDMA channel linking support
>>   gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>>   gpu: ipu-v3: Add VDI input IDMAC channels
>>   gpu: ipu-v3: Add ipu_csi_set_src()
>>   gpu: ipu-v3: Add ipu_ic_set_src()
>>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>>   gpu: ipu-v3: Fix IRT usage
>>   gpu: ipu-v3: Fix CSI0 blur in NTSC format
>>   gpu: ipu-ic: Add complete image conversion support with tiling
>>   gpu: ipu-ic: allow multiple handles to ic
>>   gpu: ipu-v3: rename CSI client device
> 
> These are all enhancements to the ipu-v3 driver shared by DRM and
> maintained by Philipp Zabel and there is no way to 'stage' them.
> Philipp, these have bee submitted previously with little to no changes
> or feedback - can we get sign-off or comment on these from you?

I'd like to know the status of this as well. If this can't go in, then
accepting the v4l2 driver in staging will likely be very difficult if not
impossible.

Regards,

	Hans

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-28 20:10   ` Hans Verkuil
@ 2016-07-01  7:19     ` Hans Verkuil
  0 siblings, 0 replies; 105+ messages in thread
From: Hans Verkuil @ 2016-07-01  7:19 UTC (permalink / raw)
  To: Tim Harvey, Steve Longerbeam; +Cc: linux-media, Steve Longerbeam, Philipp Zabel

On 06/28/2016 10:10 PM, Hans Verkuil wrote:
> On 06/28/2016 08:54 PM, Tim Harvey wrote:
>> On Tue, Jun 14, 2016 at 3:48 PM, Steve Longerbeam <slongerbeam@gmail.com> wrote:
>>> Tested on imx6q SabreAuto with ADV7180, and imx6q SabreSD with
>>> mipi-csi2 OV5640. There is device-tree support also for imx6qdl
>>> SabreLite, but that is not tested. Also, this driver should
>>> theoretically work on i.MX5 targets, but that is also untested.
>>>
>>> Not run through v4l2-compliance yet, but that is in my queue.
>>>
>>>
>>
>> Steve,
>>
>> I've tested this series successfully with a Gateworks Ventana GW5300
>> which has an IMX6Q and an adv7180 attached to IPU2_CSI1.
>>
>> First of all, a big 'thank you' for taking the time to rebase and
>> re-submit this series!
>>
>> However based on the lack of feedback of the individual patches as
> 
> It's on my TODO list, but the series was a lot larger than I expected (and
> touched on a lot of subsystems as well), so I postponed looking at this
> until I have a bit more time.

I scanned through it and the only thing I won't accept in staging is the
adv7180 driver. I also have a question about patch 28, but I'll ask that
separately. The ov5642 is also a duplicate, but the mainline version is tied
to soc_camera, so I have no problem with adding a non-soc-camera driver.

>> well as the fact they touch varied subsystems I think we are going to
>> have better luck getting this functionality accepted if you broke it
>> into separate series.
>>
>> Here are my thoughts:
>>
>>>   gpu: ipu-v3: Add Video Deinterlacer unit
>>>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>>>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>>>   gpu: ipu-v3: Add ipu_get_num()
>>>   gpu: ipu-v3: Add IDMA channel linking support
>>>   gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>>>   gpu: ipu-v3: Add VDI input IDMAC channels
>>>   gpu: ipu-v3: Add ipu_csi_set_src()
>>>   gpu: ipu-v3: Add ipu_ic_set_src()
>>>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>>>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>>>   gpu: ipu-v3: Fix IRT usage
>>>   gpu: ipu-v3: Fix CSI0 blur in NTSC format
>>>   gpu: ipu-ic: Add complete image conversion support with tiling
>>>   gpu: ipu-ic: allow multiple handles to ic
>>>   gpu: ipu-v3: rename CSI client device
>>
>> These are all enhancements to the ipu-v3 driver shared by DRM and
>> maintained by Philipp Zabel and there is no way to 'stage' them.
>> Philipp, these have bee submitted previously with little to no changes
>> or feedback - can we get sign-off or comment on these from you?
> 
> I'd like to know the status of this as well. If this can't go in, then
> accepting the v4l2 driver in staging will likely be very difficult if not
> impossible.

This is the first thing that needs to happen. Philipp, please take a look at this!

Thanks,

	Hans

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

* Re: [PATCH 28/38] v4l: Add signal lock status to source change events
  2016-06-14 22:49 ` [PATCH 28/38] v4l: Add signal lock status to source change events Steve Longerbeam
@ 2016-07-01  7:24   ` Hans Verkuil
  2016-07-02  3:59     ` Steve Longerbeam
  0 siblings, 1 reply; 105+ messages in thread
From: Hans Verkuil @ 2016-07-01  7:24 UTC (permalink / raw)
  To: Steve Longerbeam, linux-media; +Cc: Steve Longerbeam

On 06/15/2016 12:49 AM, Steve Longerbeam wrote:
> Add a signal lock status change to the source changes bitmask.
> This indicates there was a signal lock or unlock event detected
> at the input of a video decoder.
> 
> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
> ---
>  Documentation/DocBook/media/v4l/vidioc-dqevent.xml | 12 ++++++++++--
>  include/uapi/linux/videodev2.h                     |  1 +
>  2 files changed, 11 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
> index c9c3c77..7758ad7 100644
> --- a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
> +++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
> @@ -233,8 +233,9 @@
>  	    <entry>
>  	      <para>This event is triggered when a source parameter change is
>  	       detected during runtime by the video device. It can be a
> -	       runtime resolution change triggered by a video decoder or the
> -	       format change happening on an input connector.
> +	       runtime resolution change or signal lock status change
> +	       triggered by a video decoder, or the format change happening
> +	       on an input connector.
>  	       This event requires that the <structfield>id</structfield>
>  	       matches the input index (when used with a video device node)
>  	       or the pad index (when used with a subdevice node) from which
> @@ -461,6 +462,13 @@
>  	    from a video decoder.
>  	    </entry>
>  	  </row>
> +	  <row>
> +	    <entry><constant>V4L2_EVENT_SRC_CH_LOCK_STATUS</constant></entry>
> +	    <entry>0x0002</entry>
> +	    <entry>This event gets triggered when there is a signal lock or
> +	    unlock detected at the input of a video decoder.
> +	    </entry>
> +	  </row>

I'm not entirely sure I like this. Typically losing lock means that this event
is triggered with the V4L2_EVENT_SRC_CH_RESOLUTION flag set, and userspace has
to check the new timings etc., which will fail if there is no lock anymore.

This information is also available through ENUMINPUT.

I would need to know more about why you think this is needed, because I don't
see what this adds.

Regards,

	Hans

>  	</tbody>
>        </tgroup>
>      </table>
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 8f95191..2eba5da 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -2076,6 +2076,7 @@ struct v4l2_event_frame_sync {
>  };
>  
>  #define V4L2_EVENT_SRC_CH_RESOLUTION		(1 << 0)
> +#define V4L2_EVENT_SRC_CH_LOCK_STATUS		(1 << 1)
>  
>  struct v4l2_event_src_change {
>  	__u32 changes;
> 

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

* Re: [PATCH 00/38] i.MX5/6 Video Capture
  2016-06-28 18:54 ` Tim Harvey
  2016-06-28 20:10   ` Hans Verkuil
@ 2016-07-02  3:38   ` Steve Longerbeam
  1 sibling, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-02  3:38 UTC (permalink / raw)
  To: Tim Harvey; +Cc: linux-media, Steve Longerbeam, Philipp Zabel, Hans Verkuil

Hi Tim,

Pardon late reply, I'm on vacation. See below...


On 06/28/2016 11:54 AM, Tim Harvey wrote:
> On Tue, Jun 14, 2016 at 3:48 PM, Steve Longerbeam <slongerbeam@gmail.com> wrote:
>> Tested on imx6q SabreAuto with ADV7180, and imx6q SabreSD with
>> mipi-csi2 OV5640. There is device-tree support also for imx6qdl
>> SabreLite, but that is not tested. Also, this driver should
>> theoretically work on i.MX5 targets, but that is also untested.
>>
>> Not run through v4l2-compliance yet, but that is in my queue.
>>
>>
> Steve,
>
> I've tested this series successfully with a Gateworks Ventana GW5300
> which has an IMX6Q and an adv7180 attached to IPU2_CSI1.
>
> First of all, a big 'thank you' for taking the time to rebase and
> re-submit this series!
>
> However based on the lack of feedback of the individual patches as
> well as the fact they touch varied subsystems I think we are going to
> have better luck getting this functionality accepted if you broke it
> into separate series.
>
> Here are my thoughts:
>
>>    gpu: ipu-v3: Add Video Deinterlacer unit
>>    gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>>    gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>>    gpu: ipu-v3: Add ipu_get_num()
>>    gpu: ipu-v3: Add IDMA channel linking support
>>    gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>>    gpu: ipu-v3: Add VDI input IDMAC channels
>>    gpu: ipu-v3: Add ipu_csi_set_src()
>>    gpu: ipu-v3: Add ipu_ic_set_src()
>>    gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>>    gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>>    gpu: ipu-v3: Fix IRT usage
>>    gpu: ipu-v3: Fix CSI0 blur in NTSC format
>>    gpu: ipu-ic: Add complete image conversion support with tiling
>>    gpu: ipu-ic: allow multiple handles to ic
>>    gpu: ipu-v3: rename CSI client device
> These are all enhancements to the ipu-v3 driver shared by DRM and
> maintained by Philipp Zabel and there is no way to 'stage' them.

Just a note here, all these patches to ipu-v3 driver are to the
capture units, and should have no effect on DRM.

> Philipp, these have bee submitted previously with little to no changes
> or feedback - can we get sign-off or comment on these from you?
>
> Next I would submit the set of drivers that depend on the above into
> staging as Hans has said he would accept those (assuming the deps are
> accepted). Also, you should submit the drivers first in your series,
> then the imx6q.dtsi/imx6qdl.dtsi patches following such as:
>
>>    media: imx: Add MIPI CSI-2 Receiver driver
>>    media: Add camera interface driver for i.MX5/6
>>    media: Add i.MX5/6 mem2mem driver
>>    media: imx: Add video switch
>>    ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
>>    ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections
>>    ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
>>    ARM: dts: imx6qdl: add mem2mem device for sabre* boards

Ok I will reorder the patches.

> I think this last one should add the mem2mem nodes to imx6q.dtsi and
> imx6dl.dtsi to be useable by all boards with IPUs right?

Yeah, I'll move the mem2mem nodes to imx6qdl.dtsi and imx6q.dtsi.

>
> After this we have a platform that many imx6 boards with capture
> devices can build on.
>
> In parallel, you have a couple of other dependencies before saber*
> boards have full capture support that need to through other trees:
>
>>    gpio: pca953x: Add reset-gpios property
> This should be submitted through the linux-gpio list/subsystem.

I've really got a lot on my plate, I'd appreciate if someone else
could help me out with that.

>
>>    clocksource/drivers/imx: add input capture support
> Not sure what path this one goes through but it looks to me this patch
> adds a feature that only some boards may require (input-capture).

Well, the input-capture support should be usable by any imx6 based
target, and not just for v4l2 subsystem.

>
>>    media: imx: Add support for MIPI CSI-2 OV5640
>>    media: imx: Add support for Parallel OV5642
> shouldn't these be subdev drivers?

Well, they _are_ subdev drivers. I guess you mean they should be
moved to drivers/media/i2c? Agreed, at some point they need some
cleaning up and probably consolidated into a single subdev and moved
there.

>   Perhaps the can make it into
> staging at least in the form you have them now.
>
>>    v4l: Add signal lock status to source change events
>>    media: imx: Add support for ADV7180 Video Decoder
> These should be dropped (the 1st is a dependency of the 2nd) and
> instead we should be using the subdev driver. I believe you have this
> working, and I have been somewhat successful with some of your patches
> as well although I still have a 'rolling image' and do not appear to
> be getting signal detect interrupts after the first one (which is
> likely causing the rolling).

Tim, I've started a new branch 'adv718x' in my mediatree github fork
and moved all the patches to drivers/media/i2c/adv7180.c there. Note
the first commit in that branch!

As I'm currently on vacation and away from the h/w I won't be able to test
this branch with imx6 backend until I return on 7/6. Once the branch is
tested on the SabreAuto I will drop the staging adv7180.c driver.


>
>>    media: adv7180: add power pin control
>>    media: adv7180: implement g_parm
> These seem very reasonable and indeed go to linux-media but perhaps
> should be split out from the imx6 patchset to be able to get more
> attention on them?

Yes, see above.

>
>>    ARM: dts: imx6-sabrelite: add video capture ports and connections
>>    ARM: dts: imx6-sabresd: add video capture ports and connections
>>    ARM: dts: imx6-sabreauto: create i2cmux for i2c3
>>    ARM: dts: imx6-sabreauto: add reset-gpios property for max7310
>>    ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture
>>    ARM: dts: imx6-sabreauto: add video capture ports and connections
> These should probably be last in a series on their own as there are
> several dependencies within them for things that need to take
> alternate submission paths.

Agreed, I will reorder things.

Steve


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

* Re: [PATCH 28/38] v4l: Add signal lock status to source change events
  2016-07-01  7:24   ` Hans Verkuil
@ 2016-07-02  3:59     ` Steve Longerbeam
  0 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-02  3:59 UTC (permalink / raw)
  To: Hans Verkuil, Steve Longerbeam, linux-media



On 07/01/2016 12:24 AM, Hans Verkuil wrote:
> On 06/15/2016 12:49 AM, Steve Longerbeam wrote:
>> Add a signal lock status change to the source changes bitmask.
>> This indicates there was a signal lock or unlock event detected
>> at the input of a video decoder.
>>
>> Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
>> ---
>>   Documentation/DocBook/media/v4l/vidioc-dqevent.xml | 12 ++++++++++--
>>   include/uapi/linux/videodev2.h                     |  1 +
>>   2 files changed, 11 insertions(+), 2 deletions(-)
>>
>> diff --git a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
>> index c9c3c77..7758ad7 100644
>> --- a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
>> +++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml
>> @@ -233,8 +233,9 @@
>>   	    <entry>
>>   	      <para>This event is triggered when a source parameter change is
>>   	       detected during runtime by the video device. It can be a
>> -	       runtime resolution change triggered by a video decoder or the
>> -	       format change happening on an input connector.
>> +	       runtime resolution change or signal lock status change
>> +	       triggered by a video decoder, or the format change happening
>> +	       on an input connector.
>>   	       This event requires that the <structfield>id</structfield>
>>   	       matches the input index (when used with a video device node)
>>   	       or the pad index (when used with a subdevice node) from which
>> @@ -461,6 +462,13 @@
>>   	    from a video decoder.
>>   	    </entry>
>>   	  </row>
>> +	  <row>
>> +	    <entry><constant>V4L2_EVENT_SRC_CH_LOCK_STATUS</constant></entry>
>> +	    <entry>0x0002</entry>
>> +	    <entry>This event gets triggered when there is a signal lock or
>> +	    unlock detected at the input of a video decoder.
>> +	    </entry>
>> +	  </row>
> I'm not entirely sure I like this. Typically losing lock means that this event
> is triggered with the V4L2_EVENT_SRC_CH_RESOLUTION flag set, and userspace has
> to check the new timings etc., which will fail if there is no lock anymore.
>
> This information is also available through ENUMINPUT.
>
> I would need to know more about why you think this is needed, because I don't
> see what this adds.

Hi Hans,

At least on the ADV718x, a source resolution change (from an 
autodetected video
standard change) and a signal lock status change are distinct events. 
For example
there can be a temporary loss of input signal lock without a change in 
detected
input video standard/resolution.

Steve


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

* [PATCH 00/28] i.MX5/6 Video Capture, v2
  2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
                   ` (39 preceding siblings ...)
  2016-06-28 18:54 ` Tim Harvey
@ 2016-07-06 23:06 ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 01/28] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
                     ` (27 more replies)
  40 siblings, 28 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Philipp Zabel (2):
  media: imx: Add video switch
  ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their
    connections

Steve Longerbeam (25):
  gpu: ipu-v3: Add Video Deinterlacer unit
  gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  gpu: ipu-v3: Add ipu_get_num()
  gpu: ipu-v3: Add IDMA channel linking support
  gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  gpu: ipu-v3: Add VDI input IDMAC channels
  gpu: ipu-v3: Add ipu_csi_set_src()
  gpu: ipu-v3: Add ipu_ic_set_src()
  gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  gpu: ipu-v3: Fix IRT usage
  gpu: ipu-ic: Add complete image conversion support with tiling
  gpu: ipu-ic: allow multiple handles to ic
  gpu: ipu-v3: rename CSI client device
  gpio: pca953x: Add optional reset gpio control
  clocksource/drivers/imx: add input capture support
  media: Add i.MX5/6 camera interface driver
  media: imx: Add MIPI CSI-2 Receiver driver
  media: imx: Add support for MIPI CSI-2 OV5640
  media: imx: Add support for Parallel OV5642
  media: Add i.MX5/6 mem2mem driver
  ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
  ARM: dts: imx6qdl: add mem2mem devices
  ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers

Suresh Dhandapani (1):
  gpu: ipu-v3: Fix CSI0 blur in NTSC format

 Documentation/devicetree/bindings/media/imx.txt    |  449 ++
 Documentation/video4linux/imx_camera.txt           |  243 ++
 arch/arm/boot/dts/imx6dl.dtsi                      |  183 +
 arch/arm/boot/dts/imx6q.dtsi                       |  127 +
 arch/arm/boot/dts/imx6qdl.dtsi                     |   19 +
 arch/arm/configs/imx_v6_v7_defconfig               |    2 +
 drivers/clocksource/timer-imx-gpt.c                |  463 ++-
 drivers/gpio/gpio-pca953x.c                        |   18 +
 drivers/gpu/ipu-v3/Makefile                        |    2 +-
 drivers/gpu/ipu-v3/ipu-common.c                    |  155 +-
 drivers/gpu/ipu-v3/ipu-cpmem.c                     |   13 +
 drivers/gpu/ipu-v3/ipu-csi.c                       |   36 +-
 drivers/gpu/ipu-v3/ipu-ic.c                        | 1769 +++++++-
 drivers/gpu/ipu-v3/ipu-prv.h                       |    7 +
 drivers/gpu/ipu-v3/ipu-vdi.c                       |  266 ++
 drivers/staging/media/Kconfig                      |    2 +
 drivers/staging/media/Makefile                     |    1 +
 drivers/staging/media/imx/Kconfig                  |   35 +
 drivers/staging/media/imx/Makefile                 |    2 +
 drivers/staging/media/imx/capture/Kconfig          |   35 +
 drivers/staging/media/imx/capture/Makefile         |    9 +
 drivers/staging/media/imx/capture/imx-camif.c      | 2326 +++++++++++
 drivers/staging/media/imx/capture/imx-camif.h      |  270 ++
 drivers/staging/media/imx/capture/imx-csi.c        |  195 +
 drivers/staging/media/imx/capture/imx-ic-prpenc.c  |  661 +++
 drivers/staging/media/imx/capture/imx-of.c         |  354 ++
 drivers/staging/media/imx/capture/imx-of.h         |   18 +
 drivers/staging/media/imx/capture/imx-smfc.c       |  506 +++
 drivers/staging/media/imx/capture/imx-vdic.c       |  995 +++++
 .../staging/media/imx/capture/imx-video-switch.c   |  347 ++
 drivers/staging/media/imx/capture/mipi-csi2.c      |  373 ++
 drivers/staging/media/imx/capture/ov5640-mipi.c    | 2303 +++++++++++
 drivers/staging/media/imx/capture/ov5642.c         | 4309 ++++++++++++++++++++
 drivers/staging/media/imx/m2m/Makefile             |    1 +
 drivers/staging/media/imx/m2m/imx-m2m.c            | 1049 +++++
 include/linux/mxc_icap.h                           |   20 +
 include/media/imx.h                                |   15 +
 include/uapi/Kbuild                                |    1 +
 include/uapi/linux/v4l2-controls.h                 |    4 +
 include/uapi/media/Kbuild                          |    2 +
 include/uapi/media/imx.h                           |   22 +
 include/video/imx-ipu-v3.h                         |   96 +-
 42 files changed, 17596 insertions(+), 107 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/imx.txt
 create mode 100644 Documentation/video4linux/imx_camera.txt
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c
 create mode 100644 drivers/staging/media/imx/Kconfig
 create mode 100644 drivers/staging/media/imx/Makefile
 create mode 100644 drivers/staging/media/imx/capture/Kconfig
 create mode 100644 drivers/staging/media/imx/capture/Makefile
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.c
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.h
 create mode 100644 drivers/staging/media/imx/capture/imx-csi.c
 create mode 100644 drivers/staging/media/imx/capture/imx-ic-prpenc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.h
 create mode 100644 drivers/staging/media/imx/capture/imx-smfc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-vdic.c
 create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c
 create mode 100644 drivers/staging/media/imx/capture/mipi-csi2.c
 create mode 100644 drivers/staging/media/imx/capture/ov5640-mipi.c
 create mode 100644 drivers/staging/media/imx/capture/ov5642.c
 create mode 100644 drivers/staging/media/imx/m2m/Makefile
 create mode 100644 drivers/staging/media/imx/m2m/imx-m2m.c
 create mode 100644 include/linux/mxc_icap.h
 create mode 100644 include/media/imx.h
 create mode 100644 include/uapi/media/Kbuild
 create mode 100644 include/uapi/media/imx.h

-- 
1.9.1


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

* [PATCH 01/28] gpu: ipu-v3: Add Video Deinterlacer unit
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 02/28] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds the Video Deinterlacer (VDIC) unit.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/Makefile     |   2 +-
 drivers/gpu/ipu-v3/ipu-common.c |  11 ++
 drivers/gpu/ipu-v3/ipu-prv.h    |   6 +
 drivers/gpu/ipu-v3/ipu-vdi.c    | 266 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  27 ++++
 5 files changed, 311 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c

diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile
index 107ec23..aeba9dc 100644
--- a/drivers/gpu/ipu-v3/Makefile
+++ b/drivers/gpu/ipu-v3/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
 
 imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
-		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
+		ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 99dcacf..30dc115 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -833,6 +833,14 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
 		goto err_ic;
 	}
 
+	ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
+			   IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
+			   IPU_CONF_IC_INPUT);
+	if (ret) {
+		unit = "vdi";
+		goto err_vdi;
+	}
+
 	ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
 			  IPU_CONF_DI0_EN, ipu_clk);
 	if (ret) {
@@ -887,6 +895,8 @@ err_dc:
 err_di_1:
 	ipu_di_exit(ipu, 0);
 err_di_0:
+	ipu_vdi_exit(ipu);
+err_vdi:
 	ipu_ic_exit(ipu);
 err_ic:
 	ipu_csi_exit(ipu, 1);
@@ -971,6 +981,7 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
 	ipu_dc_exit(ipu);
 	ipu_di_exit(ipu, 1);
 	ipu_di_exit(ipu, 0);
+	ipu_vdi_exit(ipu);
 	ipu_ic_exit(ipu);
 	ipu_csi_exit(ipu, 1);
 	ipu_csi_exit(ipu, 0);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index bfb1e8a..845f64c 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -138,6 +138,7 @@ struct ipu_dc_priv;
 struct ipu_dmfc_priv;
 struct ipu_di;
 struct ipu_ic_priv;
+struct ipu_vdi;
 struct ipu_smfc_priv;
 
 struct ipu_devtype;
@@ -169,6 +170,7 @@ struct ipu_soc {
 	struct ipu_di		*di_priv[2];
 	struct ipu_csi		*csi_priv[2];
 	struct ipu_ic_priv	*ic_priv;
+	struct ipu_vdi          *vdi_priv;
 	struct ipu_smfc_priv	*smfc_priv;
 };
 
@@ -199,6 +201,10 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 		unsigned long base, unsigned long tpmem_base);
 void ipu_ic_exit(struct ipu_soc *ipu);
 
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module);
+void ipu_vdi_exit(struct ipu_soc *ipu);
+
 int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
 		unsigned long base, u32 module, struct clk *ipu_clk);
 void ipu_di_exit(struct ipu_soc *ipu, int id);
diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c
new file mode 100644
index 0000000..1303bcc
--- /dev/null
+++ b/drivers/gpu/ipu-v3/ipu-vdi.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2012 Mentor Graphics Inc.
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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.
+ */
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <uapi/linux/v4l2-mediabus.h>
+
+#include "ipu-prv.h"
+
+struct ipu_vdi {
+	void __iomem *base;
+	u32 module;
+	spinlock_t lock;
+	int use_count;
+	struct ipu_soc *ipu;
+};
+
+
+/* VDI Register Offsets */
+#define VDI_FSIZE 0x0000
+#define VDI_C     0x0004
+
+/* VDI Register Fields */
+#define VDI_C_CH_420             (0 << 1)
+#define VDI_C_CH_422             (1 << 1)
+#define VDI_C_MOT_SEL_MASK       (0x3 << 2)
+#define VDI_C_MOT_SEL_FULL       (2 << 2)
+#define VDI_C_MOT_SEL_LOW        (1 << 2)
+#define VDI_C_MOT_SEL_MED        (0 << 2)
+#define VDI_C_BURST_SIZE1_4      (3 << 4)
+#define VDI_C_BURST_SIZE2_4      (3 << 8)
+#define VDI_C_BURST_SIZE3_4      (3 << 12)
+#define VDI_C_BURST_SIZE_MASK    0xF
+#define VDI_C_BURST_SIZE1_OFFSET 4
+#define VDI_C_BURST_SIZE2_OFFSET 8
+#define VDI_C_BURST_SIZE3_OFFSET 12
+#define VDI_C_VWM1_SET_1         (0 << 16)
+#define VDI_C_VWM1_SET_2         (1 << 16)
+#define VDI_C_VWM1_CLR_2         (1 << 19)
+#define VDI_C_VWM3_SET_1         (0 << 22)
+#define VDI_C_VWM3_SET_2         (1 << 22)
+#define VDI_C_VWM3_CLR_2         (1 << 25)
+#define VDI_C_TOP_FIELD_MAN_1    (1 << 30)
+#define VDI_C_TOP_FIELD_AUTO_1   (1 << 31)
+
+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset)
+{
+	return readl(vdi->base + offset);
+}
+
+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value,
+				 unsigned int offset)
+{
+	writel(value, vdi->base + offset);
+}
+
+static void __ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	if (top_field_0)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+static void __ipu_vdi_set_motion(struct ipu_vdi *vdi,
+				 enum ipu_motion_sel motion_sel)
+{
+	u32 reg;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+
+	reg &= ~VDI_C_MOT_SEL_MASK;
+
+	switch (motion_sel) {
+	case MED_MOTION:
+		reg |= VDI_C_MOT_SEL_MED;
+		break;
+	case HIGH_MOTION:
+		reg |= VDI_C_MOT_SEL_FULL;
+		break;
+	default:
+		reg |= VDI_C_MOT_SEL_LOW;
+		break;
+	}
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+}
+
+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres,
+		   u32 field, enum ipu_motion_sel motion_sel)
+{
+	unsigned long flags;
+	u32 pixel_fmt, reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ((yres - 1) << 16) | (xres - 1);
+	ipu_vdi_write(vdi, reg, VDI_FSIZE);
+
+	/*
+	 * Full motion, only vertical filter is used.
+	 * Burst size is 4 accesses
+	 */
+	if (code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+	    code == MEDIA_BUS_FMT_UYVY8_1X16 ||
+	    code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+	    code == MEDIA_BUS_FMT_YUYV8_1X16)
+		pixel_fmt = VDI_C_CH_422;
+	else
+		pixel_fmt = VDI_C_CH_420;
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	reg |= pixel_fmt;
+	reg |= VDI_C_BURST_SIZE2_4;
+	reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2;
+	reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2;
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	if (field == V4L2_FIELD_INTERLACED_TB)
+		__ipu_vdi_set_top_field_man(vdi, false);
+	else if (field == V4L2_FIELD_INTERLACED_BT)
+		__ipu_vdi_set_top_field_man(vdi, true);
+
+	__ipu_vdi_set_motion(vdi, motion_sel);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_setup);
+
+void ipu_vdi_unsetup(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+	ipu_vdi_write(vdi, 0, VDI_FSIZE);
+	ipu_vdi_write(vdi, 0, VDI_C);
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
+
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+	u32 reg;
+	u32 mask_reg;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	reg = ipu_vdi_read(vdi, VDI_C);
+	mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
+	if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
+		reg &= ~VDI_C_TOP_FIELD_MAN_1;
+	else
+		reg |= VDI_C_TOP_FIELD_MAN_1;
+
+	ipu_vdi_write(vdi, reg, VDI_C);
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_toggle_top_field_man);
+
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi)
+{
+	ipu_set_vdi_src_mux(vdi->ipu, csi);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_set_src);
+
+int ipu_vdi_enable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	if (!vdi->use_count)
+		ipu_module_enable(vdi->ipu, vdi->module);
+
+	vdi->use_count++;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_enable);
+
+int ipu_vdi_disable(struct ipu_vdi *vdi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&vdi->lock, flags);
+
+	vdi->use_count--;
+
+	if (!vdi->use_count)
+		ipu_module_disable(vdi->ipu, vdi->module);
+
+	if (vdi->use_count < 0)
+		vdi->use_count = 0;
+
+	spin_unlock_irqrestore(&vdi->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_disable);
+
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu)
+{
+	return ipu->vdi_priv;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_get);
+
+void ipu_vdi_put(struct ipu_vdi *vdi)
+{
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_put);
+
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+		 unsigned long base, u32 module)
+{
+	struct ipu_vdi *vdi;
+
+	vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL);
+	if (!vdi)
+		return -ENOMEM;
+
+	ipu->vdi_priv = vdi;
+
+	spin_lock_init(&vdi->lock);
+	vdi->module = module;
+	vdi->base = devm_ioremap(dev, base, PAGE_SIZE);
+	if (!vdi->base)
+		return -ENOMEM;
+
+	dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base);
+	vdi->ipu = ipu;
+
+	return 0;
+}
+
+void ipu_vdi_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 3a2a794..22662a1 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -80,6 +80,16 @@ enum ipu_color_space {
 	IPUV3_COLORSPACE_UNKNOWN,
 };
 
+/*
+ * Enumeration of VDI MOTION select
+ */
+enum ipu_motion_sel {
+	MOTION_NONE = 0,
+	LOW_MOTION,
+	MED_MOTION,
+	HIGH_MOTION,
+};
+
 struct ipuv3_channel;
 
 enum ipu_channel_irq {
@@ -320,6 +330,23 @@ void ipu_ic_put(struct ipu_ic *ic);
 void ipu_ic_dump(struct ipu_ic *ic);
 
 /*
+ * IPU Video De-Interlacer (vdi) functions
+ */
+struct ipu_vdi;
+void ipu_vdi_set_top_field_man(struct ipu_vdi *vdi, bool top_field_0);
+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel);
+void ipu_vdi_setup(struct ipu_vdi *vdi,
+		   u32 code, int xres, int yres, u32 field,
+		   enum ipu_motion_sel motion_sel);
+void ipu_vdi_unsetup(struct ipu_vdi *vdi);
+void ipu_vdi_toggle_top_field_man(struct ipu_vdi *vdi);
+int ipu_vdi_set_src(struct ipu_vdi *vdi, bool csi);
+int ipu_vdi_enable(struct ipu_vdi *vdi);
+int ipu_vdi_disable(struct ipu_vdi *vdi);
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu);
+void ipu_vdi_put(struct ipu_vdi *vdi);
+
+/*
  * IPU Sensor Multiple FIFO Controller (SMFC) functions
  */
 struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno);
-- 
1.9.1


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

* [PATCH 02/28] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 01/28] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 03/28] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
                     ` (25 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_cpmem_set_uv_offset(), to set planar U/V offsets.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 7 +++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index 6494a4d..a36c35e 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
 
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
+{
+	ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
+	ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
+
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 22662a1..904fd12 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -194,6 +194,7 @@ void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres);
 void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch);
 void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
-- 
1.9.1


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

* [PATCH 03/28] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 01/28] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 02/28] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 04/28] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
                     ` (24 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_cpmem_get_burstsize().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-cpmem.c | 6 ++++++
 include/video/imx-ipu-v3.h     | 1 +
 2 files changed, 7 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index a36c35e..fcb7dc8 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -275,6 +275,12 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
 
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch)
+{
+	return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize);
+
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
 {
 	ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 904fd12..60540ead 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -197,6 +197,7 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
 void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
 void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
 void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
-- 
1.9.1


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

* [PATCH 04/28] gpu: ipu-v3: Add ipu_get_num()
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (2 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 03/28] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 05/28] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
                     ` (23 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds of-alias id to ipu_soc and retrieve with ipu_get_num().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 8 ++++++++
 drivers/gpu/ipu-v3/ipu-prv.h    | 1 +
 include/video/imx-ipu-v3.h      | 1 +
 3 files changed, 10 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 30dc115..49af121 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -45,6 +45,12 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
 	writel(value, ipu->cm_reg + offset);
 }
 
+int ipu_get_num(struct ipu_soc *ipu)
+{
+	return ipu->id;
+}
+EXPORT_SYMBOL_GPL(ipu_get_num);
+
 void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
 {
 	u32 val;
@@ -1220,6 +1226,7 @@ static int ipu_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id =
 			of_match_device(imx_ipu_dt_ids, &pdev->dev);
+	struct device_node *np = pdev->dev.of_node;
 	struct ipu_soc *ipu;
 	struct resource *res;
 	unsigned long ipu_base;
@@ -1248,6 +1255,7 @@ static int ipu_probe(struct platform_device *pdev)
 		ipu->channel[i].ipu = ipu;
 	ipu->devtype = devtype;
 	ipu->ipu_type = devtype->type;
+	ipu->id = of_alias_get_id(np, "ipu");
 
 	spin_lock_init(&ipu->lock);
 	mutex_init(&ipu->channel_lock);
diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h
index 845f64c..02057d8 100644
--- a/drivers/gpu/ipu-v3/ipu-prv.h
+++ b/drivers/gpu/ipu-v3/ipu-prv.h
@@ -153,6 +153,7 @@ struct ipu_soc {
 	void __iomem		*cm_reg;
 	void __iomem		*idmac_reg;
 
+	int			id;
 	int			usecount;
 
 	struct clk		*clk;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 60540ead..b174f8a 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -148,6 +148,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 /*
  * IPU Common functions
  */
+int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
 void ipu_dump(struct ipu_soc *ipu);
-- 
1.9.1


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

* [PATCH 05/28] gpu: ipu-v3: Add IDMA channel linking support
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (3 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 04/28] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 06/28] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
                     ` (22 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds functions to link and unlink IDMAC source channels to sink
channels.

So far the following links are supported:

IPUV3_CHANNEL_IC_PRP_ENC_MEM -> IPUV3_CHANNEL_MEM_ROT_ENC
PUV3_CHANNEL_IC_PRP_VF_MEM   -> IPUV3_CHANNEL_MEM_ROT_VF
IPUV3_CHANNEL_IC_PP_MEM      -> IPUV3_CHANNEL_MEM_ROT_PP

More links can be added to the idmac_link_info[] array.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 112 ++++++++++++++++++++++++++++++++++++++++
 include/video/imx-ipu-v3.h      |   3 ++
 2 files changed, 115 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 49af121..6d1676e 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,118 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+
+/* IDMAC Channel Linking */
+
+struct idmac_link_reg_info {
+	int chno;
+	u32 reg;
+	int shift;
+	int bits;
+	u32 sel;
+};
+
+struct idmac_link_info {
+	struct idmac_link_reg_info src;
+	struct idmac_link_reg_info sink;
+};
+
+static const struct idmac_link_info idmac_link_info[] = {
+	{
+		.src  = { 20, IPU_FS_PROC_FLOW1,  0, 4, 7 },
+		.sink = { 45, IPU_FS_PROC_FLOW2,  0, 4, 1 },
+	}, {
+		.src =  { 21, IPU_FS_PROC_FLOW1,  8, 4, 8 },
+		.sink = { 46, IPU_FS_PROC_FLOW2,  4, 4, 1 },
+	}, {
+		.src =  { 22, IPU_FS_PROC_FLOW1, 16, 4, 5 },
+		.sink = { 47, IPU_FS_PROC_FLOW2, 12, 4, 3 },
+	},
+};
+
+static const struct idmac_link_info *find_idmac_link_info(
+	struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(idmac_link_info); i++) {
+		if (src->num == idmac_link_info[i].src.chno &&
+		    sink->num == idmac_link_info[i].sink.chno)
+			return &idmac_link_info[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Links an IDMAC source channel to a sink channel.
+ */
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	src_reg |= (link->src.sel << link->src.shift);
+
+	sink_reg &= ~sink_mask;
+	sink_reg |= (link->sink.sel << link->sink.shift);
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_link);
+
+/*
+ * Unlinks IDMAC source and sink channels.
+ */
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+	struct ipu_soc *ipu = src->ipu;
+	const struct idmac_link_info *link;
+	u32 src_reg, sink_reg, src_mask, sink_mask;
+	unsigned long flags;
+
+	link = find_idmac_link_info(src, sink);
+	if (!link)
+		return -EINVAL;
+
+	src_mask = ((1 << link->src.bits) - 1) << link->src.shift;
+	sink_mask = ((1 << link->sink.bits) - 1) << link->sink.shift;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	src_reg = ipu_cm_read(ipu, link->src.reg);
+	sink_reg = ipu_cm_read(ipu, link->sink.reg);
+
+	src_reg &= ~src_mask;
+	sink_reg &= ~sink_mask;
+
+	ipu_cm_write(ipu, src_reg, link->src.reg);
+	ipu_cm_write(ipu, sink_reg, link->sink.reg);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
+
 struct ipu_devtype {
 	const char *name;
 	unsigned long cm_ofs;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index b174f8a..0a39c64 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -128,6 +128,7 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_ROT_VF_MEM		49
 #define IPUV3_CHANNEL_ROT_PP_MEM		50
 #define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA		51
+#define IPUV3_NUM_CHANNELS			64
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
@@ -171,6 +172,8 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
 bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num);
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink);
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink);
 
 /*
  * IPU Channel Parameter Memory (cpmem) functions
-- 
1.9.1


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

* [PATCH 06/28] gpu: ipu-v3: Add ipu_set_vdi_src_mux()
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (4 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 05/28] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 07/28] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
                     ` (21 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_set_vdi_src_mux() that selects the VDIC input
(from CSI or memory).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 20 ++++++++++++++++++++
 include/video/imx-ipu-v3.h      |  1 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 6d1676e..374100e 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -730,6 +730,26 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+/*
+ * Set the source for the VDIC. Selects either from CSI[01] or memory.
+ */
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi)
+{
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&ipu->lock, flags);
+
+	val = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1);
+	val &= ~(0x3 << 28);
+	if (csi)
+		val |= (0x01 << 28);
+	ipu_cm_write(ipu, val, IPU_FS_PROC_FLOW1);
+
+	spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_set_vdi_src_mux);
+
 
 /* IDMAC Channel Linking */
 
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 0a39c64..586979e 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -152,6 +152,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
+void ipu_set_vdi_src_mux(struct ipu_soc *ipu, bool csi);
 void ipu_dump(struct ipu_soc *ipu);
 
 /*
-- 
1.9.1


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

* [PATCH 07/28] gpu: ipu-v3: Add VDI input IDMAC channels
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (5 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 06/28] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 08/28] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
                     ` (20 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds the VDIC field input IDMAC channels. These channels
transfer fields F(n-1), F(n), and F(N+1) from memory to
the VDIC (channels 8, 9, 10 respectively).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 include/video/imx-ipu-v3.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 586979e..2302fc5 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -107,6 +107,9 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_CSI2			 2
 #define IPUV3_CHANNEL_CSI3			 3
 #define IPUV3_CHANNEL_VDI_MEM_IC_VF		 5
+#define IPUV3_CHANNEL_MEM_VDI_P			 8
+#define IPUV3_CHANNEL_MEM_VDI			 9
+#define IPUV3_CHANNEL_MEM_VDI_N			10
 #define IPUV3_CHANNEL_MEM_IC_PP			11
 #define IPUV3_CHANNEL_MEM_IC_PRP_VF		12
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF		14
-- 
1.9.1


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

* [PATCH 08/28] gpu: ipu-v3: Add ipu_csi_set_src()
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (6 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 07/28] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 09/28] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
                     ` (19 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_csi_set_src() which is just a wrapper around
ipu_set_csi_src_mux().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 8 ++++++++
 include/video/imx-ipu-v3.h   | 1 +
 2 files changed, 9 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 06631ac..336dc06 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -609,6 +609,14 @@ int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
 }
 EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc);
 
+int ipu_csi_set_src(struct ipu_csi *csi, u32 vc, bool select_mipi_csi2)
+{
+	ipu_set_csi_src_mux(csi->ipu, csi->id, select_mipi_csi2);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_csi_set_src);
+
 int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest)
 {
 	unsigned long flags;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 2302fc5..57b487d 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -301,6 +301,7 @@ int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc,
 			      struct v4l2_mbus_framefmt *mbus_fmt);
 int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip,
 			  u32 max_ratio, u32 id);
+int ipu_csi_set_src(struct ipu_csi *csi, u32 vc, bool select_mipi_csi2);
 int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest);
 int ipu_csi_enable(struct ipu_csi *csi);
 int ipu_csi_disable(struct ipu_csi *csi);
-- 
1.9.1


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

* [PATCH 09/28] gpu: ipu-v3: Add ipu_ic_set_src()
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (7 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 08/28] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 10/28] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
                     ` (18 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds ipu_ic_set_src() which is just aa wrapper around
ipu_set_ic_src_mux().

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 10 ++++++++++
 include/video/imx-ipu-v3.h  |  1 +
 2 files changed, 11 insertions(+)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 1dcb96c..f306a9c 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -629,6 +629,16 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	ipu_set_ic_src_mux(priv->ipu, csi_id, vdi);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_ic_set_src);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 57b487d..8f77ddb 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -334,6 +334,7 @@ void ipu_ic_task_disable(struct ipu_ic *ic);
 int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
+int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1


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

* [PATCH 10/28] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (8 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 09/28] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 11/28] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
                     ` (17 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Set the sensor full frame based on whether the passed in mbus_fmt
is 720x480 (NTSC) or 720x576 (PAL).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 336dc06..07c7091 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -365,10 +365,14 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 {
 	struct ipu_csi_bus_config cfg;
 	unsigned long flags;
-	u32 data = 0;
+	u32 width, height, data = 0;
 
 	fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
 
+	/* set default sensor frame width and height */
+	width = mbus_fmt->width;
+	height = mbus_fmt->height;
+
 	/* Set the CSI_SENS_CONF register remaining fields */
 	data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
 		cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
@@ -386,11 +390,6 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 	ipu_csi_write(csi, data, CSI_SENS_CONF);
 
-	/* Setup sensor frame size */
-	ipu_csi_write(csi,
-		      (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
-		      CSI_SENS_FRM_SIZE);
-
 	/* Set CCIR registers */
 
 	switch (cfg.clk_mode) {
@@ -408,11 +407,12 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
 			 * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
 			 */
+			height = 625; /* framelines for PAL */
+
 			ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
-
 		} else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
 			/*
 			 * NTSC case
@@ -422,6 +422,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 			 * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
 			 * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
 			 */
+			height = 525; /* framelines for NTSC */
+
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
 			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
@@ -447,6 +449,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 		break;
 	}
 
+	/* Setup sensor frame size */
+	ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
+		      CSI_SENS_FRM_SIZE);
+
 	dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
 		ipu_csi_read(csi, CSI_SENS_CONF));
 	dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
-- 
1.9.1


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

* [PATCH 11/28] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (9 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 10/28] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 12/28] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
                     ` (16 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

The CSI data format was being programmed incorrectly for the
1x16 media bus formats. The CSI data format for 16-bit must
be bayer/generic (CSI_SENS_CONF_DATA_FMT_BAYER).

Suggested-by: Carsten Resch <Carsten.Resch@de.bosch.com>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 07c7091..0eac28c 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
 		cfg->data_width = IPU_CSI_DATA_WIDTH_8;
 		break;
 	case MEDIA_BUS_FMT_UYVY8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
-		cfg->mipi_dt = MIPI_DT_YUV422;
-		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
-		break;
 	case MEDIA_BUS_FMT_YUYV8_1X16:
-		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+		cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
 		cfg->mipi_dt = MIPI_DT_YUV422;
 		cfg->data_width = IPU_CSI_DATA_WIDTH_16;
 		break;
-- 
1.9.1


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

* [PATCH 12/28] gpu: ipu-v3: Fix CSI0 blur in NTSC format
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (10 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 11/28] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 13/28] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
                     ` (15 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Suresh Dhandapani

From: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>

This patch will change the register IPU_CSI0_CCIR_CODE_2 value from
0x40596 to 0x405A6. The change is related to the Start of field 1
first blanking line command bit[5-3] for NTSC format only. This
change is dependent with ADV chip where the NEWAVMODE is set to 0
in register 0x31. Setting NEWAVMODE to "0" in ADV means "EAV/SAV
codes generated to suit analog devices encoders".

Signed-off-by: Suresh Dhandapani <Suresh.Dhandapani@in.bosch.com>
---
 drivers/gpu/ipu-v3/ipu-csi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/ipu-v3/ipu-csi.c b/drivers/gpu/ipu-v3/ipu-csi.c
index 0eac28c..ec81958 100644
--- a/drivers/gpu/ipu-v3/ipu-csi.c
+++ b/drivers/gpu/ipu-v3/ipu-csi.c
@@ -422,7 +422,7 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
 			ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
 					  CSI_CCIR_CODE_1);
-			ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
+			ipu_csi_write(csi, 0x405A6, CSI_CCIR_CODE_2);
 			ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
 		} else {
 			dev_err(csi->ipu->dev,
-- 
1.9.1


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

* [PATCH 13/28] gpu: ipu-v3: Fix IRT usage
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (11 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 12/28] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
                     ` (14 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

There can be multiple IC tasks using the IRT, so the IRT needs
a separate use counter. Create a private ipu_irt_enable() to
enable the IRT module when any IC task requires rotation, and
ipu_irt_disable() when a task no longer needs the IRT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 43 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index f306a9c..5329bfe 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -160,6 +160,7 @@ struct ipu_ic_priv {
 	spinlock_t lock;
 	struct ipu_soc *ipu;
 	int use_count;
+	int irt_use_count;
 	struct ipu_ic task[IC_NUM_TASKS];
 };
 
@@ -379,8 +380,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic)
 
 	ipu_ic_write(ic, ic_conf, IC_CONF);
 
-	ic->rotation = ic->graphics = false;
-
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
@@ -639,22 +638,44 @@ int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_ic_set_src);
 
+static void ipu_irt_enable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	if (!priv->irt_use_count)
+		ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN);
+
+	priv->irt_use_count++;
+}
+
+static void ipu_irt_disable(struct ipu_ic *ic)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+
+	priv->irt_use_count--;
+
+	if (!priv->irt_use_count)
+		ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN);
+
+	if (priv->irt_use_count < 0)
+		priv->irt_use_count = 0;
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
-	if (ic->rotation)
-		module |= IPU_CONF_ROT_EN;
-
 	if (!priv->use_count)
-		ipu_module_enable(priv->ipu, module);
+		ipu_module_enable(priv->ipu, IPU_CONF_IC_EN);
 
 	priv->use_count++;
 
+	if (ic->rotation)
+		ipu_irt_enable(ic);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -665,18 +686,22 @@ int ipu_ic_disable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
 	unsigned long flags;
-	u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
 
 	spin_lock_irqsave(&priv->lock, flags);
 
 	priv->use_count--;
 
 	if (!priv->use_count)
-		ipu_module_disable(priv->ipu, module);
+		ipu_module_disable(priv->ipu, IPU_CONF_IC_EN);
 
 	if (priv->use_count < 0)
 		priv->use_count = 0;
 
+	if (ic->rotation)
+		ipu_irt_disable(ic);
+
+	ic->rotation = ic->graphics = false;
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
-- 
1.9.1


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

* [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (12 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 13/28] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-13 18:58     ` Mauro Carvalho Chehab
  2016-07-06 23:06   ` [PATCH 15/28] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
                     ` (13 subsequent siblings)
  27 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This patch implements complete image conversion support to ipu-ic,
with tiling to support scaling to and from images up to 4096x4096.
Image rotation is also supported.

The internal API is subsystem agnostic (no V4L2 dependency except
for the use of V4L2 fourcc pixel formats).

Callers prepare for image conversion by calling
ipu_image_convert_prepare(), which initializes the parameters of
the conversion. The caller passes in the ipu_ic task to use for
the conversion, the input and output image formats, a rotation mode,
and a completion callback and completion context pointer:

struct image_converter_ctx *
ipu_image_convert_prepare(struct ipu_ic *ic,
                          struct ipu_image *in, struct ipu_image *out,
                          enum ipu_rotate_mode rot_mode,
                          image_converter_cb_t complete,
                          void *complete_context);

The caller is given a new conversion context that must be passed to
the further APIs:

struct image_converter_run *
ipu_image_convert_run(struct image_converter_ctx *ctx,
                      dma_addr_t in_phys, dma_addr_t out_phys);

This queues a new image conversion request to a run queue, and
starts the conversion immediately if the run queue is empty. Only
the physaddr's of the input and output image buffers are needed,
since the conversion context was created previously with
ipu_image_convert_prepare(). Returns a new run object pointer. When
the conversion completes, the run pointer is returned to the
completion callback.

void image_convert_abort(struct image_converter_ctx *ctx);

This will abort any active or pending conversions for this context.
Any currently active or pending runs belonging to this context are
returned via the completion callback with an error status.

void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);

Unprepares the conversion context. Any active or pending runs will
be aborted by calling image_convert_abort().
---
 drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
 include/video/imx-ipu-v3.h  |   57 +-
 2 files changed, 1736 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index 5329bfe..f6a1125 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -17,6 +17,8 @@
 #include <linux/bitrev.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
 #include "ipu-prv.h"
 
 /* IC Register Offsets */
@@ -82,6 +84,40 @@
 #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
 #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
 
+/*
+ * The IC Resizer has a restriction that the output frame from the
+ * resizer must be 1024 or less in both width (pixels) and height
+ * (lines).
+ *
+ * The image conversion support attempts to split up a conversion when
+ * the desired output (converted) frame resolution exceeds the IC resizer
+ * limit of 1024 in either dimension.
+ *
+ * If either dimension of the output frame exceeds the limit, the
+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum
+ * of 4*4 or 16 tiles. A conversion is then carried out for each
+ * tile (but taking care to pass the full frame stride length to
+ * the DMA channel's parameter memory!). IDMA double-buffering is used
+ * to convert each tile back-to-back when possible (see note below
+ * when double_buffering boolean is set).
+ *
+ * Note that the input frame must be split up into the same number
+ * of tiles as the output frame.
+ */
+#define MAX_STRIPES_W    4
+#define MAX_STRIPES_H    4
+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
+
+#define MIN_W     128
+#define MIN_H     128
+#define MAX_W     4096
+#define MAX_H     4096
+
+enum image_convert_type {
+	IMAGE_CONVERT_IN = 0,
+	IMAGE_CONVERT_OUT,
+};
+
 struct ic_task_regoffs {
 	u32 rsc;
 	u32 tpmem_csc[2];
@@ -96,6 +132,16 @@ struct ic_task_bitfields {
 	u32 ic_cmb_galpha_bit;
 };
 
+struct ic_task_channels {
+	int in;
+	int out;
+	int rot_in;
+	int rot_out;
+	int vdi_in_p;
+	int vdi_in;
+	int vdi_in_n;
+};
+
 static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
 	[IC_TASK_ENCODER] = {
 		.rsc = IC_PRP_ENC_RSC,
@@ -138,12 +184,159 @@ static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
 	},
 };
 
+static const struct ic_task_channels ic_task_ch[IC_NUM_TASKS] = {
+	[IC_TASK_ENCODER] = {
+		.out = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_ENC,
+		.rot_out = IPUV3_CHANNEL_ROT_ENC_MEM,
+	},
+	[IC_TASK_VIEWFINDER] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+		.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_P,
+		.vdi_in = IPUV3_CHANNEL_MEM_VDI,
+		.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_N,
+	},
+	[IC_TASK_POST_PROCESSOR] = {
+		.in = IPUV3_CHANNEL_MEM_IC_PP,
+		.out = IPUV3_CHANNEL_IC_PP_MEM,
+		.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
+		.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
+	},
+};
+
+struct ipu_ic_dma_buf {
+	void          *virt;
+	dma_addr_t    phys;
+	unsigned long len;
+};
+
+/* dimensions of one tile */
+struct ipu_ic_tile {
+	unsigned int width;
+	unsigned int height;
+	/* size and strides are in bytes */
+	unsigned int size;
+	unsigned int stride;
+	unsigned int rot_stride;
+};
+
+struct ipu_ic_tile_off {
+	/* start Y or packed offset of this tile */
+	u32     offset;
+	/* offset from start to tile in U plane, for planar formats */
+	u32     u_off;
+	/* offset from start to tile in V plane, for planar formats */
+	u32     v_off;
+};
+
+struct ipu_ic_pixfmt {
+	char	*name;
+	u32	fourcc;        /* V4L2 fourcc */
+	int     bpp;           /* total bpp */
+	int     y_depth;       /* depth of Y plane for planar formats */
+	int     uv_width_dec;  /* decimation in width for U/V planes */
+	int     uv_height_dec; /* decimation in height for U/V planes */
+	bool    uv_swapped;    /* U and V planes are swapped */
+	bool    uv_packed;     /* partial planar (U and V in same plane) */
+};
+
+struct ipu_ic_image {
+	struct ipu_image base;
+	enum image_convert_type type;
+
+	const struct ipu_ic_pixfmt *fmt;
+	unsigned int stride;
+
+	/* # of rows (horizontal stripes) if dest height is > 1024 */
+	unsigned int num_rows;
+	/* # of columns (vertical stripes) if dest width is > 1024 */
+	unsigned int num_cols;
+
+	struct ipu_ic_tile tile;
+	struct ipu_ic_tile_off tile_off[MAX_TILES];
+};
+
+struct image_converter_ctx;
+struct image_converter;
 struct ipu_ic_priv;
+struct ipu_ic;
+
+struct image_converter_run {
+	struct image_converter_ctx *ctx;
+
+	dma_addr_t in_phys;
+	dma_addr_t out_phys;
+
+	int status;
+
+	struct list_head list;
+};
+
+struct image_converter_ctx {
+	struct image_converter *cvt;
+
+	image_converter_cb_t complete;
+	void *complete_context;
+
+	/* Source/destination image data and rotation mode */
+	struct ipu_ic_image in;
+	struct ipu_ic_image out;
+	enum ipu_rotate_mode rot_mode;
+
+	/* intermediate buffer for rotation */
+	struct ipu_ic_dma_buf rot_intermediate[2];
+
+	/* current buffer number for double buffering */
+	int cur_buf_num;
+
+	bool aborting;
+	struct completion aborted;
+
+	/* can we use double-buffering for this conversion operation? */
+	bool double_buffering;
+	/* num_rows * num_cols */
+	unsigned int num_tiles;
+	/* next tile to process */
+	unsigned int next_tile;
+	/* where to place converted tile in dest image */
+	unsigned int out_tile_map[MAX_TILES];
+
+	struct list_head list;
+};
+
+struct image_converter {
+	struct ipu_ic *ic;
+
+	struct ipuv3_channel *in_chan;
+	struct ipuv3_channel *out_chan;
+	struct ipuv3_channel *rotation_in_chan;
+	struct ipuv3_channel *rotation_out_chan;
+
+	/* the IPU end-of-frame irqs */
+	int out_eof_irq;
+	int rot_out_eof_irq;
+
+	spinlock_t irqlock;
+
+	/* list of convert contexts */
+	struct list_head ctx_list;
+	/* queue of conversion runs */
+	struct list_head pending_q;
+	/* queue of completed runs */
+	struct list_head done_q;
+
+	/* the current conversion run */
+	struct image_converter_run *current_run;
+};
 
 struct ipu_ic {
 	enum ipu_ic_task task;
 	const struct ic_task_regoffs *reg;
 	const struct ic_task_bitfields *bit;
+	const struct ic_task_channels *ch;
 
 	enum ipu_color_space in_cs, g_in_cs;
 	enum ipu_color_space out_cs;
@@ -151,6 +344,8 @@ struct ipu_ic {
 	bool rotation;
 	bool in_use;
 
+	struct image_converter cvt;
+
 	struct ipu_ic_priv *priv;
 };
 
@@ -619,7 +814,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
 	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
 
-	if (rot >= IPU_ROTATE_90_RIGHT)
+	if (ipu_rot_mode_is_irt(rot))
 		ic->rotation = true;
 
 unlock:
@@ -661,6 +856,1480 @@ static void ipu_irt_disable(struct ipu_ic *ic)
 		priv->irt_use_count = 0;
 }
 
+/*
+ * Complete image conversion support follows
+ */
+
+static const struct ipu_ic_pixfmt ipu_ic_formats[] = {
+	{
+		.name	= "RGB565",
+		.fourcc	= V4L2_PIX_FMT_RGB565,
+		.bpp    = 16,
+	}, {
+		.name	= "RGB24",
+		.fourcc	= V4L2_PIX_FMT_RGB24,
+		.bpp    = 24,
+	}, {
+		.name	= "BGR24",
+		.fourcc	= V4L2_PIX_FMT_BGR24,
+		.bpp    = 24,
+	}, {
+		.name	= "RGB32",
+		.fourcc	= V4L2_PIX_FMT_RGB32,
+		.bpp    = 32,
+	}, {
+		.name	= "BGR32",
+		.fourcc	= V4L2_PIX_FMT_BGR32,
+		.bpp    = 32,
+	}, {
+		.name	= "4:2:2 packed, YUYV",
+		.fourcc	= V4L2_PIX_FMT_YUYV,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:2 packed, UYVY",
+		.fourcc	= V4L2_PIX_FMT_UYVY,
+		.bpp    = 16,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name	= "4:2:0 planar, YUV",
+		.fourcc	= V4L2_PIX_FMT_YUV420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+	}, {
+		.name	= "4:2:0 planar, YVU",
+		.fourcc	= V4L2_PIX_FMT_YVU420,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_swapped = true,
+	}, {
+		.name   = "4:2:0 partial planar, NV12",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.bpp    = 12,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 2,
+		.uv_packed = true,
+	}, {
+		.name   = "4:2:2 planar, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+	}, {
+		.name   = "4:2:2 partial planar, NV16",
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.bpp    = 16,
+		.y_depth = 8,
+		.uv_width_dec = 2,
+		.uv_height_dec = 1,
+		.uv_packed = true,
+	},
+};
+
+static const struct ipu_ic_pixfmt *ipu_ic_get_format(u32 fourcc)
+{
+	const struct ipu_ic_pixfmt *ret = NULL;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ipu_ic_formats); i++) {
+		if (ipu_ic_formats[i].fourcc == fourcc) {
+			ret = &ipu_ic_formats[i];
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void ipu_ic_dump_format(struct image_converter_ctx *ctx,
+			       struct ipu_ic_image *ic_image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev,
+		"ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
+		ctx,
+		ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
+		ic_image->base.pix.width, ic_image->base.pix.height,
+		ic_image->num_cols, ic_image->num_rows,
+		ic_image->tile.width, ic_image->tile.height,
+		ic_image->fmt->fourcc & 0xff,
+		(ic_image->fmt->fourcc >> 8) & 0xff,
+		(ic_image->fmt->fourcc >> 16) & 0xff,
+		(ic_image->fmt->fourcc >> 24) & 0xff);
+}
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc)
+{
+	const struct ipu_ic_pixfmt *fmt;
+
+	if (index >= (int)ARRAY_SIZE(ipu_ic_formats))
+		return -EINVAL;
+
+	/* Format found */
+	fmt = &ipu_ic_formats[index];
+	*desc = fmt->name;
+	*fourcc = fmt->fourcc;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
+
+static void ipu_ic_free_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf)
+{
+	if (buf->virt)
+		dma_free_coherent(priv->ipu->dev,
+				  buf->len, buf->virt, buf->phys);
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int ipu_ic_alloc_dma_buf(struct ipu_ic_priv *priv,
+				struct ipu_ic_dma_buf *buf,
+				int size)
+{
+	unsigned long newlen = PAGE_ALIGN(size);
+
+	if (buf->virt) {
+		if (buf->len == newlen)
+			return 0;
+		ipu_ic_free_dma_buf(priv, buf);
+	}
+
+	buf->len = newlen;
+	buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static inline int ipu_ic_num_stripes(int dim)
+{
+	if (dim <= 1024)
+		return 1;
+	else if (dim <= 2048)
+		return 2;
+	else
+		return 4;
+}
+
+static void ipu_ic_calc_tile_dimensions(struct image_converter_ctx *ctx,
+					struct ipu_ic_image *image)
+{
+	struct ipu_ic_tile *tile = &image->tile;
+
+	tile->height = image->base.pix.height / image->num_rows;
+	tile->width = image->base.pix.width / image->num_cols;
+	tile->size = ((tile->height * image->fmt->bpp) >> 3) * tile->width;
+
+	if (image->fmt->y_depth) {
+		tile->stride = (image->fmt->y_depth * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->y_depth * tile->height) >> 3;
+	} else {
+		tile->stride = (image->fmt->bpp * tile->width) >> 3;
+		tile->rot_stride = (image->fmt->bpp * tile->height) >> 3;
+	}
+}
+
+/*
+ * Use the rotation transformation to find the tile coordinates
+ * (row, col) of a tile in the destination frame that corresponds
+ * to the given tile coordinates of a source frame. The destination
+ * coordinate is then converted to a tile index.
+ */
+static int ipu_ic_transform_tile_index(struct image_converter_ctx *ctx,
+				       int src_row, int src_col)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	int cos, sin, dst_row, dst_col;
+
+	/* with no rotation it's a 1:1 mapping */
+	if (ctx->rot_mode == IPU_ROTATE_NONE)
+		return src_row * s_image->num_cols + src_col;
+
+	if (ctx->rot_mode & IPU_ROT_BIT_90) {
+		cos = 0;
+		sin = 1;
+	} else {
+		cos = 1;
+		sin = 0;
+	}
+
+	/*
+	 * before doing the transform, first we have to translate
+	 * source row,col for an origin in the center of s_image
+	 */
+	src_row *= 2;
+	src_col *= 2;
+	src_row -= s_image->num_rows - 1;
+	src_col -= s_image->num_cols - 1;
+
+	/* do the rotation transform */
+	dst_col = src_col * cos - src_row * sin;
+	dst_row = src_col * sin + src_row * cos;
+
+	/* apply flip */
+	if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
+		dst_col = -dst_col;
+	if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
+		dst_row = -dst_row;
+
+	dev_dbg(priv->ipu->dev, "ctx %p: [%d,%d] --> [%d,%d]\n",
+		ctx, src_col, src_row, dst_col, dst_row);
+
+	/*
+	 * finally translate dest row,col using an origin in upper
+	 * left of d_image
+	 */
+	dst_row += d_image->num_rows - 1;
+	dst_col += d_image->num_cols - 1;
+	dst_row /= 2;
+	dst_col /= 2;
+
+	return dst_row * d_image->num_cols + dst_col;
+}
+
+/*
+ * Fill the out_tile_map[] with transformed destination tile indeces.
+ */
+static void ipu_ic_calc_out_tile_map(struct image_converter_ctx *ctx)
+{
+	struct ipu_ic_image *s_image = &ctx->in;
+	unsigned int row, col, tile = 0;
+
+	for (row = 0; row < s_image->num_rows; row++) {
+		for (col = 0; col < s_image->num_cols; col++) {
+			ctx->out_tile_map[tile] =
+				ipu_ic_transform_tile_index(ctx, row, col);
+			tile++;
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_planar(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 H, w, h, y_depth, y_stride, uv_stride;
+	u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
+	u32 y_row_off, y_col_off, y_off;
+	u32 y_size, uv_size;
+
+	/* setup some convenience vars */
+	H = image->base.pix.height;
+	w = image->tile.width;
+	h = image->tile.height;
+
+	y_depth = fmt->y_depth;
+	y_stride = image->stride;
+	uv_stride = y_stride / fmt->uv_width_dec;
+	if (fmt->uv_packed)
+		uv_stride *= 2;
+
+	y_size = H * y_stride;
+	uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
+
+	for (row = 0; row < image->num_rows; row++) {
+		y_row_off = row * h * y_stride;
+		uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
+
+		for (col = 0; col < image->num_cols; col++) {
+			y_col_off = (col * w * y_depth) >> 3;
+			uv_col_off = y_col_off / fmt->uv_width_dec;
+			if (fmt->uv_packed)
+				uv_col_off *= 2;
+
+			y_off = y_row_off + y_col_off;
+			uv_off = uv_row_off + uv_col_off;
+
+			u_off = y_size - y_off + uv_off;
+			v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
+			if (fmt->uv_swapped) {
+				tmp = u_off;
+				u_off = v_off;
+				v_off = tmp;
+			}
+
+			image->tile_off[tile].offset = y_off;
+			image->tile_off[tile].u_off = u_off;
+			image->tile_off[tile++].v_off = v_off;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
+				ctx, image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				y_off, u_off, v_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets_packed(struct image_converter_ctx *ctx,
+					    struct ipu_ic_image *image)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+	const struct ipu_ic_pixfmt *fmt = image->fmt;
+	unsigned int row, col, tile = 0;
+	u32 w, h, bpp, stride;
+	u32 row_off, col_off;
+
+	/* setup some convenience vars */
+	w = image->tile.width;
+	h = image->tile.height;
+	stride = image->stride;
+	bpp = fmt->bpp;
+
+	for (row = 0; row < image->num_rows; row++) {
+		row_off = row * h * stride;
+
+		for (col = 0; col < image->num_cols; col++) {
+			col_off = (col * w * bpp) >> 3;
+
+			image->tile_off[tile].offset = row_off + col_off;
+			image->tile_off[tile].u_off = 0;
+			image->tile_off[tile++].v_off = 0;
+
+			dev_dbg(priv->ipu->dev,
+				"ctx %p: %s@[%d,%d]: phys %08x\n", ctx,
+				image->type == IMAGE_CONVERT_IN ?
+				"Input" : "Output", row, col,
+				row_off + col_off);
+		}
+	}
+}
+
+static void ipu_ic_calc_tile_offsets(struct image_converter_ctx *ctx,
+				     struct ipu_ic_image *image)
+{
+	memset(image->tile_off, 0, sizeof(image->tile_off));
+
+	if (image->fmt->y_depth)
+		ipu_ic_calc_tile_offsets_planar(ctx, image);
+	else
+		ipu_ic_calc_tile_offsets_packed(ctx, image);
+}
+
+/*
+ * return the number of runs in given queue (pending_q or done_q)
+ * for this context. hold irqlock when calling.
+ */
+static int ipu_ic_get_run_count(struct image_converter_ctx *ctx,
+				struct list_head *q)
+{
+	struct image_converter_run *run;
+	int count = 0;
+
+	list_for_each_entry(run, q, list) {
+		if (run->ctx == ctx)
+			count++;
+	}
+
+	return count;
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_convert_stop(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+
+	dev_dbg(priv->ipu->dev, "%s: stopping ctx %p run %p\n",
+		__func__, ctx, run);
+
+	/* disable IC tasks and the channels */
+	ipu_ic_task_disable(cvt->ic);
+	ipu_idmac_disable_channel(cvt->in_chan);
+	ipu_idmac_disable_channel(cvt->out_chan);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_disable_channel(cvt->rotation_in_chan);
+		ipu_idmac_disable_channel(cvt->rotation_out_chan);
+		ipu_idmac_unlink(cvt->out_chan, cvt->rotation_in_chan);
+	}
+
+	ipu_ic_disable(cvt->ic);
+}
+
+/* hold irqlock when calling */
+static void init_idmac_channel(struct image_converter_ctx *ctx,
+			       struct ipuv3_channel *channel,
+			       struct ipu_ic_image *image,
+			       enum ipu_rotate_mode rot_mode,
+			       bool rot_swap_width_height)
+{
+	struct image_converter *cvt = ctx->cvt;
+	unsigned int burst_size;
+	u32 width, height, stride;
+	dma_addr_t addr0, addr1 = 0;
+	struct ipu_image tile_image;
+	unsigned int tile_idx[2];
+
+	if (image->type == IMAGE_CONVERT_OUT) {
+		tile_idx[0] = ctx->out_tile_map[0];
+		tile_idx[1] = ctx->out_tile_map[1];
+	} else {
+		tile_idx[0] = 0;
+		tile_idx[1] = 1;
+	}
+
+	if (rot_swap_width_height) {
+		width = image->tile.height;
+		height = image->tile.width;
+		stride = image->tile.rot_stride;
+		addr0 = ctx->rot_intermediate[0].phys;
+		if (ctx->double_buffering)
+			addr1 = ctx->rot_intermediate[1].phys;
+	} else {
+		width = image->tile.width;
+		height = image->tile.height;
+		stride = image->stride;
+		addr0 = image->base.phys0 +
+			image->tile_off[tile_idx[0]].offset;
+		if (ctx->double_buffering)
+			addr1 = image->base.phys0 +
+				image->tile_off[tile_idx[1]].offset;
+	}
+
+	ipu_cpmem_zero(channel);
+
+	memset(&tile_image, 0, sizeof(tile_image));
+	tile_image.pix.width = tile_image.rect.width = width;
+	tile_image.pix.height = tile_image.rect.height = height;
+	tile_image.pix.bytesperline = stride;
+	tile_image.pix.pixelformat =  image->fmt->fourcc;
+	tile_image.phys0 = addr0;
+	tile_image.phys1 = addr1;
+	ipu_cpmem_set_image(channel, &tile_image);
+
+	if (image->fmt->y_depth && !rot_swap_width_height)
+		ipu_cpmem_set_uv_offset(channel,
+					image->tile_off[tile_idx[0]].u_off,
+					image->tile_off[tile_idx[0]].v_off);
+
+	if (rot_mode)
+		ipu_cpmem_set_rotation(channel, rot_mode);
+
+	if (channel == cvt->rotation_in_chan ||
+	    channel == cvt->rotation_out_chan) {
+		burst_size = 8;
+		ipu_cpmem_set_block_mode(channel);
+	} else
+		burst_size = (width % 16) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	ipu_ic_task_idma_init(cvt->ic, channel, width, height,
+			      burst_size, rot_mode);
+
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_convert_start(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	enum ipu_color_space src_cs, dest_cs;
+	unsigned int dest_width, dest_height;
+	int ret;
+
+	dev_dbg(priv->ipu->dev, "%s: starting ctx %p run %p\n",
+		__func__, ctx, run);
+
+	src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
+	dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* swap width/height for resizer */
+		dest_width = d_image->tile.height;
+		dest_height = d_image->tile.width;
+	} else {
+		dest_width = d_image->tile.width;
+		dest_height = d_image->tile.height;
+	}
+
+	/* setup the IC resizer and CSC */
+	ret = ipu_ic_task_init(cvt->ic,
+			       s_image->tile.width,
+			       s_image->tile.height,
+			       dest_width,
+			       dest_height,
+			       src_cs, dest_cs);
+	if (ret) {
+		dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
+		return ret;
+	}
+
+	/* init the source MEM-->IC PP IDMAC channel */
+	init_idmac_channel(ctx, cvt->in_chan, s_image,
+			   IPU_ROTATE_NONE, false);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* init the IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   IPU_ROTATE_NONE, true);
+
+		/* init the MEM-->IC PP ROT IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_in_chan, d_image,
+				   ctx->rot_mode, true);
+
+		/* init the destination IC PP ROT-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->rotation_out_chan, d_image,
+				   IPU_ROTATE_NONE, false);
+
+		/* now link IC PP-->MEM to MEM-->IC PP ROT */
+		ipu_idmac_link(cvt->out_chan, cvt->rotation_in_chan);
+	} else {
+		/* init the destination IC PP-->MEM IDMAC channel */
+		init_idmac_channel(ctx, cvt->out_chan, d_image,
+				   ctx->rot_mode, false);
+	}
+
+	/* enable the IC */
+	ipu_ic_enable(cvt->ic);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(cvt->in_chan, 0);
+	ipu_idmac_select_buffer(cvt->out_chan, 0);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode))
+		ipu_idmac_select_buffer(cvt->rotation_out_chan, 0);
+	if (ctx->double_buffering) {
+		ipu_idmac_select_buffer(cvt->in_chan, 1);
+		ipu_idmac_select_buffer(cvt->out_chan, 1);
+		if (ipu_rot_mode_is_irt(ctx->rot_mode))
+			ipu_idmac_select_buffer(cvt->rotation_out_chan, 1);
+	}
+
+	/* enable the channels! */
+	ipu_idmac_enable_channel(cvt->in_chan);
+	ipu_idmac_enable_channel(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_idmac_enable_channel(cvt->rotation_in_chan);
+		ipu_idmac_enable_channel(cvt->rotation_out_chan);
+	}
+
+	ipu_ic_task_enable(cvt->ic);
+
+	ipu_cpmem_dump(cvt->in_chan);
+	ipu_cpmem_dump(cvt->out_chan);
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ipu_cpmem_dump(cvt->rotation_in_chan);
+		ipu_cpmem_dump(cvt->rotation_out_chan);
+	}
+
+	ipu_dump(priv->ipu);
+
+	return 0;
+}
+
+/* hold irqlock when calling */
+static int ipu_ic_run(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+
+	ctx->in.base.phys0 = run->in_phys;
+	ctx->out.base.phys0 = run->out_phys;
+
+	ctx->cur_buf_num = 0;
+	ctx->next_tile = 1;
+
+	/* remove run from pending_q and set as current */
+	list_del(&run->list);
+	cvt->current_run = run;
+
+	return ipu_ic_convert_start(run);
+}
+
+/* hold irqlock when calling */
+static void ipu_ic_run_next(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *tmp;
+	int ret;
+
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		/* skip contexts that are aborting */
+		if (run->ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: skipping aborting ctx %p run %p\n",
+				 __func__, run->ctx, run);
+			continue;
+		}
+
+		ret = ipu_ic_run(run);
+		if (!ret)
+			break;
+
+		/*
+		 * something went wrong with start, add the run
+		 * to done q and continue to the next run in the
+		 * pending q.
+		 */
+		run->status = ret;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+	}
+}
+
+static void ipu_ic_empty_done_q(struct image_converter *cvt)
+{
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	while (!list_empty(&cvt->done_q)) {
+		run = list_entry(cvt->done_q.next,
+				 struct image_converter_run,
+				 list);
+
+		list_del(&run->list);
+
+		dev_dbg(priv->ipu->dev,
+			"%s: completing ctx %p run %p with %d\n",
+			__func__, run->ctx, run, run->status);
+
+		/* call the completion callback and free the run */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		run->ctx->complete(run->ctx->complete_context, run,
+				   run->status);
+		kfree(run);
+		spin_lock_irqsave(&cvt->irqlock, flags);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+}
+
+/*
+ * the bottom half thread clears out the done_q, calling the
+ * completion handler for each.
+ */
+static irqreturn_t ipu_ic_bh(int irq, void *dev_id)
+{
+	struct image_converter *cvt = dev_id;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+
+	dev_dbg(priv->ipu->dev, "%s: enter\n", __func__);
+
+	ipu_ic_empty_done_q(cvt);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/*
+	 * the done_q is cleared out, signal any contexts
+	 * that are aborting that abort can complete.
+	 */
+	list_for_each_entry(ctx, &cvt->ctx_list, list) {
+		if (ctx->aborting) {
+			dev_dbg(priv->ipu->dev,
+				 "%s: signaling abort for ctx %p\n",
+				 __func__, ctx);
+			complete(&ctx->aborted);
+		}
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	dev_dbg(priv->ipu->dev, "%s: exit\n", __func__);
+	return IRQ_HANDLED;
+}
+
+/* hold irqlock when calling */
+static irqreturn_t ipu_ic_doirq(struct image_converter_run *run)
+{
+	struct image_converter_ctx *ctx = run->ctx;
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_tile_off *src_off, *dst_off;
+	struct ipu_ic_image *s_image = &ctx->in;
+	struct ipu_ic_image *d_image = &ctx->out;
+	struct ipuv3_channel *outch;
+	unsigned int dst_idx;
+
+	outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
+		cvt->rotation_out_chan : cvt->out_chan;
+
+	/*
+	 * It is difficult to stop the channel DMA before the channels
+	 * enter the paused state. Without double-buffering the channels
+	 * are always in a paused state when the EOF irq occurs, so it
+	 * is safe to stop the channels now. For double-buffering we
+	 * just ignore the abort until the operation completes, when it
+	 * is safe to shut down.
+	 */
+	if (ctx->aborting && !ctx->double_buffering) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		goto done;
+	}
+
+	if (ctx->next_tile == ctx->num_tiles) {
+		/*
+		 * the conversion is complete
+		 */
+		ipu_ic_convert_stop(run);
+		run->status = 0;
+		goto done;
+	}
+
+	/*
+	 * not done, place the next tile buffers.
+	 */
+	if (!ctx->double_buffering) {
+
+		src_off = &s_image->tile_off[ctx->next_tile];
+		dst_idx = ctx->out_tile_map[ctx->next_tile];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, 0,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, 0,
+				     d_image->base.phys0 + dst_off->offset);
+		if (s_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(cvt->in_chan,
+						src_off->u_off,
+						src_off->v_off);
+		if (d_image->fmt->y_depth)
+			ipu_cpmem_set_uv_offset(outch,
+						dst_off->u_off,
+						dst_off->v_off);
+
+		ipu_idmac_select_buffer(cvt->in_chan, 0);
+		ipu_idmac_select_buffer(outch, 0);
+
+	} else if (ctx->next_tile < ctx->num_tiles - 1) {
+
+		src_off = &s_image->tile_off[ctx->next_tile + 1];
+		dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
+		dst_off = &d_image->tile_off[dst_idx];
+
+		ipu_cpmem_set_buffer(cvt->in_chan, ctx->cur_buf_num,
+				     s_image->base.phys0 + src_off->offset);
+		ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
+				     d_image->base.phys0 + dst_off->offset);
+
+		ipu_idmac_select_buffer(cvt->in_chan, ctx->cur_buf_num);
+		ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
+
+		ctx->cur_buf_num ^= 1;
+	}
+
+	ctx->next_tile++;
+	return IRQ_HANDLED;
+done:
+	list_add_tail(&run->list, &cvt->done_q);
+	cvt->current_run = NULL;
+	ipu_ic_run_next(cvt);
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ipu_ic_norotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this is a rotation operation, just ignore */
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+static irqreturn_t ipu_ic_rotate_irq(int irq, void *data)
+{
+	struct image_converter *cvt = data;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+	unsigned long flags;
+	irqreturn_t ret;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* get current run and its context */
+	run = cvt->current_run;
+	if (!run) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	ctx = run->ctx;
+
+	if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		/* this was NOT a rotation operation, shouldn't happen */
+		dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
+		spin_unlock_irqrestore(&cvt->irqlock, flags);
+		return IRQ_HANDLED;
+	}
+
+	ret = ipu_ic_doirq(run);
+out:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+	return ret;
+}
+
+/*
+ * try to force the completion of runs for this ctx. Called when
+ * abort wait times out in ipu_image_convert_abort().
+ */
+static void ipu_ic_force_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct image_converter_run *run;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	run = cvt->current_run;
+	if (run && run->ctx == ctx) {
+		ipu_ic_convert_stop(run);
+		run->status = -EIO;
+		list_add_tail(&run->list, &cvt->done_q);
+		cvt->current_run = NULL;
+		ipu_ic_run_next(cvt);
+	}
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	ipu_ic_empty_done_q(cvt);
+}
+
+static void ipu_ic_release_ipu_resources(struct image_converter *cvt)
+{
+	if (cvt->out_eof_irq >= 0)
+		free_irq(cvt->out_eof_irq, cvt);
+	if (cvt->rot_out_eof_irq >= 0)
+		free_irq(cvt->rot_out_eof_irq, cvt);
+
+	if (!IS_ERR_OR_NULL(cvt->in_chan))
+		ipu_idmac_put(cvt->in_chan);
+	if (!IS_ERR_OR_NULL(cvt->out_chan))
+		ipu_idmac_put(cvt->out_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_in_chan))
+		ipu_idmac_put(cvt->rotation_in_chan);
+	if (!IS_ERR_OR_NULL(cvt->rotation_out_chan))
+		ipu_idmac_put(cvt->rotation_out_chan);
+
+	cvt->in_chan = cvt->out_chan = cvt->rotation_in_chan =
+		cvt->rotation_out_chan = NULL;
+	cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
+}
+
+static int ipu_ic_get_ipu_resources(struct image_converter *cvt)
+{
+	const struct ic_task_channels *chan = cvt->ic->ch;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	int ret;
+
+	/* get IDMAC channels */
+	cvt->in_chan = ipu_idmac_get(priv->ipu, chan->in);
+	cvt->out_chan = ipu_idmac_get(priv->ipu, chan->out);
+	if (IS_ERR(cvt->in_chan) || IS_ERR(cvt->out_chan)) {
+		dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	cvt->rotation_in_chan = ipu_idmac_get(priv->ipu, chan->rot_in);
+	cvt->rotation_out_chan = ipu_idmac_get(priv->ipu, chan->rot_out);
+	if (IS_ERR(cvt->rotation_in_chan) || IS_ERR(cvt->rotation_out_chan)) {
+		dev_err(priv->ipu->dev,
+			"could not acquire idmac rotation channels\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	/* acquire the EOF interrupts */
+	cvt->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						cvt->out_chan,
+						IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->out_eof_irq,
+				   ipu_ic_norotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			 cvt->out_eof_irq);
+		cvt->out_eof_irq = -1;
+		goto err;
+	}
+
+	cvt->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						     cvt->rotation_out_chan,
+						     IPU_IRQ_EOF);
+
+	ret = request_threaded_irq(cvt->rot_out_eof_irq,
+				   ipu_ic_rotate_irq, ipu_ic_bh,
+				   0, "ipu-ic", cvt);
+	if (ret < 0) {
+		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+			cvt->rot_out_eof_irq);
+		cvt->rot_out_eof_irq = -1;
+		goto err;
+	}
+
+	return 0;
+err:
+	ipu_ic_release_ipu_resources(cvt);
+	return ret;
+}
+
+static int ipu_ic_fill_image(struct image_converter_ctx *ctx,
+			     struct ipu_ic_image *ic_image,
+			     struct ipu_image *image,
+			     enum image_convert_type type)
+{
+	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
+
+	ic_image->base = *image;
+	ic_image->type = type;
+
+	ic_image->fmt = ipu_ic_get_format(image->pix.pixelformat);
+	if (!ic_image->fmt) {
+		dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
+			type == IMAGE_CONVERT_OUT ? "Output" : "Input");
+		return -EINVAL;
+	}
+
+	if (ic_image->fmt->y_depth)
+		ic_image->stride = (ic_image->fmt->y_depth *
+				    ic_image->base.pix.width) >> 3;
+	else
+		ic_image->stride  = ic_image->base.pix.bytesperline;
+
+	ipu_ic_calc_tile_dimensions(ctx, ic_image);
+	ipu_ic_calc_tile_offsets(ctx, ic_image);
+
+	return 0;
+}
+
+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+				unsigned int max, unsigned int align)
+{
+	/* Bits that must be zero to be aligned */
+	unsigned int mask = ~((1 << align) - 1);
+
+	/* Clamp to aligned min and max */
+	x = clamp(x, (min + ~mask) & mask, max & mask);
+
+	/* Round to nearest aligned value */
+	if (align)
+		x = (x + (1 << (align - 1))) & mask;
+
+	return x;
+}
+
+/*
+ * We have to adjust the tile width such that the tile physaddrs and
+ * U and V plane offsets are multiples of 8 bytes as required by
+ * the IPU DMA Controller. For the planar formats, this corresponds
+ * to a pixel alignment of 16 (but use a more formal equation since
+ * the variables are available). For all the packed formats, 8 is
+ * good enough.
+ */
+static inline u32 tile_width_align(const struct ipu_ic_pixfmt *fmt)
+{
+	return fmt->y_depth ? (64 * fmt->uv_width_dec) / fmt->y_depth : 8;
+}
+
+/*
+ * For tile height alignment, we have to ensure that the output tile
+ * heights are multiples of 8 lines if the IRT is required by the
+ * given rotation mode (the IRT performs rotations on 8x8 blocks
+ * at a time). If the IRT is not used, or for input image tiles,
+ * 2 lines are good enough.
+ */
+static inline u32 tile_height_align(enum image_convert_type type,
+				    enum ipu_rotate_mode rot_mode)
+{
+	return (type == IMAGE_CONVERT_OUT &&
+		ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
+}
+
+/* Adjusts input/output images to IPU restrictions */
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	const struct ipu_ic_pixfmt *infmt, *outfmt;
+	unsigned int num_in_rows, num_in_cols;
+	unsigned int num_out_rows, num_out_cols;
+	u32 w_align, h_align;
+
+	infmt = ipu_ic_get_format(in->pix.pixelformat);
+	outfmt = ipu_ic_get_format(out->pix.pixelformat);
+
+	/* set some defaults if needed */
+	if (!infmt) {
+		in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		infmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+	if (!outfmt) {
+		out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+		outfmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
+	}
+
+	if (!in->pix.width || !in->pix.height) {
+		in->pix.width = 640;
+		in->pix.height = 480;
+	}
+	if (!out->pix.width || !out->pix.height) {
+		out->pix.width = 640;
+		out->pix.height = 480;
+	}
+
+	/* image converter does not handle fields */
+	in->pix.field = out->pix.field = V4L2_FIELD_NONE;
+
+	/* resizer cannot downsize more than 4:1 */
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.width / 4);
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.height / 4);
+	} else {
+		out->pix.width = max_t(__u32, out->pix.width,
+				       in->pix.width / 4);
+		out->pix.height = max_t(__u32, out->pix.height,
+					in->pix.height / 4);
+	}
+
+	/* get tiling rows/cols from output format */
+	num_out_rows = ipu_ic_num_stripes(out->pix.height);
+	num_out_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		num_in_rows = num_out_cols;
+		num_in_cols = num_out_rows;
+	} else {
+		num_in_rows = num_out_rows;
+		num_in_cols = num_out_cols;
+	}
+
+	/* align input width/height */
+	w_align = ilog2(tile_width_align(infmt) * num_in_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
+			num_in_rows);
+	in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
+	in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
+
+	/* align output width/height */
+	w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
+	h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
+			num_out_rows);
+	out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
+	out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
+
+	/* set input/output strides and image sizes */
+	in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
+	in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
+	out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
+	out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
+
+/*
+ * this is used by ipu_image_convert_prepare() to verify set input and
+ * output images are valid before starting the conversion. Clients can
+ * also call it before calling ipu_image_convert_prepare().
+ */
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode)
+{
+	struct ipu_image testin, testout;
+	int ret;
+
+	testin = *in;
+	testout = *out;
+
+	ret = ipu_image_convert_adjust(&testin, &testout, rot_mode);
+	if (ret)
+		return ret;
+
+	if (testin.pix.width != in->pix.width ||
+	    testin.pix.height != in->pix.height ||
+	    testout.pix.width != out->pix.width ||
+	    testout.pix.height != out->pix.height)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
+
+/*
+ * Call ipu_image_convert_prepare() to prepare for the conversion of
+ * given images and rotation mode. Returns a new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context)
+{
+	struct ipu_ic_priv *priv = ic->priv;
+	struct image_converter *cvt = &ic->cvt;
+	struct ipu_ic_image *s_image, *d_image;
+	struct image_converter_ctx *ctx;
+	unsigned long flags;
+	bool get_res;
+	int ret;
+
+	if (!ic || !in || !out || !complete)
+		return ERR_PTR(-EINVAL);
+
+	/* verify the in/out images before continuing */
+	ret = ipu_image_convert_verify(in, out, rot_mode);
+	if (ret) {
+		dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
+			__func__);
+		return ERR_PTR(ret);
+	}
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p\n", __func__, ctx);
+
+	ctx->cvt = cvt;
+	init_completion(&ctx->aborted);
+
+	s_image = &ctx->in;
+	d_image = &ctx->out;
+
+	/* set tiling and rotation */
+	d_image->num_rows = ipu_ic_num_stripes(out->pix.height);
+	d_image->num_cols = ipu_ic_num_stripes(out->pix.width);
+	if (ipu_rot_mode_is_irt(rot_mode)) {
+		s_image->num_rows = d_image->num_cols;
+		s_image->num_cols = d_image->num_rows;
+	} else {
+		s_image->num_rows = d_image->num_rows;
+		s_image->num_cols = d_image->num_cols;
+	}
+
+	ctx->num_tiles = d_image->num_cols * d_image->num_rows;
+	ctx->rot_mode = rot_mode;
+
+	ret = ipu_ic_fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
+	if (ret)
+		goto out_free;
+	ret = ipu_ic_fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
+	if (ret)
+		goto out_free;
+
+	ipu_ic_calc_out_tile_map(ctx);
+
+	ipu_ic_dump_format(ctx, s_image);
+	ipu_ic_dump_format(ctx, d_image);
+
+	ctx->complete = complete;
+	ctx->complete_context = complete_context;
+
+	/*
+	 * Can we use double-buffering for this operation? If there is
+	 * only one tile (the whole image can be converted in a single
+	 * operation) there's no point in using double-buffering. Also,
+	 * the IPU's IDMAC channels allow only a single U and V plane
+	 * offset shared between both buffers, but these offsets change
+	 * for every tile, and therefore would have to be updated for
+	 * each buffer which is not possible. So double-buffering is
+	 * impossible when either the source or destination images are
+	 * a planar format (YUV420, YUV422P, etc.).
+	 */
+	ctx->double_buffering = (ctx->num_tiles > 1 &&
+				 !s_image->fmt->y_depth &&
+				 !d_image->fmt->y_depth);
+
+	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+		ret = ipu_ic_alloc_dma_buf(priv, &ctx->rot_intermediate[0],
+					   d_image->tile.size);
+		if (ret)
+			goto out_free;
+		if (ctx->double_buffering) {
+			ret = ipu_ic_alloc_dma_buf(priv,
+						   &ctx->rot_intermediate[1],
+						   d_image->tile.size);
+			if (ret)
+				goto out_free_dmabuf0;
+		}
+	}
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	get_res = list_empty(&cvt->ctx_list);
+
+	list_add_tail(&ctx->list, &cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (get_res) {
+		ret = ipu_ic_get_ipu_resources(cvt);
+		if (ret)
+			goto out_free_dmabuf1;
+	}
+
+	return ctx;
+
+out_free_dmabuf1:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	spin_lock_irqsave(&cvt->irqlock, flags);
+	list_del(&ctx->list);
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+out_free_dmabuf0:
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+out_free:
+	kfree(ctx);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+/*
+ * Carry out a single image conversion. Only the physaddr's of the input
+ * and output image buffers are needed. The conversion context must have
+ * been created previously with ipu_image_convert_prepare(). Returns the
+ * new run object.
+ */
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run;
+	unsigned long flags;
+	int ret = 0;
+
+	run = kzalloc(sizeof(*run), GFP_KERNEL);
+	if (!run)
+		return ERR_PTR(-ENOMEM);
+
+	run->ctx = ctx;
+	run->in_phys = in_phys;
+	run->out_phys = out_phys;
+
+	dev_dbg(priv->ipu->dev, "%s: ctx %p run %p\n", __func__,
+		ctx, run);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	if (ctx->aborting) {
+		ret = -EIO;
+		goto unlock;
+	}
+
+	list_add_tail(&run->list, &cvt->pending_q);
+
+	if (!cvt->current_run) {
+		ret = ipu_ic_run(run);
+		if (ret)
+			cvt->current_run = NULL;
+	}
+unlock:
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (ret) {
+		kfree(run);
+		run = ERR_PTR(ret);
+	}
+
+	return run;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_run);
+
+/* Abort any active or pending conversions for this context */
+void ipu_image_convert_abort(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	struct image_converter_run *run, *active_run, *tmp;
+	unsigned long flags;
+	int run_count, ret;
+	bool need_abort;
+
+	reinit_completion(&ctx->aborted);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	/* move all remaining pending runs in this context to done_q */
+	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
+		if (run->ctx != ctx)
+			continue;
+		run->status = -EIO;
+		list_move_tail(&run->list, &cvt->done_q);
+	}
+
+	run_count = ipu_ic_get_run_count(ctx, &cvt->done_q);
+	active_run = (cvt->current_run && cvt->current_run->ctx == ctx) ?
+		cvt->current_run : NULL;
+
+	need_abort = (run_count || active_run);
+
+	ctx->aborting = need_abort;
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (!need_abort) {
+		dev_dbg(priv->ipu->dev, "%s: no abort needed for ctx %p\n",
+			__func__, ctx);
+		return;
+	}
+
+	dev_dbg(priv->ipu->dev,
+		 "%s: wait for completion: %d runs, active run %p\n",
+		 __func__, run_count, active_run);
+
+	ret = wait_for_completion_timeout(&ctx->aborted,
+					  msecs_to_jiffies(10000));
+	if (ret == 0) {
+		dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
+		ipu_ic_force_abort(ctx);
+	}
+
+	ctx->aborting = false;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
+
+/* Unprepare image conversion context */
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx)
+{
+	struct image_converter *cvt = ctx->cvt;
+	struct ipu_ic_priv *priv = cvt->ic->priv;
+	unsigned long flags;
+	bool put_res;
+
+	/* make sure no runs are hanging around */
+	ipu_image_convert_abort(ctx);
+
+	dev_dbg(priv->ipu->dev, "%s: removing ctx %p\n", __func__, ctx);
+
+	spin_lock_irqsave(&cvt->irqlock, flags);
+
+	list_del(&ctx->list);
+
+	put_res = list_empty(&cvt->ctx_list);
+
+	spin_unlock_irqrestore(&cvt->irqlock, flags);
+
+	if (put_res)
+		ipu_ic_release_ipu_resources(cvt);
+
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
+	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
+
+	kfree(ctx);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
+
+/*
+ * "Canned" asynchronous single image conversion. On successful return
+ * caller must call ipu_image_convert_unprepare() after conversion completes.
+ * Returns the new conversion context.
+ */
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context)
+{
+	struct image_converter_ctx *ctx;
+	struct image_converter_run *run;
+
+	ctx = ipu_image_convert_prepare(ic, in, out, rot_mode,
+					complete, complete_context);
+	if (IS_ERR(ctx))
+		return ctx;
+
+	run = ipu_image_convert_run(ctx, in->phys0, out->phys0);
+	if (IS_ERR(run)) {
+		ipu_image_convert_unprepare(ctx);
+		return ERR_PTR(PTR_ERR(run));
+	}
+
+	return ctx;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert);
+
+/* "Canned" synchronous single image conversion */
+static void image_convert_sync_complete(void *data,
+					struct image_converter_run *run,
+					int err)
+{
+	struct completion *comp = data;
+
+	complete(comp);
+}
+
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode)
+{
+	struct image_converter_ctx *ctx;
+	struct completion comp;
+	int ret;
+
+	init_completion(&comp);
+
+	ctx = ipu_image_convert(ic, in, out, rot_mode,
+				image_convert_sync_complete, &comp);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
+	ret = (ret == 0) ? -ETIMEDOUT : 0;
+
+	ipu_image_convert_unprepare(ctx);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
 	struct ipu_ic_priv *priv = ic->priv;
@@ -759,6 +2428,7 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	ipu->ic_priv = priv;
 
 	spin_lock_init(&priv->lock);
+
 	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
 	if (!priv->base)
 		return -ENOMEM;
@@ -771,10 +2441,21 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
 	priv->ipu = ipu;
 
 	for (i = 0; i < IC_NUM_TASKS; i++) {
-		priv->task[i].task = i;
-		priv->task[i].priv = priv;
-		priv->task[i].reg = &ic_task_reg[i];
-		priv->task[i].bit = &ic_task_bit[i];
+		struct ipu_ic *ic = &priv->task[i];
+		struct image_converter *cvt = &ic->cvt;
+
+		ic->task = i;
+		ic->priv = priv;
+		ic->reg = &ic_task_reg[i];
+		ic->bit = &ic_task_bit[i];
+		ic->ch = &ic_task_ch[i];
+
+		cvt->ic = ic;
+		spin_lock_init(&cvt->irqlock);
+		INIT_LIST_HEAD(&cvt->ctx_list);
+		INIT_LIST_HEAD(&cvt->pending_q);
+		INIT_LIST_HEAD(&cvt->done_q);
+		cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
 	}
 
 	return 0;
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 8f77ddb..5938a69 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -63,17 +63,25 @@ enum ipu_csi_dest {
 /*
  * Enumeration of IPU rotation modes
  */
+#define IPU_ROT_BIT_VFLIP (1 << 0)
+#define IPU_ROT_BIT_HFLIP (1 << 1)
+#define IPU_ROT_BIT_90    (1 << 2)
+
 enum ipu_rotate_mode {
 	IPU_ROTATE_NONE = 0,
-	IPU_ROTATE_VERT_FLIP,
-	IPU_ROTATE_HORIZ_FLIP,
-	IPU_ROTATE_180,
-	IPU_ROTATE_90_RIGHT,
-	IPU_ROTATE_90_RIGHT_VFLIP,
-	IPU_ROTATE_90_RIGHT_HFLIP,
-	IPU_ROTATE_90_LEFT,
+	IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
+	IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
+	IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
+	IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
+	IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
+	IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
+			      IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
 };
 
+/* 90-degree rotations require the IRT unit */
+#define ipu_rot_mode_is_irt(m) ((m) >= IPU_ROTATE_90_RIGHT)
+
 enum ipu_color_space {
 	IPUV3_COLORSPACE_RGB,
 	IPUV3_COLORSPACE_YUV,
@@ -320,6 +328,7 @@ enum ipu_ic_task {
 };
 
 struct ipu_ic;
+
 int ipu_ic_task_init(struct ipu_ic *ic,
 		     int in_width, int in_height,
 		     int out_width, int out_height,
@@ -335,6 +344,40 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
 			  u32 width, u32 height, int burst_size,
 			  enum ipu_rotate_mode rot);
 int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
+
+struct image_converter_ctx;
+struct image_converter_run;
+
+typedef void (*image_converter_cb_t)(void *ctx,
+				     struct image_converter_run *run,
+				     int err);
+
+int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc);
+int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+			     enum ipu_rotate_mode rot_mode);
+struct image_converter_ctx *
+ipu_image_convert_prepare(struct ipu_ic *ic,
+			  struct ipu_image *in, struct ipu_image *out,
+			  enum ipu_rotate_mode rot_mode,
+			  image_converter_cb_t complete,
+			  void *complete_context);
+void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
+struct image_converter_run *
+ipu_image_convert_run(struct image_converter_ctx *ctx,
+		      dma_addr_t in_phys, dma_addr_t out_phys);
+void ipu_image_convert_abort(struct image_converter_ctx *ctx);
+struct image_converter_ctx *
+ipu_image_convert(struct ipu_ic *ic,
+		  struct ipu_image *in, struct ipu_image *out,
+		  enum ipu_rotate_mode rot_mode,
+		  image_converter_cb_t complete,
+		  void *complete_context);
+int ipu_image_convert_sync(struct ipu_ic *ic,
+			   struct ipu_image *in, struct ipu_image *out,
+			   enum ipu_rotate_mode rot_mode);
+
 int ipu_ic_enable(struct ipu_ic *ic);
 int ipu_ic_disable(struct ipu_ic *ic);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
-- 
1.9.1


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

* [PATCH 15/28] gpu: ipu-ic: allow multiple handles to ic
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (13 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 16/28] gpu: ipu-v3: rename CSI client device Steve Longerbeam
                     ` (12 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

The image converter kernel API supports conversion contexts and
job queues, so we should allow more than one handle to the IC, so
that multiple users can add jobs to the queue.

Note however that users that control the IC manually (that do not
use the image converter APIs but setup the IC task by hand via calls
to ipu_ic_task_enable(), ipu_ic_enable(), etc.) must still be careful not
to share the IC handle with other threads. At this point, the only user
that still controls the IC manually is the i.mx capture driver. In that
case the capture driver only allows one open context to get a handle
to the IC at a time, so we should be ok there.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-ic.c | 25 +------------------------
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
index f6a1125..51e34a1 100644
--- a/drivers/gpu/ipu-v3/ipu-ic.c
+++ b/drivers/gpu/ipu-v3/ipu-ic.c
@@ -342,7 +342,6 @@ struct ipu_ic {
 	enum ipu_color_space out_cs;
 	bool graphics;
 	bool rotation;
-	bool in_use;
 
 	struct image_converter cvt;
 
@@ -2380,38 +2379,16 @@ EXPORT_SYMBOL_GPL(ipu_ic_disable);
 struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task)
 {
 	struct ipu_ic_priv *priv = ipu->ic_priv;
-	unsigned long flags;
-	struct ipu_ic *ic, *ret;
 
 	if (task >= IC_NUM_TASKS)
 		return ERR_PTR(-EINVAL);
 
-	ic = &priv->task[task];
-
-	spin_lock_irqsave(&priv->lock, flags);
-
-	if (ic->in_use) {
-		ret = ERR_PTR(-EBUSY);
-		goto unlock;
-	}
-
-	ic->in_use = true;
-	ret = ic;
-
-unlock:
-	spin_unlock_irqrestore(&priv->lock, flags);
-	return ret;
+	return &priv->task[task];
 }
 EXPORT_SYMBOL_GPL(ipu_ic_get);
 
 void ipu_ic_put(struct ipu_ic *ic)
 {
-	struct ipu_ic_priv *priv = ic->priv;
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	ic->in_use = false;
-	spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_put);
 
-- 
1.9.1


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

* [PATCH 16/28] gpu: ipu-v3: rename CSI client device
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (14 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 15/28] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 17/28] gpio: pca953x: Add optional reset gpio control Steve Longerbeam
                     ` (11 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Rename the CSI client device in the client_reg[] table to
"imx-ipuv3-csi".

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index 374100e..bd6771b 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -1153,14 +1153,14 @@ static struct ipu_platform_reg client_reg[] = {
 			.dma[0] = IPUV3_CHANNEL_CSI0,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.csi = 1,
 			.dma[0] = IPUV3_CHANNEL_CSI1,
 			.dma[1] = -EINVAL,
 		},
-		.name = "imx-ipuv3-camera",
+		.name = "imx-ipuv3-csi",
 	}, {
 		.pdata = {
 			.di = 0,
-- 
1.9.1


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

* [PATCH 17/28] gpio: pca953x: Add optional reset gpio control
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (15 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 16/28] gpu: ipu-v3: rename CSI client device Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 18/28] clocksource/drivers/imx: add input capture support Steve Longerbeam
                     ` (10 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Add optional reset-gpios pin control. If present, de-assert the
specified reset gpio pin to bring the chip out of reset.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/gpio/gpio-pca953x.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index 5e3be32..8698815 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -21,6 +21,7 @@
 #include <asm/unaligned.h>
 #include <linux/of_platform.h>
 #include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
 
 #define PCA953X_INPUT		0
 #define PCA953X_OUTPUT		1
@@ -111,6 +112,8 @@ struct pca953x_chip {
 	const char *const *names;
 	int	chip_type;
 	unsigned long driver_data;
+
+	struct gpio_desc *reset_gpio;
 };
 
 static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val,
@@ -759,6 +762,21 @@ static int pca953x_probe(struct i2c_client *client,
 	} else {
 		chip->gpio_start = -1;
 		irq_base = 0;
+
+		/* see if we need to de-assert a reset pin */
+		chip->reset_gpio = devm_gpiod_get_optional(&client->dev,
+							   "reset",
+							   GPIOD_OUT_LOW);
+		if (IS_ERR(chip->reset_gpio)) {
+			dev_err(&client->dev, "request for reset pin failed\n");
+			return PTR_ERR(chip->reset_gpio);
+		}
+
+		if (chip->reset_gpio) {
+			/* bring chip out of reset */
+			dev_info(&client->dev, "releasing reset\n");
+			gpiod_set_value(chip->reset_gpio, 0);
+		}
 	}
 
 	chip->client = client;
-- 
1.9.1


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

* [PATCH 18/28] clocksource/drivers/imx: add input capture support
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (16 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 17/28] gpio: pca953x: Add optional reset gpio control Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 19/28] media: Add i.MX5/6 camera interface driver Steve Longerbeam
                     ` (9 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This patch adds support for the input capture function in the
i.MX GPT. Output compare and input capture functions are mixed
in the same register block, so we need to modify the irq ack/enable/
disable primitives to not stomp on the other function.

The input capture API is modelled after request/free irq:

typedef void (*mxc_icap_handler_t)(int, void *, struct timespec *);

int mxc_request_input_capture(unsigned int chan,
			      mxc_icap_handler_t handler,
			      unsigned long capflags, void *dev_id);

    - chan: the channel number being requested (0 or 1).

    - handler: a callback when there is an input capture event. The
      handler is given the channel number, the dev_id, and a timespec
      marking the input capture event. The timespec is always reset at
      request time, that is, the first event after request will always
      have a timespec of 0, and will increase thereafter.

    - capflags: IRQF_TRIGGER_RISING and/or IRQF_TRIGGER_FALLING. If
      both are specified, events will be triggered on both rising and
      falling edges of the input capture signal.

    - dev_id: a context pointer given back to the handler.

void mxc_free_input_capture(unsigned int chan, void *dev_id);

    This disables the given input capture channel in the GPT.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/clocksource/timer-imx-gpt.c | 463 ++++++++++++++++++++++++++++++++----
 include/linux/mxc_icap.h            |  20 ++
 2 files changed, 437 insertions(+), 46 deletions(-)
 create mode 100644 include/linux/mxc_icap.h

diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
index 99ec967..1f7f871 100644
--- a/drivers/clocksource/timer-imx-gpt.c
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -21,6 +21,7 @@
  * MA 02110-1301, USA.
  */
 
+#include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/clockchips.h>
@@ -32,6 +33,8 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/mxc_icap.h>
 #include <soc/imx/timer.h>
 
 /*
@@ -65,16 +68,53 @@
 #define V2_TCTL_CLK_PER		(2 << 6)
 #define V2_TCTL_CLK_OSC_DIV8	(5 << 6)
 #define V2_TCTL_FRR		(1 << 9)
+#define V2_TCTL_IM1_BIT		16
+#define V2_TCTL_IM2_BIT		18
+#define V2_IM_DISABLE		0
+#define V2_IM_RISING		1
+#define V2_IM_FALLING		2
+#define V2_IM_BOTH		3
 #define V2_TCTL_24MEN		(1 << 10)
 #define V2_TPRER_PRE24M		12
 #define V2_IR			0x0c
+#define V2_IR_OF1		(1 << 0)
+#define V2_IR_IF1		(1 << 3)
+#define V2_IR_IF2		(1 << 4)
 #define V2_TSTAT		0x08
 #define V2_TSTAT_OF1		(1 << 0)
+#define V2_TSTAT_IF1		(1 << 3)
+#define V2_TSTAT_IF2		(1 << 4)
 #define V2_TCN			0x24
 #define V2_TCMP			0x10
+#define V2_TCAP1		0x1c
+#define V2_TCAP2		0x20
 
 #define V2_TIMER_RATE_OSC_DIV8	3000000
 
+struct imx_timer;
+
+struct icap_channel {
+	struct imx_timer *imxtm;
+
+	int chan;
+
+	u32 cnt_reg;
+	u32 irqen_bit;
+	u32 status_bit;
+	u32 mode_bit;
+
+	mxc_icap_handler_t handler;
+	void *dev_id;
+
+	struct timespec ts;
+	cycles_t last_cycles;
+	bool first_event;
+};
+
+/* FIXME, for now can't find icap unless it's statically allocated */
+static struct icap_channel icap_channel[2];
+static DEFINE_SPINLOCK(icap_lock);
+
 struct imx_timer {
 	enum imx_gpt_type type;
 	void __iomem *base;
@@ -90,12 +130,20 @@ struct imx_gpt_data {
 	int reg_tstat;
 	int reg_tcn;
 	int reg_tcmp;
-	void (*gpt_setup_tctl)(struct imx_timer *imxtm);
-	void (*gpt_irq_enable)(struct imx_timer *imxtm);
-	void (*gpt_irq_disable)(struct imx_timer *imxtm);
-	void (*gpt_irq_acknowledge)(struct imx_timer *imxtm);
+	void (*gpt_oc_setup_tctl)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_enable)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_disable)(struct imx_timer *imxtm);
+	void (*gpt_oc_irq_acknowledge)(struct imx_timer *imxtm);
+	bool (*gpt_is_oc_irq)(unsigned int tstat);
 	int (*set_next_event)(unsigned long evt,
 			      struct clock_event_device *ced);
+
+	void (*gpt_ic_irq_enable)(struct icap_channel *ic);
+	void (*gpt_ic_irq_disable)(struct icap_channel *ic);
+	void (*gpt_ic_irq_acknowledge)(struct icap_channel *ic);
+	bool (*gpt_is_ic_irq)(unsigned int tstat);
+	void (*gpt_ic_enable)(struct icap_channel *ic, unsigned int mode);
+	void (*gpt_ic_disable)(struct icap_channel *ic);
 };
 
 static inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
@@ -103,52 +151,144 @@ static inline struct imx_timer *to_imx_timer(struct clock_event_device *ced)
 	return container_of(ced, struct imx_timer, ced);
 }
 
-static void imx1_gpt_irq_disable(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_disable(struct imx_timer *imxtm)
 {
 	unsigned int tmp;
 
 	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
 	writel_relaxed(tmp & ~MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_irq_disable imx1_gpt_irq_disable
+#define imx21_gpt_oc_irq_disable imx1_gpt_oc_irq_disable
 
-static void imx31_gpt_irq_disable(struct imx_timer *imxtm)
+static void imx31_gpt_oc_irq_disable(struct imx_timer *imxtm)
 {
-	writel_relaxed(0, imxtm->base + V2_IR);
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	writel_relaxed(tmp & ~V2_IR_OF1, imxtm->base + V2_IR);
 }
-#define imx6dl_gpt_irq_disable imx31_gpt_irq_disable
+#define imx6dl_gpt_oc_irq_disable imx31_gpt_oc_irq_disable
 
-static void imx1_gpt_irq_enable(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_enable(struct imx_timer *imxtm)
 {
 	unsigned int tmp;
 
 	tmp = readl_relaxed(imxtm->base + MXC_TCTL);
 	writel_relaxed(tmp | MX1_2_TCTL_IRQEN, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_irq_enable imx1_gpt_irq_enable
+#define imx21_gpt_oc_irq_enable imx1_gpt_oc_irq_enable
 
-static void imx31_gpt_irq_enable(struct imx_timer *imxtm)
+static void imx31_gpt_oc_irq_enable(struct imx_timer *imxtm)
 {
-	writel_relaxed(1<<0, imxtm->base + V2_IR);
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	writel_relaxed(tmp | V2_IR_OF1, imxtm->base + V2_IR);
 }
-#define imx6dl_gpt_irq_enable imx31_gpt_irq_enable
+#define imx6dl_gpt_oc_irq_enable imx31_gpt_oc_irq_enable
 
-static void imx1_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static void imx1_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(0, imxtm->base + MX1_2_TSTAT);
 }
 
-static void imx21_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static void imx21_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(MX2_TSTAT_CAPT | MX2_TSTAT_COMP,
 				imxtm->base + MX1_2_TSTAT);
 }
 
-static void imx31_gpt_irq_acknowledge(struct imx_timer *imxtm)
+static bool imx1_gpt_is_oc_irq(unsigned int tstat)
+{
+	return true;
+}
+
+static bool imx21_gpt_is_oc_irq(unsigned int tstat)
+{
+	return (tstat & MX2_TSTAT_COMP) != 0;
+}
+
+static bool imx31_gpt_is_oc_irq(unsigned int tstat)
+{
+	return (tstat & V2_TSTAT_OF1) != 0;
+}
+#define imx6dl_gpt_is_oc_irq imx31_gpt_is_oc_irq
+
+static void imx31_gpt_oc_irq_acknowledge(struct imx_timer *imxtm)
 {
 	writel_relaxed(V2_TSTAT_OF1, imxtm->base + V2_TSTAT);
 }
-#define imx6dl_gpt_irq_acknowledge imx31_gpt_irq_acknowledge
+#define imx6dl_gpt_oc_irq_acknowledge imx31_gpt_oc_irq_acknowledge
+
+static void imx31_gpt_ic_irq_disable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	tmp &= ~ic->irqen_bit;
+	writel_relaxed(tmp, imxtm->base + V2_IR);
+}
+#define imx6dl_gpt_ic_irq_disable imx31_gpt_ic_irq_disable
+
+static void imx31_gpt_ic_irq_enable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tmp;
+
+	tmp = readl_relaxed(imxtm->base + V2_IR);
+	tmp |= ic->irqen_bit;
+	writel_relaxed(tmp, imxtm->base + V2_IR);
+}
+#define imx6dl_gpt_ic_irq_enable imx31_gpt_ic_irq_enable
+
+static void imx31_gpt_ic_irq_acknowledge(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+
+	writel_relaxed(ic->status_bit, imxtm->base + V2_TSTAT);
+}
+#define imx6dl_gpt_ic_irq_acknowledge imx31_gpt_ic_irq_acknowledge
+
+static bool imx1_gpt_is_ic_irq(unsigned int tstat)
+{
+	return false;
+}
+#define imx21_gpt_is_ic_irq imx1_gpt_is_ic_irq
+
+static bool imx31_gpt_is_ic_irq(unsigned int tstat)
+{
+	return (tstat & (V2_TSTAT_IF1 | V2_TSTAT_IF2)) != 0;
+}
+#define imx6dl_gpt_is_ic_irq imx31_gpt_is_ic_irq
+
+static void imx31_gpt_ic_enable(struct icap_channel *ic, unsigned int mode)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tctl, mask;
+
+	mask = 0x3 << ic->mode_bit;
+	mode <<= ic->mode_bit;
+
+	tctl = readl_relaxed(imxtm->base + MXC_TCTL);
+	tctl &= ~mask;
+	tctl |= mode;
+	writel_relaxed(tctl, imxtm->base + MXC_TCTL);
+}
+#define imx6dl_gpt_ic_enable imx31_gpt_ic_enable
+
+static void imx31_gpt_ic_disable(struct icap_channel *ic)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	unsigned int tctl, mask;
+
+	mask = 0x3 << ic->mode_bit;
+
+	tctl = readl_relaxed(imxtm->base + MXC_TCTL);
+	tctl &= ~mask;
+	writel_relaxed(tctl, imxtm->base + MXC_TCTL);
+}
+#define imx6dl_gpt_ic_disable imx31_gpt_ic_disable
 
 static void __iomem *sched_clock_reg;
 
@@ -164,6 +304,32 @@ static unsigned long imx_read_current_timer(void)
 	return readl_relaxed(sched_clock_reg);
 }
 
+static void mxc_update_icap_ts(struct icap_channel *ic, struct timespec *ts)
+{
+	struct imx_timer *imxtm = ic->imxtm;
+	cycles_t cycles;
+	u64 diff;
+	u32 rem;
+
+	cycles = readl_relaxed(imxtm->base + ic->cnt_reg);
+	if (!ic->first_event) {
+		if (cycles >= ic->last_cycles)
+			diff = cycles - ic->last_cycles;
+		else
+			diff = ((u64)1 << 32) - ic->last_cycles + cycles;
+
+		diff *= NSEC_PER_SEC;
+		diff += imx_delay_timer.freq / 2;
+
+		rem = do_div(diff, imx_delay_timer.freq);
+
+		timespec_add_ns(&ic->ts, diff);
+	}
+
+	*ts = ic->ts;
+	ic->last_cycles = cycles;
+}
+
 static int __init mxc_clocksource_init(struct imx_timer *imxtm)
 {
 	unsigned int c = clk_get_rate(imxtm->clk_per);
@@ -224,14 +390,14 @@ static int mxc_shutdown(struct clock_event_device *ced)
 	local_irq_save(flags);
 
 	/* Disable interrupt in GPT module */
-	imxtm->gpt->gpt_irq_disable(imxtm);
+	imxtm->gpt->gpt_oc_irq_disable(imxtm);
 
 	tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
 	/* Set event time into far-far future */
 	writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
 
 	/* Clear pending interrupt */
-	imxtm->gpt->gpt_irq_acknowledge(imxtm);
+	imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
 
 #ifdef DEBUG
 	printk(KERN_INFO "%s: changing mode\n", __func__);
@@ -254,7 +420,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 	local_irq_save(flags);
 
 	/* Disable interrupt in GPT module */
-	imxtm->gpt->gpt_irq_disable(imxtm);
+	imxtm->gpt->gpt_oc_irq_disable(imxtm);
 
 	if (!clockevent_state_oneshot(ced)) {
 		u32 tcn = readl_relaxed(imxtm->base + imxtm->gpt->reg_tcn);
@@ -262,7 +428,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 		writel_relaxed(tcn - 3, imxtm->base + imxtm->gpt->reg_tcmp);
 
 		/* Clear pending interrupt */
-		imxtm->gpt->gpt_irq_acknowledge(imxtm);
+		imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
 	}
 
 #ifdef DEBUG
@@ -275,7 +441,7 @@ static int mxc_set_oneshot(struct clock_event_device *ced)
 	 * to call mxc_set_next_event() or shutdown clock after
 	 * mode switching
 	 */
-	imxtm->gpt->gpt_irq_enable(imxtm);
+	imxtm->gpt->gpt_oc_irq_enable(imxtm);
 	local_irq_restore(flags);
 
 	return 0;
@@ -292,9 +458,31 @@ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
 
 	tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat);
 
-	imxtm->gpt->gpt_irq_acknowledge(imxtm);
+	if (imxtm->gpt->gpt_is_ic_irq(tstat)) {
+		struct icap_channel *ic;
+		struct timespec ts;
+		int i;
+
+		for (i = 0; i < 2; i++) {
+			ic = &icap_channel[i];
+
+			if (!(tstat & ic->status_bit))
+				continue;
 
-	ced->event_handler(ced);
+			imxtm->gpt->gpt_ic_irq_acknowledge(ic);
+
+			mxc_update_icap_ts(ic, &ts);
+			ic->first_event = false;
+
+			if (ic->handler)
+				ic->handler(ic->chan, ic->dev_id, &ts);
+		}
+	}
+
+	if (imxtm->gpt->gpt_is_oc_irq(tstat)) {
+		imxtm->gpt->gpt_oc_irq_acknowledge(imxtm);
+		ced->event_handler(ced);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -324,16 +512,16 @@ static int __init mxc_clockevent_init(struct imx_timer *imxtm)
 	return setup_irq(imxtm->irq, act);
 }
 
-static void imx1_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx1_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
 	tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
 	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
 }
-#define imx21_gpt_setup_tctl imx1_gpt_setup_tctl
+#define imx21_gpt_oc_setup_tctl imx1_gpt_oc_setup_tctl
 
-static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx31_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
@@ -346,7 +534,7 @@ static void imx31_gpt_setup_tctl(struct imx_timer *imxtm)
 	writel_relaxed(tctl_val, imxtm->base + MXC_TCTL);
 }
 
-static void imx6dl_gpt_setup_tctl(struct imx_timer *imxtm)
+static void imx6dl_gpt_oc_setup_tctl(struct imx_timer *imxtm)
 {
 	u32 tctl_val;
 
@@ -367,10 +555,12 @@ static const struct imx_gpt_data imx1_gpt_data = {
 	.reg_tstat = MX1_2_TSTAT,
 	.reg_tcn = MX1_2_TCN,
 	.reg_tcmp = MX1_2_TCMP,
-	.gpt_irq_enable = imx1_gpt_irq_enable,
-	.gpt_irq_disable = imx1_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx1_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx1_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx1_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx1_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx1_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx1_gpt_is_oc_irq,
+	.gpt_is_ic_irq = imx1_gpt_is_ic_irq,
+	.gpt_oc_setup_tctl = imx1_gpt_oc_setup_tctl,
 	.set_next_event = mx1_2_set_next_event,
 };
 
@@ -378,10 +568,12 @@ static const struct imx_gpt_data imx21_gpt_data = {
 	.reg_tstat = MX1_2_TSTAT,
 	.reg_tcn = MX1_2_TCN,
 	.reg_tcmp = MX1_2_TCMP,
-	.gpt_irq_enable = imx21_gpt_irq_enable,
-	.gpt_irq_disable = imx21_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx21_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx21_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx21_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx21_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx21_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx21_gpt_is_oc_irq,
+	.gpt_is_ic_irq = imx21_gpt_is_ic_irq,
+	.gpt_oc_setup_tctl = imx21_gpt_oc_setup_tctl,
 	.set_next_event = mx1_2_set_next_event,
 };
 
@@ -389,26 +581,136 @@ static const struct imx_gpt_data imx31_gpt_data = {
 	.reg_tstat = V2_TSTAT,
 	.reg_tcn = V2_TCN,
 	.reg_tcmp = V2_TCMP,
-	.gpt_irq_enable = imx31_gpt_irq_enable,
-	.gpt_irq_disable = imx31_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx31_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx31_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx31_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx31_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx31_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx31_gpt_is_oc_irq,
+	.gpt_oc_setup_tctl = imx31_gpt_oc_setup_tctl,
 	.set_next_event = v2_set_next_event,
+
+	/* input capture methods */
+	.gpt_ic_irq_enable = imx31_gpt_ic_irq_enable,
+	.gpt_ic_irq_disable = imx31_gpt_ic_irq_disable,
+	.gpt_ic_irq_acknowledge = imx31_gpt_ic_irq_acknowledge,
+	.gpt_is_ic_irq = imx31_gpt_is_ic_irq,
+	.gpt_ic_enable = imx31_gpt_ic_enable,
+	.gpt_ic_disable = imx31_gpt_ic_disable,
 };
 
 static const struct imx_gpt_data imx6dl_gpt_data = {
 	.reg_tstat = V2_TSTAT,
 	.reg_tcn = V2_TCN,
 	.reg_tcmp = V2_TCMP,
-	.gpt_irq_enable = imx6dl_gpt_irq_enable,
-	.gpt_irq_disable = imx6dl_gpt_irq_disable,
-	.gpt_irq_acknowledge = imx6dl_gpt_irq_acknowledge,
-	.gpt_setup_tctl = imx6dl_gpt_setup_tctl,
+	.gpt_oc_irq_enable = imx6dl_gpt_oc_irq_enable,
+	.gpt_oc_irq_disable = imx6dl_gpt_oc_irq_disable,
+	.gpt_oc_irq_acknowledge = imx6dl_gpt_oc_irq_acknowledge,
+	.gpt_is_oc_irq = imx6dl_gpt_is_oc_irq,
+	.gpt_oc_setup_tctl = imx6dl_gpt_oc_setup_tctl,
 	.set_next_event = v2_set_next_event,
+
+	/* input capture methods */
+	.gpt_ic_irq_enable = imx6dl_gpt_ic_irq_enable,
+	.gpt_ic_irq_disable = imx6dl_gpt_ic_irq_disable,
+	.gpt_ic_irq_acknowledge = imx6dl_gpt_ic_irq_acknowledge,
+	.gpt_is_ic_irq = imx6dl_gpt_is_ic_irq,
+	.gpt_ic_enable = imx6dl_gpt_ic_enable,
+	.gpt_ic_disable = imx6dl_gpt_ic_disable,
 };
 
+int mxc_request_input_capture(unsigned int chan, mxc_icap_handler_t handler,
+			      unsigned long capflags, void *dev_id)
+{
+	struct imx_timer *imxtm;
+	struct icap_channel *ic;
+	unsigned long flags;
+	int ret = 0;
+	u32 mode;
+
+	/* we only care about rising and falling flags */
+	capflags &= (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING);
+
+	if (chan > 1 || !handler || !capflags)
+		return -EINVAL;
+
+	ic = &icap_channel[chan];
+	imxtm = ic->imxtm;
+
+	if (!imxtm->gpt->gpt_ic_enable)
+		return -ENODEV;
+
+	spin_lock_irqsave(&icap_lock, flags);
+
+	if (ic->handler) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ic->handler = handler;
+	ic->dev_id = dev_id;
+
+	switch (capflags) {
+	case IRQF_TRIGGER_RISING:
+		mode = V2_IM_RISING;
+		break;
+	case IRQF_TRIGGER_FALLING:
+		mode = V2_IM_FALLING;
+		break;
+	default:
+		mode = V2_IM_BOTH;
+		break;
+	}
+
+	/* ack any pending input capture interrupt before enabling */
+	imxtm->gpt->gpt_ic_irq_acknowledge(ic);
+
+	/* initialize timespec */
+	ic->ts.tv_sec = ic->ts.tv_nsec = 0;
+	ic->first_event = true;
+
+	imxtm->gpt->gpt_ic_enable(ic, mode);
+	imxtm->gpt->gpt_ic_irq_enable(ic);
+
+out:
+	spin_unlock_irqrestore(&icap_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mxc_request_input_capture);
+
+void mxc_free_input_capture(unsigned int chan, void *dev_id)
+{
+	struct imx_timer *imxtm;
+	struct icap_channel *ic;
+	unsigned long flags;
+
+	if (chan > 1)
+		return;
+
+	ic = &icap_channel[chan];
+	imxtm = ic->imxtm;
+
+	if (!imxtm->gpt->gpt_ic_disable)
+		return;
+
+	spin_lock_irqsave(&icap_lock, flags);
+
+	if (!ic->handler || dev_id != ic->dev_id)
+		goto out;
+
+	imxtm->gpt->gpt_ic_irq_disable(ic);
+	imxtm->gpt->gpt_ic_disable(ic);
+
+	ic->handler = NULL;
+	ic->dev_id = NULL;
+out:
+	spin_unlock_irqrestore(&icap_lock, flags);
+}
+EXPORT_SYMBOL_GPL(mxc_free_input_capture);
+
 static void __init _mxc_timer_init(struct imx_timer *imxtm)
 {
+	struct icap_channel *ic;
+	int i;
+
 	switch (imxtm->type) {
 	case GPT_TYPE_IMX1:
 		imxtm->gpt = &imx1_gpt_data;
@@ -443,11 +745,16 @@ static void __init _mxc_timer_init(struct imx_timer *imxtm)
 	writel_relaxed(0, imxtm->base + MXC_TCTL);
 	writel_relaxed(0, imxtm->base + MXC_TPRER); /* see datasheet note */
 
-	imxtm->gpt->gpt_setup_tctl(imxtm);
+	imxtm->gpt->gpt_oc_setup_tctl(imxtm);
 
 	/* init and register the timer to the framework */
 	mxc_clocksource_init(imxtm);
 	mxc_clockevent_init(imxtm);
+
+	for (i = 0; i < 2; i++) {
+		ic = &icap_channel[i];
+		ic->imxtm = imxtm;
+	}
 }
 
 void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
@@ -469,6 +776,70 @@ void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type)
 	_mxc_timer_init(imxtm);
 }
 
+/*
+ * a platform driver is needed in order to acquire pinmux
+ * for input capture pins. The probe call is also useful
+ * for setting up the input capture channel structures.
+ */
+static int mxc_timer_probe(struct platform_device *pdev)
+{
+	struct icap_channel *ic;
+	int i;
+
+	/* setup the input capture channels */
+	for (i = 0; i < 2; i++) {
+		ic = &icap_channel[i];
+		ic->chan = i;
+		if (i == 0) {
+			ic->cnt_reg = V2_TCAP1;
+			ic->irqen_bit = V2_IR_IF1;
+			ic->status_bit = V2_TSTAT_IF1;
+			ic->mode_bit = V2_TCTL_IM1_BIT;
+		} else {
+			ic->cnt_reg = V2_TCAP2;
+			ic->irqen_bit = V2_IR_IF2;
+			ic->status_bit = V2_TSTAT_IF2;
+			ic->mode_bit = V2_TCTL_IM2_BIT;
+		}
+	}
+
+	return 0;
+}
+
+static int mxc_timer_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id timer_of_match[] = {
+	{ .compatible = "fsl,imx1-gpt" },
+	{ .compatible = "fsl,imx21-gpt" },
+	{ .compatible = "fsl,imx27-gpt" },
+	{ .compatible = "fsl,imx31-gpt" },
+	{ .compatible = "fsl,imx25-gpt" },
+	{ .compatible = "fsl,imx50-gpt" },
+	{ .compatible = "fsl,imx51-gpt" },
+	{ .compatible = "fsl,imx53-gpt" },
+	{ .compatible = "fsl,imx6q-gpt" },
+	{ .compatible = "fsl,imx6dl-gpt" },
+	{ .compatible = "fsl,imx6sl-gpt" },
+	{ .compatible = "fsl,imx6sx-gpt" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, timer_of_match);
+
+static struct platform_driver mxc_timer_pdrv = {
+	.probe		= mxc_timer_probe,
+	.remove		= mxc_timer_remove,
+	.driver		= {
+		.name	= "mxc-timer",
+		.owner	= THIS_MODULE,
+		.of_match_table	= timer_of_match,
+	},
+};
+
+module_platform_driver(mxc_timer_pdrv);
+
 static void __init mxc_timer_init_dt(struct device_node *np,  enum imx_gpt_type type)
 {
 	struct imx_timer *imxtm;
diff --git a/include/linux/mxc_icap.h b/include/linux/mxc_icap.h
new file mode 100644
index 0000000..a829f11
--- /dev/null
+++ b/include/linux/mxc_icap.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 Mentor Graphics, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __MXC_ICAP_H__
+#define __MXC_ICAP_H__
+
+typedef void (*mxc_icap_handler_t)(int, void *, struct timespec *);
+
+int mxc_request_input_capture(unsigned int chan, mxc_icap_handler_t handler,
+			      unsigned long capflags, void *dev_id);
+void mxc_free_input_capture(unsigned int chan, void *dev_id);
+
+#endif
-- 
1.9.1


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

* [PATCH 19/28] media: Add i.MX5/6 camera interface driver
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (17 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 18/28] clocksource/drivers/imx: add input capture support Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 20/28] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
                     ` (8 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This is a V4L2 camera interface driver for i.MX5/6. See
Documentation/video4linux/imx_camera.txt and device tree binding
documentation at Documentation/devicetree/bindings/media/imx.txt.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 Documentation/devicetree/bindings/media/imx.txt   |  426 ++++
 Documentation/video4linux/imx_camera.txt          |  243 +++
 drivers/staging/media/Kconfig                     |    2 +
 drivers/staging/media/Makefile                    |    1 +
 drivers/staging/media/imx/Kconfig                 |   23 +
 drivers/staging/media/imx/Makefile                |    1 +
 drivers/staging/media/imx/capture/Kconfig         |    3 +
 drivers/staging/media/imx/capture/Makefile        |    5 +
 drivers/staging/media/imx/capture/imx-camif.c     | 2326 +++++++++++++++++++++
 drivers/staging/media/imx/capture/imx-camif.h     |  270 +++
 drivers/staging/media/imx/capture/imx-csi.c       |  195 ++
 drivers/staging/media/imx/capture/imx-ic-prpenc.c |  661 ++++++
 drivers/staging/media/imx/capture/imx-of.c        |  354 ++++
 drivers/staging/media/imx/capture/imx-of.h        |   18 +
 drivers/staging/media/imx/capture/imx-smfc.c      |  506 +++++
 drivers/staging/media/imx/capture/imx-vdic.c      |  995 +++++++++
 include/media/imx.h                               |   15 +
 include/uapi/Kbuild                               |    1 +
 include/uapi/linux/v4l2-controls.h                |    4 +
 include/uapi/media/Kbuild                         |    2 +
 include/uapi/media/imx.h                          |   22 +
 21 files changed, 6073 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/imx.txt
 create mode 100644 Documentation/video4linux/imx_camera.txt
 create mode 100644 drivers/staging/media/imx/Kconfig
 create mode 100644 drivers/staging/media/imx/Makefile
 create mode 100644 drivers/staging/media/imx/capture/Kconfig
 create mode 100644 drivers/staging/media/imx/capture/Makefile
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.c
 create mode 100644 drivers/staging/media/imx/capture/imx-camif.h
 create mode 100644 drivers/staging/media/imx/capture/imx-csi.c
 create mode 100644 drivers/staging/media/imx/capture/imx-ic-prpenc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.c
 create mode 100644 drivers/staging/media/imx/capture/imx-of.h
 create mode 100644 drivers/staging/media/imx/capture/imx-smfc.c
 create mode 100644 drivers/staging/media/imx/capture/imx-vdic.c
 create mode 100644 include/media/imx.h
 create mode 100644 include/uapi/media/Kbuild
 create mode 100644 include/uapi/media/imx.h

diff --git a/Documentation/devicetree/bindings/media/imx.txt b/Documentation/devicetree/bindings/media/imx.txt
new file mode 100644
index 0000000..5a89b51
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/imx.txt
@@ -0,0 +1,426 @@
+Freescale i.MX Video Capture
+
+Video Capture node
+------------------
+
+This is the imx video capture host interface node. The host node is an IPU
+client and uses the register-level primitives of the IPU, so it does
+not require reg or interrupt properties. Only a compatible property
+and a list of IPU CSI port phandles is required.
+
+Required properties:
+- compatible	: "fsl,imx-video-capture";
+- ports         : a list of CSI port phandles this device will control
+
+Optional properties:
+- fim           : child node that sets boot-time behavior of the
+		  Frame Interval Monitor;
+
+fim child node
+--------------
+
+This is an optional child node of the video capture node. It can
+be used to modify the default control values for the video capture
+Frame Interval Monitor. Refer to Documentation/video4linux/imx_camera.txt
+for more info on the Frame Interval Monitor.
+
+Optional properties:
+- enable          : enable (1) or disable (0) the FIM;
+- num-avg         : how many frame intervals the FIM will average;
+- num-skip        : how many frames the FIM will skip after a video
+		    capture restart before beginning to sample frame
+		    intervals;
+- tolerance-range : a range of tolerances for the averaged frame
+		    interval error, specified as <min max>, in usec.
+		    The FIM will signal a frame interval error if
+		    min < error < max. If the max is <= min, then
+		    tolerance range is disabled (interval error if
+		    error > min).
+- input-capture-channel: an input capture channel and channel flags,
+			 specified as <chan flags>. The channel number
+			 must be 0 or 1. The flags can be
+			 IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, or
+			 IRQ_TYPE_EDGE_BOTH, and specify which input
+			 capture signal edge will trigger the event. If
+			 an input capture channel is specified, the FIM
+			 will use this method to measure frame intervals
+			 instead of via the EOF interrupt. The input capture
+			 method is much preferred over EOF as it is not
+			 subject to interrupt latency errors. However it
+			 requires routing the VSYNC or FIELD output
+			 signals of the camera sensor to one of the
+			 i.MX input capture pads (SD1_DAT0, SD1_DAT1),
+			 which also gives up support for SD1.
+
+
+mipi_csi2 node
+--------------
+
+This is the device node for the MIPI CSI-2 Receiver, required for MIPI
+CSI-2 sensors.
+
+Required properties:
+- compatible	: "fsl,imx-mipi-csi2";
+- reg           : physical base address and length of the register set;
+- clocks	: the MIPI CSI-2 receiver requires three clocks: hsi_tx
+                  (the DPHY clock), video_27m, and eim_sel;
+- clock-names	: must contain "dphy_clk", "cfg_clk", "pix_clk";
+
+Optional properties:
+- interrupts	: must contain two level-triggered interrupts,
+                  in order: 100 and 101;
+
+
+Device tree nodes of the image sensors' controlled directly by the imx
+camera host interface driver must be child nodes of their corresponding
+I2C bus controller node. The data link of these image sensors must be
+specified using the common video interfaces bindings, defined in
+video-interfaces.txt.
+
+Video capture is supported with the following imx-based reference
+platforms:
+
+
+SabreLite with OV5642
+---------------------
+
+The OV5642 module is connected to the parallel bus input on the internal
+video mux to IPU1 CSI0. It's i2c bus connects to i2c bus 2, so the ov5642
+sensor node must be a child of i2c2.
+
+OV5642 Required properties:
+- compatible	: "ovti,ov5642";
+- clocks        : the OV5642 system clock (cko2, 200);
+- clock-names	: must be "xclk";
+- reg           : must be 0x3c;
+- xclk          : the system clock frequency, must be 24000000;
+- reset-gpios   : must be <&gpio1 8 GPIO_ACTIVE_LOW>;
+- pwdn-gpios    : must be <&gpio1 6 GPIO_ACTIVE_HIGH>;
+
+OV5642 Endpoint Required properties:
+- remote-endpoint : must connect to parallel sensor interface input endpoint 
+  		    on ipu1_csi0 video mux (ipu1_csi0_mux_from_parallel_sensor).
+- bus-width       : must be 8;
+- hsync-active    : must be 1;
+- vsync-active    : must be 1;
+
+The following is an example devicetree video capture configuration for
+SabreLite:
+
+/ {
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>;
+		status = "okay";
+	};
+};
+
+&ipu1_csi0_from_ipu1_csi0_mux {
+	bus-width = <8>;
+	data-shift = <12>; /* Lines 19:12 used */
+	hsync-active = <1>;
+	vync-active = <1>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&ov5642_to_ipu1_csi0_mux>;
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+
+&i2c2 {
+	camera: ov5642@3c {
+		compatible = "ovti,ov5642";
+		clocks = <&clks 200>;
+		clock-names = "xclk";
+		reg = <0x3c>;
+		xclk = <24000000>;
+		reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
+		pwdn-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
+		gp-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>;
+
+		port {
+			ov5642_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <8>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+			};
+		};
+	};
+};
+
+
+SabreAuto with ADV7180
+----------------------
+
+On the SabreAuto, an on-board ADV7180 SD decoder is connected to the
+parallel bus input on the internal video mux to IPU1 CSI0.
+
+Two analog video inputs are routed to the ADV7180 on the SabreAuto,
+composite on Ain1, and composite on Ain3. Those inputs are defined
+via inputs and input-names properties under the ipu1_csi0_mux parallel
+sensor input endpoint (ipu1_csi0_mux_from_parallel_sensor).
+
+Regulators and port expanders are required for the ADV7180 (power pin
+is via port expander gpio on i2c3). The reset pin to the port expander
+chip (MAX7310) is controlled by a gpio, so a reset-gpios property must
+be defined under the port expander node to control it.
+
+The sabreauto uses a steering pin to select between the SDA signal on
+i2c3 bus, and a data-in pin for an SPI NOR chip. i2cmux can be used to
+control this steering pin. Idle state of the i2cmux selects SPI NOR.
+This is not classic way to use i2cmux, since one side of the mux selects
+something other than an i2c bus, but it works and is probably the cleanest
+solution. Note that if one thread is attempting to access SPI NOR while
+another thread is accessing i2c3, the SPI NOR access will fail since the
+i2cmux has selected the SDA pin rather than SPI NOR data-in. This couldn't
+be avoided in any case, the board is not designed to allow concurrent
+i2c3 and SPI NOR functions (and the default device-tree does not enable
+SPI NOR anyway).
+
+Endpoint ipu1_csi0_mux_from_parallel_sensor Optional Properties:
+- inputs        : list of input mux values, must be 0x00 followed by
+                  0x02 on SabreAuto;
+- input-names   : names of the inputs;
+
+ADV7180 Required properties:
+- compatible    : "adi,adv7180";
+- reg           : must be 0x21;
+
+ADV7180 Optional properties:
+- DOVDD-supply  : DOVDD regulator supply;
+- AVDD-supply   : AVDD regulator supply;
+- DVDD-supply   : DVDD regulator supply;
+- PVDD-supply   : PVDD regulator supply;
+- pwdn-gpios    : gpio to control ADV7180 power pin, must be
+                  <&port_exp_b 2 GPIO_ACTIVE_LOW> on SabreAuto;
+- interrupts    : interrupt from ADV7180, must be <27 0x8> on SabreAuto;
+- interrupt-parent : must be <&gpio1> on SabreAuto;
+
+ADV7180 Endpoint Required properties:
+- remote-endpoint : must connect to parallel sensor interface input endpoint 
+  		    on ipu1_csi0 video mux (ipu1_csi0_mux_from_parallel_sensor).
+- bus-width       : must be 8;
+
+
+The following is an example devicetree video capture configuration for
+SabreAuto:
+
+/ {
+	i2cmux {
+		i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+
+			camera: adv7180@21 {
+				compatible = "adi,adv7180";
+				reg = <0x21>;
+				pwdn-gpios = <&port_exp_b 2 GPIO_ACTIVE_LOW>;
+				interrupt-parent = <&gpio1>;
+				interrupts = <27 0x8>;
+
+				port {
+					adv7180_to_ipu1_csi0_mux: endpoint {
+						remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+						bus-width = <8>;
+					};
+				};
+			};
+
+			port_exp_b: gpio_pca953x@32 {
+				compatible = "maxim,max7310";
+				gpio-controller;
+				#gpio-cells = <2>;
+				reg = <0x32>;
+				reset-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
+			};
+
+		};
+	};
+
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>;
+		status = "okay";
+
+		fim {
+			enable = <1>;
+			tolerance-range = <20 0>;
+			num-avg = <1>;
+			input-capture-channel = <0 IRQ_TYPE_EDGE_RISING>;
+		};
+	};
+};
+
+&ipu1_csi0_from_ipu1_csi0_mux {
+        bus-width = <8>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&adv7180_to_ipu1_csi0_mux>;
+	inputs = <0x00 0x02>;
+	input-names = "ADV7180 Composite on Ain1", "ADV7180 Composite on Ain3";
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+
+/* input capture requires the input capture pin */
+&gpt {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_gpt_input_capture0>;
+};
+
+/* enabling input capture requires disabling SDHC1 */
+&usdhc1 {
+	status = "disabled";
+};
+
+
+
+SabreSD Quad with OV5642 and MIPI CSI-2 OV5640
+----------------------------------------------
+
+On the imx6q SabreSD, two camera sensors are supported: a parallel interface
+OV5642 on IPU1 CSI0, and a MIPI CSI-2 OV5640 on IPU1 CSI1 on MIPI virtual
+channel 1. The OV5642 connects to i2c bus 1 (i2c1) and the OV5640 to i2c
+bus 2 (i2c2).
+
+The mipi_csi2 receiver node must be enabled and its input endpoint connected
+via remote-endpoint to the OV5640 MIPI CSI-2 endpoint.
+
+OV5642 properties are as described above on SabreLite.
+
+OV5640 Required properties:
+- compatible	: "ovti,ov5640_mipi";
+- clocks        : the OV5640 system clock (cko, 201);
+- clock-names	: must be "xclk";
+- reg           : must be 0x3c;
+- xclk          : the system clock frequency, must be 24000000;
+- reset-gpios   : must be <&gpio1 20 GPIO_ACTIVE_LOW>;
+- pwdn-gpios    : must be <&gpio1 19 GPIO_ACTIVE_HIGH>;
+
+OV5640 Optional properties:
+- DOVDD-supply  : DOVDD regulator supply;
+- AVDD-supply   : AVDD regulator supply;
+- DVDD-supply   : DVDD regulator supply;
+
+OV5640 MIPI CSI-2 Endpoint Required properties:
+- remote-endpoint : must connect to mipi_csi receiver input endpoint
+  		    (mipi_csi_from_mipi_sensor).
+- reg             : must be 1; /* virtual channel 1 */
+- data-lanes      : must be <0 1>;
+- clock-lanes     : must be <2>;
+
+
+The following is an example devicetree video capture configuration for
+SabreSD:
+
+/ {
+	ipucap0: ipucap@0 {
+		compatible = "fsl,imx-video-capture";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_csi0>;
+		ports = <&ipu1_csi0>, <&ipu1_csi1>;
+		status = "okay";
+	};
+};
+
+&i2c1 {
+	camera: ov5642@3c {
+		compatible = "ovti,ov5642";
+		clocks = <&clks 201>;
+		clock-names = "xclk";
+		reg = <0x3c>;
+		xclk = <24000000>;
+		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
+		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
+						rev B board is VGEN5 */
+		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
+		pwdn-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>;   /* SD1_DAT0 */
+		reset-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>; /* SD1_DAT1 */
+
+		port {
+			ov5642_to_ipu1_csi0_mux: endpoint {
+				remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+				bus-width = <8>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+			};
+		};
+	};
+};
+
+&i2c2 {
+	mipi_camera: ov5640@3c {
+		compatible = "ovti,ov5640_mipi";
+		reg = <0x3c>;
+		clocks = <&clks 201>;
+		clock-names = "xclk";
+		xclk = <24000000>;
+		DOVDD-supply = <&vgen4_reg>; /* 1.8v */
+		AVDD-supply = <&vgen5_reg>;  /* 2.8v, rev C board is VGEN3
+						rev B board is VGEN5 */
+		DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
+		pwdn-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; /* SD1_DAT2 */
+		reset-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; /* SD1_CLK */
+
+		port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			ov5640_to_mipi_csi: endpoint@1 {
+				reg = <1>; /* virtual channel 1 */
+				remote-endpoint = <&mipi_csi_from_mipi_sensor>;
+				data-lanes = <0 1>;
+				clock-lanes = <2>;
+			};
+		};
+	};
+};
+
+ipu1_csi0_from_ipu1_csi0_mux {
+	bus-width = <8>;
+	data-shift = <12>; /* Lines 19:12 used */
+	hsync-active = <1>;
+	vsync-active = <1>;
+};
+
+&ipu1_csi0_mux_from_parallel_sensor {
+	remote-endpoint = <&ov5642_to_ipu1_csi0_mux>;
+};
+
+&ipu1_csi0_mux {
+	status = "okay";
+};
+
+&mipi_csi {
+	status = "okay";
+};
+
+/* Incoming port from sensor */
+&mipi_csi_from_mipi_sensor {
+	remote-endpoint = <&ov5640_to_mipi_csi>;
+	data-lanes = <0 1>;
+	clock-lanes = <2>;
+};
+
+&ipu1_csi1_from_mipi_vc1 {
+	data-lanes = <0 1>;
+	clock-lanes = <2>;
+};
diff --git a/Documentation/video4linux/imx_camera.txt b/Documentation/video4linux/imx_camera.txt
new file mode 100644
index 0000000..67cd1ce
--- /dev/null
+++ b/Documentation/video4linux/imx_camera.txt
@@ -0,0 +1,243 @@
+                         i.MX Video Capture Driver
+                         ==========================
+
+Introduction
+------------
+
+The Freescale i.MX5/6 contains an Image Processing Unit (IPU), which
+handles the flow of image frames to and from capture devices and
+display devices.
+
+For image capture, the IPU contains the following subunits:
+
+- Image DMA Controller (IDMAC)
+- Camera Serial Interface (CSI)
+- Image Converter (IC)
+- Sensor Multi-FIFO Controller (SMFC)
+- Image Rotator (IRT)
+- Video De-Interlace Controller (VDIC)
+
+The IDMAC is the DMA controller for transfer of image frames to and from
+memory. Various dedicated DMA channels exist for both video capture and
+display paths.
+
+The CSI is the frontend capture unit that interfaces directly with
+capture devices over Parallel, BT.656, and MIPI CSI-2 busses.
+
+The IC handles color-space conversion, resizing, and rotation
+operations.
+
+The SMFC is used to send image frames directly to memory, bypassing the
+IC. The SMFC is used when no color-space conversion or resizing is
+required, i.e. the requested V4L2 formats and color-space are identical
+to raw frames from the capture device.
+
+The IRT carries out 90 and 270 degree image rotation operations.
+
+Finally, the VDIC handles the conversion of interlaced video to
+progressive, with support for different motion compensation modes (low
+and high).
+
+For more info, refer to the latest versions of the i.MX5/6 reference
+manuals listed under References.
+
+
+Features
+--------
+
+Some of the features of this driver include:
+
+- Supports parallel, BT.565, and MIPI CSI-2 interfaces.
+
+- Multiple subdev sensors can be registered and controlled by a single
+  interface driver instance. Input enumeration will list every registered
+  sensor's inputs and input names, and setting an input will switch to
+  a different sensor if the input index is handled by a different sensor.
+
+- Simultaneous streaming from two separate sensors is possible with two
+  interface driver instances, each instance controlling a different
+  sensor. This is currently possible with the SabreSD reference board
+  with OV5642 and MIPI CSI-2 OV5640 sensors.
+
+- Scaling, color-space conversion, and image rotation.
+
+- Many pixel formats supported (RGB, packed and planar YUV, partial
+  planar YUV).
+
+- Full device-tree support using OF graph bindings.
+
+- Analog decoder input video source hot-swap support (during streaming)
+  via decoder status change subdev notification.
+
+- MMAP, and DMABUF importer/exporter buffers supported.
+
+- Motion compensated de-interlacing using the VDIC, with three
+  motion compensation modes: low, medium, and high motion. The mode is
+  specified with a custom control.
+
+- Includes a Frame Interval Monitor (FIM) that can correct vertical sync
+  problems with the ADV718x video decoders. See below for a description
+  of the FIM.
+
+
+Usage Notes
+-----------
+
+The i.MX capture driver is a standardized driver that supports the
+following community V4L2 tools:
+
+- v4l2-ctl
+- v4l2-cap
+- v4l2src gstreamer plugin
+
+
+The following platforms have been tested:
+
+
+SabreLite with parallel-interface OV5642
+----------------------------------------
+
+This platform requires the OmniVision OV5642 module with a parallel
+camera interface from Boundary Devices for the SabreLite
+(http://boundarydevices.com/products/nit6x_5mp/).
+
+There is a pin conflict between OV5642 and ethernet devices on this
+platform, so by default video capture is disabled in the device tree. To
+enable video capture, edit arch/arm/boot/dts/imx6qdl-sabrelite.dtsi and
+uncomment the macro __OV5642_CAPTURE__.
+
+
+SabreAuto with ADV7180 decoder
+------------------------------
+
+This platform accepts Composite Video analog inputs on Ain1 (connector
+J42) and Ain3 (connector J43).
+
+To switch to Ain1:
+
+# v4l2-ctl -i0
+
+To switch to Ain3:
+
+# v4l2-ctl -i1
+
+
+Frame Interval Monitor
+----------------------
+
+The adv718x decoders can occasionally send corrupt fields during
+NTSC/PAL signal re-sync (too little or too many video lines). When
+this happens, the IPU triggers a mechanism to re-establish vertical
+sync by adding 1 dummy line every frame, which causes a rolling effect
+from image to image, and can last a long time before a stable image is
+recovered. Or sometimes the mechanism doesn't work at all, causing a
+permanent split image (one frame contains lines from two consecutive
+captured images).
+
+From experiment it was found that during image rolling, the frame
+intervals (elapsed time between two EOF's) drop below the nominal
+value for the current standard, by about one frame time (60 usec),
+and remain at that value until rolling stops.
+
+While the reason for this observation isn't known (the IPU dummy
+line mechanism should show an increase in the intervals by 1 line
+time every frame, not a fixed value), we can use it to detect the
+corrupt fields using a frame interval monitor. If the FIM detects a
+bad frame interval, the camera interface driver restarts IPU capture
+which corrects the rolling/split image.
+
+Custom controls exist to tweak some dials for FIM. If one of these
+controls is changed during streaming, the FIM will be reset and will
+continue at the new settings.
+
+- V4L2_CID_IMX_FIM_ENABLE
+
+Enable/disable the FIM.
+
+- V4L2_CID_IMX_FIM_NUM
+
+How many frame interval errors to average before comparing against the nominal
+frame interval reported by the sensor. This can reduce noise from interrupt
+latency.
+
+- V4L2_CID_IMX_FIM_TOLERANCE_MIN
+
+If the averaged intervals fall outside nominal by this amount, in
+microseconds, streaming will be restarted.
+
+- V4L2_CID_IMX_FIM_TOLERANCE_MAX
+
+If any interval errors are higher than this value, those error samples
+are discarded and do not enter into the average. This can be used to
+discard really high interval errors that might be due to very high
+system load, causing excessive interrupt latencies.
+
+- V4L2_CID_IMX_FIM_NUM_SKIP
+
+How many frames to skip after a FIM reset or stream restart before
+FIM begins to average intervals. It has been found that there are
+always a few bad frame intervals after stream restart, so this is
+used to skip those frames to prevent endless restarts.
+
+Finally, all the defaults for these controls can be modified via a
+device tree child node of the capture node, see
+Documentation/devicetree/bindings/media/imx.txt.
+
+
+SabreSD with MIPI CSI-2 OV5640
+------------------------------
+
+The default device tree for SabreSD includes endpoints for both the
+parallel OV5642 and the MIPI CSI-2 OV5640, but as of this writing only
+the MIPI CSI-2 OV5640 has been tested. The OV5640 module connects to
+MIPI connector J5 (sorry I don't have the compatible module part number
+or URL).
+
+Inputs are registered for both the OV5642 and OV5640, and by default the
+OV5642 is selected. To switch to the OV5640:
+
+# v4l2-ctl -i1
+
+
+Known Issues
+------------
+
+1. When using 90 or 270 degree rotation control at capture resolutions
+   near the IC resizer limit of 1024x1024, and combined with planar
+   pixel formats (YUV420, YUV422p), frame capture will often fail with
+   no end-of-frame interrupts from the IDMAC channel. To work around
+   this, use lower resolution and/or packed formats (YUYV, RGB3, etc.)
+   when 90 or 270 rotations are needed.
+
+2. Simple IDMAC interleaving using the ILO field in the IDMAC cpmem
+   doesn't work when combined with the 16-bit planar pixel formats
+   (YUV422P and NV16). This looks like a silicon bug, and there is
+   no satisfactory replies to queries about it from Freescale. So
+   the driver works around the issue by forcing the format to the
+   12-bit planar versions (YUV420 and NV12) when simple interleaving
+   is used and the sensor sends interlaced fields (ADV718x). Another
+   option to workaround the issue is to use motion compensation when
+   combined with YUV422P or NV16.
+
+File list
+---------
+
+drivers/staging/media/imx/capture/
+include/media/imx.h
+include/uapi/media/imx.h
+
+References
+----------
+
+[1] "i.MX 6Dual/6Quad Applications Processor Reference Manual"
+[2] "i.MX 6Solo/6DualLite Applications Processor Reference Manual"
+
+
+Authors
+-------
+Steve Longerbeam <steve_longerbeam@mentor.com>
+Dmitry Eremin-Solenikov <dmitry_eremin@mentor.com>
+Jiada Wang <jiada_wang@mentor.com>
+Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
+
+Copyright (C) 2012-2016 Mentor Graphics Inc.
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index ee91868..1ebcd1d 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -25,6 +25,8 @@ source "drivers/staging/media/cxd2099/Kconfig"
 
 source "drivers/staging/media/davinci_vpfe/Kconfig"
 
+source "drivers/staging/media/imx/Kconfig"
+
 source "drivers/staging/media/omap4iss/Kconfig"
 
 source "drivers/staging/media/tw686x-kh/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 8c05d0a..9f00aea 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_I2C_BCM2048)	+= bcm2048/
 obj-$(CONFIG_DVB_CXD2099)	+= cxd2099/
+obj-$(CONFIG_VIDEO_IMX)		+= imx/
 obj-$(CONFIG_LIRC_STAGING)	+= lirc/
 obj-$(CONFIG_VIDEO_DM365_VPFE)	+= davinci_vpfe/
 obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
new file mode 100644
index 0000000..65e1645
--- /dev/null
+++ b/drivers/staging/media/imx/Kconfig
@@ -0,0 +1,23 @@
+config VIDEO_IMX
+	tristate "i.MX5/6 V4L2 devices"
+	depends on VIDEO_V4L2 && ARCH_MXC && IMX_IPUV3_CORE
+	default y
+	---help---
+	  Say yes here to enable support for video4linux drivers for
+	  the i.MX5/6 SOC.
+
+config VIDEO_IMX_CAMERA
+	tristate "i.MX5/6 Camera Interface driver"
+	depends on VIDEO_IMX && VIDEO_DEV && I2C
+	select VIDEOBUF2_DMA_CONTIG
+	default y
+	---help---
+	  A video4linux capture driver for i.MX5/6 SOC. Some of the
+	  features of this driver include MIPI CSI-2 sensor support,
+	  hardware scaling, colorspace conversion, and rotation,
+	  simultaneous capture from separate sensors, dmabuf
+	  importer/exporter, and full devicetree support.
+
+if VIDEO_IMX_CAMERA
+source "drivers/staging/media/imx/capture/Kconfig"
+endif
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
new file mode 100644
index 0000000..7c97629
--- /dev/null
+++ b/drivers/staging/media/imx/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_IMX_CAMERA) += capture/
diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
new file mode 100644
index 0000000..ee2cbab
--- /dev/null
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -0,0 +1,3 @@
+menu "i.MX5/6 Camera Sub devices"
+
+endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
new file mode 100644
index 0000000..5c965f9
--- /dev/null
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -0,0 +1,5 @@
+imx-camera-objs := imx-camif.o imx-ic-prpenc.o imx-of.o \
+		imx-smfc.o imx-vdic.o
+
+obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
+obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
diff --git a/drivers/staging/media/imx/capture/imx-camif.c b/drivers/staging/media/imx/capture/imx-camif.c
new file mode 100644
index 0000000..57fc3e8
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-camif.c
@@ -0,0 +1,2326 @@
+/*
+ * Video Camera Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/mxc_icap.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+#include "imx-of.h"
+
+/*
+ * Min/Max supported width and heights.
+ */
+#define MIN_W       176
+#define MIN_H       144
+#define MAX_W      8192
+#define MAX_H      4096
+#define MAX_W_IC   1024
+#define MAX_H_IC   1024
+#define MAX_W_VDIC  968
+#define MAX_H_VDIC 2048
+
+#define H_ALIGN    3 /* multiple of 8 */
+#define S_ALIGN    1 /* multiple of 2 */
+
+#define DEVICE_NAME "imx-camera"
+
+/* In bytes, per queue */
+#define VID_MEM_LIMIT	SZ_64M
+
+static struct vb2_ops imxcam_qops;
+
+static inline struct imxcam_dev *sd2dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd->v4l2_dev, struct imxcam_dev, v4l2_dev);
+}
+
+static inline struct imxcam_dev *notifier2dev(struct v4l2_async_notifier *n)
+{
+	return container_of(n, struct imxcam_dev, subdev_notifier);
+}
+
+static inline struct imxcam_dev *fim2dev(struct imxcam_fim *fim)
+{
+	return container_of(fim, struct imxcam_dev, fim);
+}
+
+/* forward references */
+static void imxcam_bump_restart_timer(struct imxcam_dev *dev);
+
+/* Supported user and sensor pixel formats */
+static struct imxcam_pixfmt imxcam_pixformats[] = {
+	{
+		.name	= "RGB565",
+		.fourcc	= V4L2_PIX_FMT_RGB565,
+		.codes  = {MEDIA_BUS_FMT_RGB565_2X8_LE},
+		.bpp    = 16,
+	}, {
+		.name	= "RGB24",
+		.fourcc	= V4L2_PIX_FMT_RGB24,
+		.codes  = {MEDIA_BUS_FMT_RGB888_1X24,
+			   MEDIA_BUS_FMT_RGB888_2X12_LE},
+		.bpp    = 24,
+	}, {
+		.name	= "BGR24",
+		.fourcc	= V4L2_PIX_FMT_BGR24,
+		.bpp    = 24,
+	}, {
+		.name	= "RGB32",
+		.fourcc	= V4L2_PIX_FMT_RGB32,
+		.codes = {MEDIA_BUS_FMT_ARGB8888_1X32},
+		.bpp   = 32,
+	}, {
+		.name	= "BGR32",
+		.fourcc	= V4L2_PIX_FMT_BGR32,
+		.bpp    = 32,
+	}, {
+		.name	= "4:2:2 packed, YUYV",
+		.fourcc	= V4L2_PIX_FMT_YUYV,
+		.codes = {MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_1X16},
+		.bpp   = 16,
+	}, {
+		.name	= "4:2:2 packed, UYVY",
+		.fourcc	= V4L2_PIX_FMT_UYVY,
+		.codes = {MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_1X16},
+		.bpp   = 16,
+	}, {
+		.name	= "4:2:0 planar, YUV",
+		.fourcc	= V4L2_PIX_FMT_YUV420,
+		.bpp    = 12,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:0 planar, YVU",
+		.fourcc = V4L2_PIX_FMT_YVU420,
+		.bpp    = 12,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:2 planar, YUV",
+		.fourcc = V4L2_PIX_FMT_YUV422P,
+		.bpp    = 16,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:0 planar, Y/CbCr",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.bpp    = 12,
+		.y_depth = 8,
+	}, {
+		.name   = "4:2:2 planar, Y/CbCr",
+		.fourcc = V4L2_PIX_FMT_NV16,
+		.bpp    = 16,
+		.y_depth = 8,
+	},
+};
+
+#define NUM_FORMATS ARRAY_SIZE(imxcam_pixformats)
+
+static struct imxcam_pixfmt *imxcam_get_format(u32 fourcc, u32 code)
+{
+	struct imxcam_pixfmt *fmt, *ret = NULL;
+	int i, j;
+
+	for (i = 0; i < NUM_FORMATS; i++) {
+		fmt = &imxcam_pixformats[i];
+
+		if (fourcc && fmt->fourcc == fourcc) {
+			ret = fmt;
+			goto out;
+		}
+
+		for (j = 0; fmt->codes[j]; j++) {
+			if (fmt->codes[j] == code) {
+				ret = fmt;
+				goto out;
+			}
+		}
+	}
+out:
+	return ret;
+}
+
+/* Support functions */
+
+/* find the sensor that is handling this input index */
+static struct imxcam_sensor *
+find_sensor_by_input_index(struct imxcam_dev *dev, int input_idx)
+{
+	struct imxcam_sensor *sensor;
+	int i;
+
+	for (i = 0; i < dev->num_sensors; i++) {
+		sensor = &dev->sensor_list[i];
+		if (!sensor->sd)
+			continue;
+
+		if (input_idx >= sensor->input.first &&
+		    input_idx <= sensor->input.last)
+			break;
+	}
+
+	return (i < dev->num_sensors) ? sensor : NULL;
+}
+
+/*
+ * Set all the video muxes required to receive data from the
+ * current sensor.
+ */
+static int imxcam_set_video_muxes(struct imxcam_dev *dev)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+	int i, ret;
+
+	for (i = 0; i < IMXCAM_MAX_VIDEOMUX; i++) {
+		if (sensor->vidmux_input[i] < 0)
+			continue;
+		dev_dbg(dev->dev, "%s: vidmux %d, input %d\n",
+			sensor->sd->name, i, sensor->vidmux_input[i]);
+		ret = v4l2_subdev_call(dev->vidmux_list[i], video, s_routing,
+				       sensor->vidmux_input[i], 0, 0);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Query sensor and update signal lock status. Returns true if lock
+ * status has changed.
+ */
+static bool update_signal_lock_status(struct imxcam_dev *dev)
+{
+	bool locked, changed;
+	u32 status;
+	int ret;
+
+	ret = v4l2_subdev_call(dev->sensor->sd, video, g_input_status, &status);
+	if (ret)
+		return false;
+
+	locked = ((status & (V4L2_IN_ST_NO_SIGNAL | V4L2_IN_ST_NO_SYNC)) == 0);
+	changed = (dev->signal_locked != locked);
+	dev->signal_locked = locked;
+
+	return changed;
+}
+
+/*
+ * Return true if the VDIC deinterlacer is needed. We need the VDIC
+ * if the sensor is transmitting fields, and userland is requesting
+ * motion compensation (rather than simple weaving).
+ */
+static bool need_vdic(struct imxcam_dev *dev,
+		      struct v4l2_mbus_framefmt *sf)
+{
+	return dev->motion != MOTION_NONE && V4L2_FIELD_HAS_BOTH(sf->field);
+}
+
+/*
+ * Return true if sensor format currently meets the VDIC
+ * restrictions:
+ *     o the full-frame resolution to the VDIC must be at or below 968x2048.
+ *     o the pixel format to the VDIC must be YUV422
+ */
+static bool can_use_vdic(struct imxcam_dev *dev,
+			 struct v4l2_mbus_framefmt *sf)
+{
+	return sf->width <= MAX_W_VDIC &&
+		sf->height <= MAX_H_VDIC &&
+		(sf->code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+		 sf->code == MEDIA_BUS_FMT_UYVY8_1X16 ||
+		 sf->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+		 sf->code == MEDIA_BUS_FMT_YUYV8_1X16);
+}
+
+/*
+ * Return true if the current capture parameters require the use of
+ * the Image Converter. We need the IC for scaling, colorspace conversion,
+ * and rotation.
+ */
+static bool need_ic(struct imxcam_dev *dev,
+		    struct v4l2_mbus_framefmt *sf,
+		    struct v4l2_format *uf,
+		    struct v4l2_rect *crop)
+{
+	struct v4l2_pix_format *user_fmt = &uf->fmt.pix;
+	enum ipu_color_space sensor_cs, user_cs;
+	bool ret;
+
+	sensor_cs = ipu_mbus_code_to_colorspace(sf->code);
+	user_cs = ipu_pixelformat_to_colorspace(user_fmt->pixelformat);
+
+	ret = (user_fmt->width != crop->width ||
+	       user_fmt->height != crop->height ||
+	       user_cs != sensor_cs ||
+	       dev->rot_mode != IPU_ROTATE_NONE);
+
+	return ret;
+}
+
+/*
+ * Return true if user and sensor formats currently meet the IC
+ * restrictions:
+ *     o the parallel CSI bus cannot be 16-bit wide.
+ *     o the endpoint id of the CSI this sensor connects to must be 0
+ *       (for MIPI CSI2, the endpoint id is the virtual channel number,
+ *        and only VC0 can pass through the IC).
+ *     o the resizer output size must be at or below 1024x1024.
+ */
+static bool can_use_ic(struct imxcam_dev *dev,
+		       struct v4l2_mbus_framefmt *sf,
+		       struct v4l2_format *uf)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+
+	return (sensor->ep.bus_type == V4L2_MBUS_CSI2 ||
+		sensor->ep.bus.parallel.bus_width < 16) &&
+		sensor->csi_ep.base.id == 0 &&
+		uf->fmt.pix.width <= MAX_W_IC &&
+		uf->fmt.pix.height <= MAX_H_IC;
+}
+
+/*
+ * Adjusts passed width and height to meet IC resizer limits.
+ */
+static void adjust_to_resizer_limits(struct imxcam_dev *dev,
+				     struct v4l2_format *uf,
+				     struct v4l2_rect *crop)
+{
+	u32 *width, *height;
+
+	if (uf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		width = &uf->fmt.pix.width;
+		height = &uf->fmt.pix.height;
+	} else {
+		width = &uf->fmt.win.w.width;
+		height = &uf->fmt.win.w.height;
+	}
+
+	/* output of resizer can't be above 1024x1024 */
+	*width = min_t(__u32, *width, MAX_W_IC);
+	*height = min_t(__u32, *height, MAX_H_IC);
+
+	/* resizer cannot downsize more than 4:1 */
+	if (ipu_rot_mode_is_irt(dev->rot_mode)) {
+		*height = max_t(__u32, *height, crop->width / 4);
+		*width = max_t(__u32, *width, crop->height / 4);
+	} else {
+		*width = max_t(__u32, *width, crop->width / 4);
+		*height = max_t(__u32, *height, crop->height / 4);
+	}
+}
+
+static void adjust_user_fmt(struct imxcam_dev *dev,
+			    struct v4l2_mbus_framefmt *sf,
+			    struct v4l2_format *uf,
+			    struct v4l2_rect *crop)
+{
+	struct imxcam_pixfmt *fmt;
+
+	/*
+	 * Make sure resolution is within IC resizer limits
+	 * if we need the Image Converter.
+	 */
+	if (need_ic(dev, sf, uf, crop))
+		adjust_to_resizer_limits(dev, uf, crop);
+
+	/*
+	 * Force the resolution to match crop window if
+	 * we can't use the Image Converter.
+	 */
+	if (!can_use_ic(dev, sf, uf)) {
+		uf->fmt.pix.width = crop->width;
+		uf->fmt.pix.height = crop->height;
+	}
+
+	fmt = imxcam_get_format(uf->fmt.pix.pixelformat, 0);
+
+	uf->fmt.pix.bytesperline = (uf->fmt.pix.width * fmt->bpp) >> 3;
+	uf->fmt.pix.sizeimage = uf->fmt.pix.height * uf->fmt.pix.bytesperline;
+}
+
+/*
+ * calculte the default active crop window, given a sensor frame and
+ * video standard. This crop window will be stored to dev->crop_defrect.
+ */
+static void calc_default_crop(struct imxcam_dev *dev,
+			      struct v4l2_rect *rect,
+			      struct v4l2_mbus_framefmt *sf,
+			      v4l2_std_id std)
+{
+	rect->width = sf->width;
+	rect->height = sf->height;
+	rect->top = 0;
+	rect->left = 0;
+
+	/*
+	 * FIXME: For NTSC standards, top must be set to an
+	 * offset of 13 lines to match fixed CCIR programming
+	 * in the IPU.
+	 */
+	if (std != V4L2_STD_UNKNOWN && (std & V4L2_STD_525_60))
+		rect->top = 13;
+
+	/* adjust crop window to h/w alignment restrictions */
+	rect->width &= ~0x7;
+}
+
+static int update_sensor_std(struct imxcam_dev *dev)
+{
+	return v4l2_subdev_call(dev->sensor->sd, video, querystd,
+				&dev->current_std);
+}
+
+static void update_fim(struct imxcam_dev *dev)
+{
+	struct imxcam_fim *fim = &dev->fim;
+
+	if (dev->sensor_tpf.denominator == 0) {
+		fim->enabled = false;
+		return;
+	}
+
+	fim->nominal = DIV_ROUND_CLOSEST(
+		1000 * 1000 * dev->sensor_tpf.numerator,
+		dev->sensor_tpf.denominator);
+}
+
+static int update_sensor_fmt(struct imxcam_dev *dev)
+{
+	struct v4l2_subdev_format fmt;
+	struct v4l2_streamparm parm;
+	struct v4l2_rect crop;
+	int ret;
+
+	update_sensor_std(dev);
+
+	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	fmt.pad = 0;
+
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, get_fmt, NULL, &fmt);
+	if (ret)
+		return ret;
+
+	dev->sensor_fmt = fmt.format;
+
+	parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	ret = v4l2_subdev_call(dev->sensor->sd, video, g_parm, &parm);
+	if (ret)
+		memset(&dev->sensor_tpf, 0, sizeof(dev->sensor_tpf));
+	else
+		dev->sensor_tpf = parm.parm.capture.timeperframe;
+	update_fim(dev);
+
+	ret = v4l2_subdev_call(dev->sensor->sd, video, g_mbus_config,
+			       &dev->mbus_cfg);
+	if (ret)
+		return ret;
+
+	dev->sensor_pixfmt = imxcam_get_format(0, dev->sensor_fmt.code);
+
+	/* get new sensor default crop window */
+	calc_default_crop(dev, &crop, &dev->sensor_fmt, dev->current_std);
+
+	/* and update crop bounds */
+	dev->crop_bounds.top = dev->crop_bounds.left = 0;
+	dev->crop_bounds.width = crop.width + (u32)crop.left;
+	dev->crop_bounds.height = crop.height + (u32)crop.top;
+
+	/*
+	 * reset the user crop window to defrect if defrect has changed,
+	 * or if user crop is not initialized yet.
+	 */
+	if (dev->crop_defrect.width != crop.width ||
+	    dev->crop_defrect.left != crop.left ||
+	    dev->crop_defrect.height != crop.height ||
+	    dev->crop_defrect.top != crop.top ||
+	    !dev->crop.width || !dev->crop.height) {
+		dev->crop_defrect = crop;
+		dev->crop = dev->crop_defrect;
+	}
+
+	return 0;
+}
+
+/*
+ * Turn current sensor power on/off according to power_count.
+ */
+static int sensor_set_power(struct imxcam_dev *dev, int on)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+	struct v4l2_subdev *sd = sensor->sd;
+	int ret;
+
+	if (on && sensor->power_count++ > 0)
+		return 0;
+	else if (!on && (sensor->power_count == 0 ||
+			 --sensor->power_count > 0))
+		return 0;
+
+	if (on) {
+		/* power-on the csi2 receiver */
+		if (sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd) {
+			ret = v4l2_subdev_call(dev->csi2_sd, core, s_power,
+					       true);
+			if (ret)
+				goto out;
+		}
+
+		ret = v4l2_subdev_call(sd, core, s_power, true);
+		if (ret && ret != -ENOIOCTLCMD)
+			goto csi2_off;
+	} else {
+		v4l2_subdev_call(sd, core, s_power, false);
+		if (sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+			v4l2_subdev_call(dev->csi2_sd, core, s_power, false);
+	}
+
+	return 0;
+
+csi2_off:
+	if (sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+		v4l2_subdev_call(dev->csi2_sd, core, s_power, false);
+out:
+	sensor->power_count--;
+	return ret;
+}
+
+static void reset_fim(struct imxcam_dev *dev, bool curval)
+{
+	struct imxcam_fim *fim = &dev->fim;
+	struct v4l2_ctrl *en = fim->ctrl[FIM_CL_ENABLE];
+	struct v4l2_ctrl *num = fim->ctrl[FIM_CL_NUM];
+	struct v4l2_ctrl *skip = fim->ctrl[FIM_CL_NUM_SKIP];
+	struct v4l2_ctrl *tol_min = fim->ctrl[FIM_CL_TOLERANCE_MIN];
+	struct v4l2_ctrl *tol_max = fim->ctrl[FIM_CL_TOLERANCE_MAX];
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	if (curval) {
+		fim->enabled = en->cur.val;
+		fim->num_avg = num->cur.val;
+		fim->num_skip = skip->cur.val;
+		fim->tolerance_min = tol_min->cur.val;
+		fim->tolerance_max = tol_max->cur.val;
+	} else {
+		fim->enabled = en->val;
+		fim->num_avg = num->val;
+		fim->num_skip = skip->val;
+		fim->tolerance_min = tol_min->val;
+		fim->tolerance_max = tol_max->val;
+	}
+
+	/* disable tolerance range if max <= min */
+	if (fim->tolerance_max <= fim->tolerance_min)
+		fim->tolerance_max = 0;
+
+	fim->counter = -fim->num_skip;
+	fim->sum = 0;
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Monitor an averaged frame interval. If the average deviates too much
+ * from the sensor's nominal frame rate, return -EIO. The frame intervals
+ * are averaged in order to quiet noise from (presumably random) interrupt
+ * latency.
+ */
+static int frame_interval_monitor(struct imxcam_fim *fim, struct timespec *ts)
+{
+	unsigned long interval, error, error_avg;
+	struct imxcam_dev *dev = fim2dev(fim);
+	struct timespec diff;
+	int ret = 0;
+
+	if (++fim->counter <= 0)
+		goto out_update_ts;
+
+	diff = timespec_sub(*ts, fim->last_ts);
+	interval = diff.tv_sec * 1000 * 1000 + diff.tv_nsec / 1000;
+	error = abs(interval - fim->nominal);
+
+	if (fim->tolerance_max && error >= fim->tolerance_max) {
+		dev_dbg(dev->dev,
+			"FIM: %lu ignored, out of tolerance bounds\n",
+			error);
+		fim->counter--;
+		goto out_update_ts;
+	}
+
+	fim->sum += error;
+
+	if (fim->counter == fim->num_avg) {
+		error_avg = DIV_ROUND_CLOSEST(fim->sum, fim->num_avg);
+
+		if (error_avg > fim->tolerance_min)
+			ret = -EIO;
+
+		dev_dbg(dev->dev, "FIM: error: %lu usec%s\n",
+			error_avg, ret ? " (!!!)" : "");
+
+		fim->counter = 0;
+		fim->sum = 0;
+	}
+
+out_update_ts:
+	fim->last_ts = *ts;
+	return ret;
+}
+
+/*
+ * Called by the encode and vdic subdevs in their EOF interrupt
+ * handlers with the irqlock held. This way of measuring frame
+ * intervals is subject to errors introduced by interrupt latency.
+ */
+static int fim_eof_handler(struct imxcam_dev *dev, struct timeval *now)
+{
+	struct imxcam_fim *fim = &dev->fim;
+	struct timespec ts;
+
+	if (!fim->enabled)
+		return 0;
+
+	ts.tv_sec = now->tv_sec;
+	ts.tv_nsec = now->tv_usec * 1000;
+
+	return frame_interval_monitor(fim, &ts);
+}
+
+/*
+ * Input Capture method of measuring frame intervals. Not subject
+ * to interrupt latency.
+ */
+static void fim_input_capture_handler(int channel, void *dev_id,
+				      struct timespec *now)
+{
+	struct imxcam_fim *fim = dev_id;
+	struct imxcam_dev *dev = fim2dev(fim);
+	unsigned long flags;
+
+	if (!fim->enabled)
+		return;
+
+	if (!frame_interval_monitor(fim, now))
+		return;
+
+	spin_lock_irqsave(&dev->notify_lock, flags);
+	if (!dev->stop && !atomic_read(&dev->pending_restart))
+		imxcam_bump_restart_timer(dev);
+	spin_unlock_irqrestore(&dev->notify_lock, flags);
+}
+
+static int fim_request_input_capture(struct imxcam_dev *dev)
+{
+	struct imxcam_fim *fim = &dev->fim;
+
+	if (fim->icap_channel < 0)
+		return 0;
+
+	return mxc_request_input_capture(fim->icap_channel,
+					 fim_input_capture_handler,
+					 fim->icap_flags, fim);
+}
+
+static void fim_free_input_capture(struct imxcam_dev *dev)
+{
+	struct imxcam_fim *fim = &dev->fim;
+
+	if (fim->icap_channel < 0)
+		return;
+
+	mxc_free_input_capture(fim->icap_channel, fim);
+}
+
+/*
+ * Turn current sensor and CSI streaming on/off according to stream_count.
+ */
+static int sensor_set_stream(struct imxcam_dev *dev, int on)
+{
+	struct imxcam_sensor *sensor = dev->sensor;
+	int ret;
+
+	if (on && sensor->stream_count++ > 0)
+		return 0;
+	else if (!on && (sensor->stream_count == 0 ||
+			 --sensor->stream_count > 0))
+		return 0;
+
+	if (on) {
+		ret = v4l2_subdev_call(sensor->sd, video, s_stream, true);
+		if (ret && ret != -ENOIOCTLCMD)
+			goto out;
+
+		if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd) {
+			ret = v4l2_subdev_call(dev->csi2_sd, video, s_stream,
+					       true);
+			if (ret)
+				goto sensor_off;
+		}
+
+		ret = v4l2_subdev_call(sensor->csi_sd, video, s_stream, true);
+		if (ret)
+			goto csi2_off;
+
+		ret = fim_request_input_capture(dev);
+		if (ret)
+			goto csi_off;
+	} else {
+		fim_free_input_capture(dev);
+		v4l2_subdev_call(sensor->csi_sd, video, s_stream, false);
+		if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+			v4l2_subdev_call(dev->csi2_sd, video, s_stream, false);
+		v4l2_subdev_call(sensor->sd, video, s_stream, false);
+	}
+
+	return 0;
+
+csi_off:
+	v4l2_subdev_call(sensor->csi_sd, video, s_stream, false);
+csi2_off:
+	if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2 && dev->csi2_sd)
+		v4l2_subdev_call(dev->csi2_sd, video, s_stream, false);
+sensor_off:
+	v4l2_subdev_call(sensor->sd, video, s_stream, false);
+out:
+	sensor->stream_count--;
+	return ret;
+}
+
+/*
+ * Start the encoder for buffer streaming. There must be at least two
+ * frames in the vb2 queue.
+ */
+static int start_encoder(struct imxcam_dev *dev)
+{
+	struct v4l2_subdev *streaming_sd;
+	int ret;
+
+	if (dev->encoder_on)
+		return 0;
+
+	if (dev->using_vdic)
+		streaming_sd = dev->vdic_sd;
+	else if (dev->using_ic)
+		streaming_sd = dev->prpenc_sd;
+	else
+		streaming_sd = dev->smfc_sd;
+
+	ret = v4l2_subdev_call(streaming_sd, video, s_stream, 1);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "encoder stream on failed\n");
+		return ret;
+	}
+
+	dev->encoder_on = true;
+	return 0;
+}
+
+/*
+ * Stop the encoder.
+ */
+static int stop_encoder(struct imxcam_dev *dev)
+{
+	struct v4l2_subdev *streaming_sd;
+	int ret;
+
+	if (!dev->encoder_on)
+		return 0;
+
+	if (dev->using_vdic)
+		streaming_sd = dev->vdic_sd;
+	else if (dev->using_ic)
+		streaming_sd = dev->prpenc_sd;
+	else
+		streaming_sd = dev->smfc_sd;
+
+	/* encoder/vdic off */
+	ret = v4l2_subdev_call(streaming_sd, video, s_stream, 0);
+	if (ret)
+		v4l2_err(&dev->v4l2_dev, "encoder stream off failed\n");
+
+	dev->encoder_on = false;
+	return ret;
+}
+
+/*
+ * Start/Stop streaming.
+ */
+static int set_stream(struct imxcam_dev *dev, bool on)
+{
+	int ret = 0;
+
+	if (on) {
+		if (atomic_read(&dev->status_change)) {
+			update_signal_lock_status(dev);
+			update_sensor_fmt(dev);
+			atomic_set(&dev->status_change, 0);
+			v4l2_info(&dev->v4l2_dev, "at stream on: %s, %s\n",
+				  v4l2_norm_to_name(dev->current_std),
+				  dev->signal_locked ?
+				  "signal locked" : "no signal");
+		}
+
+		atomic_set(&dev->pending_restart, 0);
+
+		dev->using_ic =
+			(need_ic(dev, &dev->sensor_fmt, &dev->user_fmt,
+				 &dev->crop) &&
+			 can_use_ic(dev, &dev->sensor_fmt, &dev->user_fmt));
+
+		dev->using_vdic = need_vdic(dev, &dev->sensor_fmt) &&
+			can_use_vdic(dev, &dev->sensor_fmt);
+
+		reset_fim(dev, true);
+
+		/*
+		 * If there are two or more frames in the queue, we can start
+		 * the encoder now. Otherwise the encoding will start once
+		 * two frames have been queued.
+		 */
+		if (!list_empty(&dev->ready_q) &&
+		    !list_is_singular(&dev->ready_q))
+			ret = start_encoder(dev);
+	} else {
+		ret = stop_encoder(dev);
+	}
+
+	return ret;
+}
+
+/*
+ * Restart work handler. This is called in three cases during active
+ * streaming.
+ *
+ * o NFB4EOF errors
+ * o A decoder's signal lock status or autodetected video standard changes
+ * o End-of-Frame timeouts
+ */
+static void restart_work_handler(struct work_struct *w)
+{
+	struct imxcam_dev *dev = container_of(w, struct imxcam_dev,
+					      restart_work);
+
+	mutex_lock(&dev->mutex);
+
+	if (!vb2_is_streaming(&dev->buffer_queue))
+		goto out_unlock;
+
+	if (!dev->stop) {
+		v4l2_warn(&dev->v4l2_dev, "restarting\n");
+		set_stream(dev, false);
+		set_stream(dev, true);
+	}
+
+out_unlock:
+	mutex_unlock(&dev->mutex);
+}
+
+/*
+ * Stop work handler. Not currently needed but keep around.
+ */
+static void stop_work_handler(struct work_struct *w)
+{
+	struct imxcam_dev *dev = container_of(w, struct imxcam_dev,
+					      stop_work);
+
+	mutex_lock(&dev->mutex);
+
+	if (vb2_is_streaming(&dev->buffer_queue)) {
+		v4l2_err(&dev->v4l2_dev, "stopping\n");
+		vb2_streamoff(&dev->buffer_queue, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	}
+
+	mutex_unlock(&dev->mutex);
+}
+
+/*
+ * Restart timer function. Schedules a restart.
+ */
+static void imxcam_restart_timeout(unsigned long data)
+{
+	struct imxcam_dev *dev = (struct imxcam_dev *)data;
+
+	schedule_work(&dev->restart_work);
+}
+
+/*
+ * bump the restart timer and set the pending restart flag.
+ * notify_lock must be held when calling.
+ */
+static void imxcam_bump_restart_timer(struct imxcam_dev *dev)
+{
+	mod_timer(&dev->restart_timer, jiffies +
+		  msecs_to_jiffies(IMXCAM_RESTART_DELAY));
+	atomic_set(&dev->pending_restart, 1);
+}
+
+/* Controls */
+static int imxcam_set_rotation(struct imxcam_dev *dev,
+			       int rotation, bool hflip, bool vflip)
+{
+	enum ipu_rotate_mode rot_mode;
+	int ret;
+
+	ret = ipu_degrees_to_rot_mode(&rot_mode, rotation,
+				      hflip, vflip);
+	if (ret)
+		return ret;
+
+	if (rot_mode != dev->rot_mode) {
+		/* can't change rotation mid-streaming */
+		if (vb2_is_streaming(&dev->buffer_queue)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: not allowed while streaming\n",
+				 __func__);
+			return -EBUSY;
+		}
+
+		if (rot_mode != IPU_ROTATE_NONE &&
+		    !can_use_ic(dev, &dev->sensor_fmt, &dev->user_fmt)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: current format does not allow rotation\n",
+				 __func__);
+			return -EINVAL;
+		}
+	}
+
+	dev->rot_mode = rot_mode;
+	dev->rotation = rotation;
+	dev->hflip = hflip;
+	dev->vflip = vflip;
+
+	return 0;
+}
+
+static int imxcam_set_motion(struct imxcam_dev *dev,
+			     enum ipu_motion_sel motion)
+{
+	if (motion != dev->motion) {
+		/* can't change motion setting mid-streaming */
+		if (vb2_is_streaming(&dev->buffer_queue)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "%s: not allowed while streaming\n",
+				 __func__);
+			return -EBUSY;
+		}
+
+		if (motion != MOTION_NONE &&
+		    !can_use_vdic(dev, &dev->sensor_fmt)) {
+			v4l2_err(&dev->v4l2_dev,
+				 "sensor format does not allow deinterlace\n");
+			return -EINVAL;
+		}
+	}
+
+	dev->motion = motion;
+	return 0;
+}
+
+static int imxcam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct imxcam_dev *dev = container_of(ctrl->handler,
+					      struct imxcam_dev, ctrl_hdlr);
+	enum ipu_motion_sel motion;
+	bool hflip, vflip;
+	int rotation;
+
+	rotation = dev->rotation;
+	hflip = dev->hflip;
+	vflip = dev->vflip;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		hflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_VFLIP:
+		vflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_ROTATE:
+		rotation = ctrl->val;
+		break;
+	case V4L2_CID_IMX_MOTION:
+		motion = ctrl->val;
+		return imxcam_set_motion(dev, motion);
+	case V4L2_CID_IMX_FIM_ENABLE:
+		reset_fim(dev, false);
+		return 0;
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+
+	return imxcam_set_rotation(dev, rotation, hflip, vflip);
+}
+
+static const struct v4l2_ctrl_ops imxcam_ctrl_ops = {
+	.s_ctrl = imxcam_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config imxcam_std_ctrl[] = {
+	{
+		.id = V4L2_CID_HFLIP,
+		.name = "Horizontal Flip",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.def =  0,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.id = V4L2_CID_VFLIP,
+		.name = "Vertical Flip",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.def =  0,
+		.min =  0,
+		.max =  1,
+		.step = 1,
+	}, {
+		.id = V4L2_CID_ROTATE,
+		.name = "Rotation",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def =   0,
+		.min =   0,
+		.max = 270,
+		.step = 90,
+	},
+};
+
+#define IMXCAM_NUM_STD_CONTROLS ARRAY_SIZE(imxcam_std_ctrl)
+
+static const struct v4l2_ctrl_config imxcam_custom_ctrl[] = {
+	{
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_MOTION,
+		.name = "Motion Compensation",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = MOTION_NONE,
+		.min = MOTION_NONE,
+		.max = HIGH_MOTION,
+		.step = 1,
+	},
+};
+
+#define IMXCAM_NUM_CUSTOM_CONTROLS ARRAY_SIZE(imxcam_custom_ctrl)
+
+static const struct v4l2_ctrl_config imxcam_fim_ctrl[] = {
+	[FIM_CL_ENABLE] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_ENABLE,
+		.name = "FIM Enable",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.def = FIM_CL_ENABLE_DEF,
+		.min = 0,
+		.max = 1,
+		.step = 1,
+	},
+	[FIM_CL_NUM] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_NUM,
+		.name = "FIM Num Average",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_NUM_DEF,
+		.min =  1, /* no averaging */
+		.max = 64, /* average 64 frames */
+		.step = 1,
+	},
+	[FIM_CL_TOLERANCE_MIN] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_TOLERANCE_MIN,
+		.name = "FIM Tolerance Min",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_TOLERANCE_MIN_DEF,
+		.min =    2,
+		.max =  200,
+		.step =   1,
+	},
+	[FIM_CL_TOLERANCE_MAX] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_TOLERANCE_MAX,
+		.name = "FIM Tolerance Max",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_TOLERANCE_MAX_DEF,
+		.min =    0,
+		.max =  500,
+		.step =   1,
+	},
+	[FIM_CL_NUM_SKIP] = {
+		.ops = &imxcam_ctrl_ops,
+		.id = V4L2_CID_IMX_FIM_NUM_SKIP,
+		.name = "FIM Num Skip",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.def = FIM_CL_NUM_SKIP_DEF,
+		.min =   1, /* skip 1 frame */
+		.max = 256, /* skip 256 frames */
+		.step =  1,
+	},
+};
+
+/*
+ * the adv7182 has the most controls with 27, so add 32
+ * on top of our own
+ */
+#define IMXCAM_NUM_CONTROLS (IMXCAM_NUM_STD_CONTROLS    + \
+			     IMXCAM_NUM_CUSTOM_CONTROLS + \
+			     FIM_NUM_CONTROLS + 32)
+
+static int imxcam_init_controls(struct imxcam_dev *dev)
+{
+	struct v4l2_ctrl_handler *hdlr = &dev->ctrl_hdlr;
+	struct imxcam_fim *fim = &dev->fim;
+	const struct v4l2_ctrl_config *c;
+	struct v4l2_ctrl_config fim_c;
+	int i, ret;
+
+	v4l2_ctrl_handler_init(hdlr, IMXCAM_NUM_CONTROLS);
+
+	for (i = 0; i < IMXCAM_NUM_STD_CONTROLS; i++) {
+		c = &imxcam_std_ctrl[i];
+
+		v4l2_ctrl_new_std(hdlr, &imxcam_ctrl_ops,
+				  c->id, c->min, c->max, c->step, c->def);
+	}
+
+	for (i = 0; i < IMXCAM_NUM_CUSTOM_CONTROLS; i++) {
+		c = &imxcam_custom_ctrl[i];
+
+		v4l2_ctrl_new_custom(hdlr, c, NULL);
+	}
+
+	for (i = 0; i < FIM_NUM_CONTROLS; i++) {
+		fim_c = imxcam_fim_ctrl[i];
+		fim_c.def = fim->of_defaults[i];
+		fim->ctrl[i] = v4l2_ctrl_new_custom(hdlr, &fim_c, NULL);
+	}
+
+	if (hdlr->error) {
+		ret = hdlr->error;
+		v4l2_ctrl_handler_free(hdlr);
+		return ret;
+	}
+
+	v4l2_ctrl_cluster(FIM_NUM_CONTROLS, fim->ctrl);
+
+	dev->v4l2_dev.ctrl_handler = hdlr;
+	dev->vfd->ctrl_handler = hdlr;
+
+	return 0;
+}
+
+/*
+ * Video ioctls follow
+ */
+
+static int vidioc_querycap(struct file *file, void *priv,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, DEVICE_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, DEVICE_NAME, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int imxcam_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct imxcam_pixfmt *fmt;
+
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+
+	fmt = &imxcam_pixformats[f->index];
+	strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+	f->pixelformat = fmt->fourcc;
+	return 0;
+}
+
+static int imxcam_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+
+	f->fmt.pix = dev->user_fmt.fmt.pix;
+	return 0;
+}
+
+static int imxcam_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct v4l2_subdev_pad_config pad_cfg;
+	struct v4l2_subdev_format format;
+	struct imxcam_pixfmt *fmt;
+	unsigned int width_align;
+	struct v4l2_rect crop;
+	int ret;
+
+	fmt = imxcam_get_format(f->fmt.pix.pixelformat, 0);
+	if (!fmt) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Fourcc format (0x%08x) invalid.\n",
+			 f->fmt.pix.pixelformat);
+		return -EINVAL;
+	}
+
+	/*
+	 * simple IDMAC interleaving using ILO field doesn't work
+	 * when combined with the 16-bit planar formats (YUV422P
+	 * and NV16). This looks like a silicon bug, no satisfactory
+	 * replies to queries about it from Freescale. So workaround
+	 * the issue by forcing the formats to the 12-bit planar versions.
+	 */
+	if (V4L2_FIELD_HAS_BOTH(dev->sensor_fmt.field) &&
+	    dev->motion == MOTION_NONE) {
+		switch (fmt->fourcc) {
+		case V4L2_PIX_FMT_YUV422P:
+			v4l2_info(&dev->v4l2_dev,
+				  "ILO workaround: YUV422P forced to YUV420\n");
+			f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+			break;
+		case V4L2_PIX_FMT_NV16:
+			v4l2_info(&dev->v4l2_dev,
+				  "ILO workaround: NV16 forced to NV12\n");
+			f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
+			break;
+		default:
+			break;
+		}
+		fmt = imxcam_get_format(f->fmt.pix.pixelformat, 0);
+	}
+
+	/*
+	 * We have to adjust the width such that the physaddrs and U and
+	 * U and V plane offsets are multiples of 8 bytes as required by
+	 * the IPU DMA Controller. For the planar formats, this corresponds
+	 * to a pixel alignment of 16. For all the packed formats, 8 is
+	 * good enough.
+	 *
+	 * For height alignment, we have to ensure that the heights
+	 * are multiples of 8 lines, to satisfy the requirement of the
+	 * IRT (the IRT performs rotations on 8x8 blocks at a time).
+	 */
+	width_align = ipu_pixelformat_is_planar(fmt->fourcc) ? 4 : 3;
+
+	v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+			      width_align, &f->fmt.pix.height,
+			      MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+
+	format.which = V4L2_SUBDEV_FORMAT_TRY;
+	format.pad = 0;
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, 0);
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, set_fmt, &pad_cfg, &format);
+	if (ret)
+		return ret;
+
+	fmt = imxcam_get_format(0, pad_cfg.try_fmt.code);
+	if (!fmt) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Sensor mbus format (0x%08x) invalid\n",
+			 pad_cfg.try_fmt.code);
+		return -EINVAL;
+	}
+
+	/*
+	 * calculate what the optimal crop window will be for this
+	 * sensor format and make any user format adjustments.
+	 */
+	calc_default_crop(dev, &crop, &pad_cfg.try_fmt, dev->current_std);
+	adjust_user_fmt(dev, &pad_cfg.try_fmt, f, &crop);
+
+	/* this driver only delivers progressive frames to userland */
+	f->fmt.pix.field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int imxcam_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct v4l2_subdev_format format;
+	int ret;
+
+	if (vb2_is_busy(&dev->buffer_queue)) {
+		v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+		return -EBUSY;
+	}
+
+	ret = imxcam_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	format.pad = 0;
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, 0);
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, set_fmt, NULL, &format);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "%s set_fmt failed\n", __func__);
+		return ret;
+	}
+
+	ret = update_sensor_fmt(dev);
+	if (ret)
+		return ret;
+
+	dev->user_fmt = *f;
+	dev->user_pixfmt = imxcam_get_format(f->fmt.pix.pixelformat, 0);
+
+	return 0;
+}
+
+static int imxcam_enum_framesizes(struct file *file, void *priv,
+				  struct v4l2_frmsizeenum *fsize)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct imxcam_pixfmt *fmt;
+	struct v4l2_format uf;
+
+	fmt = imxcam_get_format(fsize->pixel_format, 0);
+	if (!fmt)
+		return -EINVAL;
+
+	if (fsize->index)
+		return -EINVAL;
+
+	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+	fsize->stepwise.min_width = MIN_W;
+	fsize->stepwise.step_width =
+		ipu_pixelformat_is_planar(fmt->fourcc) ? 16 : 8;
+	fsize->stepwise.min_height = MIN_H;
+	fsize->stepwise.step_height = 1 << H_ALIGN;
+
+	uf = dev->user_fmt;
+	uf.fmt.pix.pixelformat = fmt->fourcc;
+
+	if (need_ic(dev, &dev->sensor_fmt, &uf, &dev->crop)) {
+		fsize->stepwise.max_width = MAX_W_IC;
+		fsize->stepwise.max_height = MAX_H_IC;
+	} else {
+		fsize->stepwise.max_width = MAX_W;
+		fsize->stepwise.max_height = MAX_H;
+	}
+
+	return 0;
+}
+
+static int imxcam_enum_frameintervals(struct file *file, void *priv,
+				      struct v4l2_frmivalenum *fival)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct imxcam_pixfmt *fmt;
+	struct v4l2_subdev_frame_interval_enum fie = {
+		.index = fival->index,
+		.pad = 0,
+		.width = fival->width,
+		.height = fival->height,
+		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
+	};
+	int ret;
+
+	fmt = imxcam_get_format(fival->pixel_format, 0);
+	if (!fmt)
+		return -EINVAL;
+
+	fie.code = fmt->codes[0];
+
+	ret = v4l2_subdev_call(dev->sensor->sd, pad, enum_frame_interval,
+			       NULL, &fie);
+	if (ret)
+		return ret;
+
+	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+	fival->discrete = fie.interval;
+	return 0;
+}
+
+static int imxcam_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	int ret;
+
+	ret = update_sensor_std(dev);
+	if (!ret)
+		*std = dev->current_std;
+	return ret;
+}
+
+static int imxcam_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+
+	*std = dev->current_std;
+	return 0;
+}
+
+static int imxcam_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	int ret;
+
+	if (vb2_is_busy(&dev->buffer_queue))
+		return -EBUSY;
+
+	ret = v4l2_subdev_call(dev->sensor->sd, video, s_std, std);
+	if (ret < 0)
+		return ret;
+
+	dev->current_std = std;
+	return 0;
+}
+
+static int imxcam_enum_input(struct file *file, void *priv,
+			     struct v4l2_input *input)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct imxcam_sensor_input *sinput;
+	struct imxcam_sensor *sensor;
+	int sensor_input;
+
+	/* find the sensor that is handling this input */
+	sensor = find_sensor_by_input_index(dev, input->index);
+	if (!sensor)
+		return -EINVAL;
+
+	sinput = &sensor->input;
+	sensor_input = input->index - sinput->first;
+
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+	input->capabilities = sinput->caps[sensor_input];
+	strncpy(input->name, sinput->name[sensor_input], sizeof(input->name));
+
+	if (input->index == dev->current_input) {
+		v4l2_subdev_call(sensor->sd, video, g_input_status, &input->status);
+		update_sensor_std(dev);
+		input->std = dev->current_std;
+	} else {
+		input->status = V4L2_IN_ST_NO_SIGNAL;
+		input->std = V4L2_STD_UNKNOWN;
+	}
+
+	return 0;
+}
+
+static int imxcam_g_input(struct file *file, void *priv, unsigned int *index)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+
+	*index = dev->current_input;
+	return 0;
+}
+
+static int imxcam_s_input(struct file *file, void *priv, unsigned int index)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct imxcam_sensor_input *sinput;
+	struct imxcam_sensor *sensor;
+	int ret, sensor_input, fim_actv;
+
+	if (index == dev->current_input)
+		return 0;
+
+	/* find the sensor that is handling this input */
+	sensor = find_sensor_by_input_index(dev, index);
+	if (!sensor)
+		return -EINVAL;
+
+	if (dev->sensor != sensor) {
+		/*
+		 * don't allow switching sensors if there are queued buffers
+		 * or there are other users of the current sensor besides us.
+		 */
+		if (vb2_is_busy(&dev->buffer_queue) ||
+		    dev->sensor->power_count > 1)
+			return -EBUSY;
+
+		v4l2_info(&dev->v4l2_dev, "switching to sensor %s\n",
+			  sensor->sd->name);
+
+		/* power down current sensor before enabling new one */
+		ret = sensor_set_power(dev, 0);
+		if (ret)
+			v4l2_warn(&dev->v4l2_dev, "sensor power off failed\n");
+
+		/* set new sensor and the video mux(es) in the pipeline to it */
+		dev->sensor = sensor;
+		ret = imxcam_set_video_muxes(dev);
+		if (ret)
+			v4l2_warn(&dev->v4l2_dev, "set video muxes failed\n");
+
+		/*
+		 * turn on FIM if ADV718x is selected else turn off FIM
+		 * for other sensors.
+		 */
+		if (strncasecmp(sensor->sd->name, "adv718", 6) == 0)
+			fim_actv = 1;
+		else
+			fim_actv = 0;
+		v4l2_ctrl_s_ctrl(dev->fim.ctrl[FIM_CL_ENABLE], fim_actv);
+
+		/* power-on the new sensor */
+		ret = sensor_set_power(dev, 1);
+		if (ret)
+			v4l2_warn(&dev->v4l2_dev, "sensor power on failed\n");
+	}
+
+	/* finally select the sensor's input */
+	sinput = &sensor->input;
+	sensor_input = index - sinput->first;
+	ret = v4l2_subdev_call(sensor->sd, video, s_routing,
+			       sinput->value[sensor_input], 0, 0);
+
+	dev->current_input = index;
+
+	/*
+	 * Status update required if there is a change
+	 * of inputs
+	 */
+	atomic_set(&dev->status_change, 1);
+
+	return 0;
+}
+
+static int imxcam_g_parm(struct file *file, void *fh,
+			 struct v4l2_streamparm *a)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return v4l2_subdev_call(dev->sensor->sd, video, g_parm, a);
+}
+
+static int imxcam_s_parm(struct file *file, void *fh,
+			 struct v4l2_streamparm *a)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	return v4l2_subdev_call(dev->sensor->sd, video, s_parm, a);
+}
+
+static int imxcam_g_selection(struct file *file, void *priv,
+			      struct v4l2_selection *sel)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	switch (sel->target) {
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE:
+		/*
+		 * compose windows are not supported in this driver,
+		 * compose window is same as user buffers from s_fmt.
+		 */
+		sel->r.left = 0;
+		sel->r.top = 0;
+		sel->r.width = dev->user_fmt.fmt.pix.width;
+		sel->r.height = dev->user_fmt.fmt.pix.height;
+		break;
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+		sel->r = dev->crop_bounds;
+		break;
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r = dev->crop_defrect;
+		break;
+	case V4L2_SEL_TGT_CROP:
+		sel->r = dev->crop;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int imxcam_s_selection(struct file *file, void *priv,
+			      struct v4l2_selection *sel)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct v4l2_rect *bounds = &dev->crop_bounds;
+
+	if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+	    sel->target != V4L2_SEL_TGT_CROP)
+		return -EINVAL;
+
+	if (vb2_is_busy(&dev->buffer_queue))
+		return -EBUSY;
+
+	/* make sure crop window is within bounds */
+	if (sel->r.top < 0 || sel->r.left < 0 ||
+	    sel->r.left + sel->r.width > bounds->width ||
+	    sel->r.top + sel->r.height > bounds->height)
+		return -EINVAL;
+
+	/*
+	 * FIXME: the IPU currently does not setup the CCIR code
+	 * registers properly to handle arbitrary vertical crop
+	 * windows. So return error if the sensor bus is BT.656
+	 * and user is asking to change vertical cropping.
+	 */
+	if (dev->sensor->ep.bus_type == V4L2_MBUS_BT656 &&
+	    (sel->r.top != dev->crop.top ||
+	     sel->r.height != dev->crop.height)) {
+		v4l2_err(&dev->v4l2_dev,
+			 "vertical crop is not supported for this sensor!\n");
+		return -EINVAL;
+	}
+
+	/* adjust crop window to h/w alignment restrictions */
+	sel->r.width &= ~0x7;
+	sel->r.left &= ~0x3;
+
+	dev->crop = sel->r;
+
+	/*
+	 * Crop window has changed, we need to adjust the user
+	 * width/height to meet new IC resizer restrictions or to
+	 * match the new crop window if the IC can't be used.
+	 */
+	adjust_user_fmt(dev, &dev->sensor_fmt, &dev->user_fmt,
+			&dev->crop);
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops imxcam_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap        = imxcam_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap           = imxcam_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap         = imxcam_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap           = imxcam_s_fmt_vid_cap,
+
+	.vidioc_enum_framesizes         = imxcam_enum_framesizes,
+	.vidioc_enum_frameintervals     = imxcam_enum_frameintervals,
+
+	.vidioc_querystd        = imxcam_querystd,
+	.vidioc_g_std           = imxcam_g_std,
+	.vidioc_s_std           = imxcam_s_std,
+
+	.vidioc_enum_input      = imxcam_enum_input,
+	.vidioc_g_input         = imxcam_g_input,
+	.vidioc_s_input         = imxcam_s_input,
+
+	.vidioc_g_parm          = imxcam_g_parm,
+	.vidioc_s_parm          = imxcam_s_parm,
+
+	.vidioc_g_selection     = imxcam_g_selection,
+	.vidioc_s_selection     = imxcam_s_selection,
+
+	.vidioc_reqbufs		= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs     = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf     = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf	= vb2_ioctl_querybuf,
+	.vidioc_qbuf		= vb2_ioctl_qbuf,
+	.vidioc_dqbuf		= vb2_ioctl_dqbuf,
+	.vidioc_expbuf		= vb2_ioctl_expbuf,
+	.vidioc_streamon	= vb2_ioctl_streamon,
+	.vidioc_streamoff	= vb2_ioctl_streamoff,
+};
+
+/*
+ * Queue operations
+ */
+
+static int imxcam_queue_setup(struct vb2_queue *vq,
+			      unsigned int *nbuffers, unsigned int *nplanes,
+			      unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct imxcam_dev *dev = vb2_get_drv_priv(vq);
+	unsigned int count = *nbuffers;
+	u32 sizeimage = dev->user_fmt.fmt.pix.sizeimage;
+
+	if (vq->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	while (sizeimage * count > VID_MEM_LIMIT)
+		count--;
+
+	*nplanes = 1;
+	*nbuffers = count;
+	sizes[0] = sizeimage;
+
+	alloc_ctxs[0] = dev->alloc_ctx;
+
+	dprintk(dev, "get %d buffer(s) of size %d each.\n", count, sizeimage);
+
+	return 0;
+}
+
+static int imxcam_buf_init(struct vb2_buffer *vb)
+{
+	struct imxcam_buffer *buf = to_imxcam_vb(vb);
+
+	INIT_LIST_HEAD(&buf->list);
+	return 0;
+}
+
+static int imxcam_buf_prepare(struct vb2_buffer *vb)
+{
+	struct imxcam_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	if (vb2_plane_size(vb, 0) < dev->user_fmt.fmt.pix.sizeimage) {
+		v4l2_err(&dev->v4l2_dev,
+			 "data will not fit into plane (%lu < %lu)\n",
+			 vb2_plane_size(vb, 0),
+			 (long)dev->user_fmt.fmt.pix.sizeimage);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, dev->user_fmt.fmt.pix.sizeimage);
+
+	return 0;
+}
+
+static void imxcam_buf_queue(struct vb2_buffer *vb)
+{
+	struct imxcam_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+	struct imxcam_buffer *buf = to_imxcam_vb(vb);
+	unsigned long flags;
+	bool kickstart;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	list_add_tail(&buf->list, &dev->ready_q);
+
+	/* kickstart DMA chain if we have two frames in active q */
+	kickstart = (vb2_is_streaming(vb->vb2_queue) &&
+		     !(list_empty(&dev->ready_q) ||
+		       list_is_singular(&dev->ready_q)));
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	if (kickstart)
+		start_encoder(dev);
+}
+
+static void imxcam_lock(struct vb2_queue *vq)
+{
+	struct imxcam_dev *dev = vb2_get_drv_priv(vq);
+
+	mutex_lock(&dev->mutex);
+}
+
+static void imxcam_unlock(struct vb2_queue *vq)
+{
+	struct imxcam_dev *dev = vb2_get_drv_priv(vq);
+
+	mutex_unlock(&dev->mutex);
+}
+
+static int imxcam_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct imxcam_dev *dev = vb2_get_drv_priv(vq);
+	struct imxcam_buffer *buf, *tmp;
+	unsigned long flags;
+	int ret;
+
+	if (vb2_is_streaming(vq))
+		return 0;
+
+	dev->stop = false;
+
+	ret = set_stream(dev, true);
+	if (ret)
+		goto return_bufs;
+
+	return 0;
+
+return_bufs:
+	spin_lock_irqsave(&dev->irqlock, flags);
+	list_for_each_entry_safe(buf, tmp, &dev->ready_q, list) {
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+	}
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return ret;
+}
+
+static void imxcam_stop_streaming(struct vb2_queue *vq)
+{
+	struct imxcam_dev *dev = vb2_get_drv_priv(vq);
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+
+	if (!vb2_is_streaming(vq))
+		return;
+
+	/*
+	 * signal that streaming is being stopped, so that the
+	 * restart_work_handler() will skip unnecessary stream
+	 * restarts, and to stop kicking the restart timer.
+	 */
+	dev->stop = true;
+
+	set_stream(dev, false);
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	/* release all active buffers */
+	while (!list_empty(&dev->ready_q)) {
+		frame = list_entry(dev->ready_q.next,
+				   struct imxcam_buffer, list);
+		list_del(&frame->list);
+		vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+	}
+
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+static struct vb2_ops imxcam_qops = {
+	.queue_setup	 = imxcam_queue_setup,
+	.buf_init        = imxcam_buf_init,
+	.buf_prepare	 = imxcam_buf_prepare,
+	.buf_queue	 = imxcam_buf_queue,
+	.wait_prepare	 = imxcam_unlock,
+	.wait_finish	 = imxcam_lock,
+	.start_streaming = imxcam_start_streaming,
+	.stop_streaming  = imxcam_stop_streaming,
+};
+
+/*
+ * File operations
+ */
+static int imxcam_open(struct file *file)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->mutex))
+		return -ERESTARTSYS;
+
+	if (!dev->sensor || !dev->sensor->sd) {
+		v4l2_err(&dev->v4l2_dev, "no subdevice registered\n");
+		ret = -ENODEV;
+		goto unlock;
+	}
+
+	ret = v4l2_fh_open(file);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "v4l2_fh_open failed\n");
+		goto unlock;
+	}
+
+	if (!v4l2_fh_is_singular_file(file))
+		goto unlock;
+
+	ret = sensor_set_power(dev, 1);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "sensor power on failed\n");
+		goto fh_release;
+	}
+
+	/* update the sensor's current lock status and format */
+	update_signal_lock_status(dev);
+	update_sensor_fmt(dev);
+
+	mutex_unlock(&dev->mutex);
+	return 0;
+
+fh_release:
+	v4l2_fh_release(file);
+unlock:
+	mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+static int imxcam_release(struct file *file)
+{
+	struct imxcam_dev *dev = video_drvdata(file);
+	struct vb2_queue *vq = &dev->buffer_queue;
+	unsigned long flags;
+	int ret = 0;
+
+	mutex_lock(&dev->mutex);
+
+	if (file->private_data == vq->owner) {
+		vb2_queue_release(vq);
+		vq->owner = NULL;
+	}
+
+	if (!v4l2_fh_is_singular_file(file))
+		goto fh_release;
+
+	spin_lock_irqsave(&dev->notify_lock, flags);
+	/* cancel any pending or scheduled restart timer */
+	del_timer_sync(&dev->restart_timer);
+	spin_unlock_irqrestore(&dev->notify_lock, flags);
+
+	/*
+	 * cancel any scheduled restart work, we have to release
+	 * the dev->mutex in case it has already been scheduled.
+	 */
+	mutex_unlock(&dev->mutex);
+	cancel_work_sync(&dev->restart_work);
+	mutex_lock(&dev->mutex);
+
+	if (!dev->sensor || !dev->sensor->sd) {
+		v4l2_warn(&dev->v4l2_dev, "lost the slave?\n");
+		goto fh_release;
+	}
+
+	ret = sensor_set_power(dev, 0);
+	if (ret)
+		v4l2_err(&dev->v4l2_dev, "sensor power off failed\n");
+
+fh_release:
+	v4l2_fh_release(file);
+	mutex_unlock(&dev->mutex);
+	return ret;
+}
+
+static const struct v4l2_file_operations imxcam_fops = {
+	.owner		= THIS_MODULE,
+	.open		= imxcam_open,
+	.release	= imxcam_release,
+	.poll		= vb2_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= vb2_fop_mmap,
+};
+
+static struct video_device imxcam_videodev = {
+	.name		= DEVICE_NAME,
+	.fops		= &imxcam_fops,
+	.ioctl_ops	= &imxcam_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release,
+	.vfl_dir	= VFL_DIR_RX,
+	.tvnorms	= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
+/*
+ * Handle notifications from the subdevs.
+ */
+static void imxcam_subdev_notification(struct v4l2_subdev *sd,
+				       unsigned int notification,
+				       void *arg)
+{
+	struct imxcam_dev *dev;
+	struct v4l2_event *ev;
+	unsigned long flags;
+
+	if (!sd)
+		return;
+
+	dev = sd2dev(sd);
+
+	spin_lock_irqsave(&dev->notify_lock, flags);
+
+	switch (notification) {
+	case IMXCAM_NFB4EOF_NOTIFY:
+		if (!dev->stop)
+			imxcam_bump_restart_timer(dev);
+		break;
+	case IMXCAM_FRAME_INTERVAL_NOTIFY:
+		if (!dev->stop && !atomic_read(&dev->pending_restart))
+			imxcam_bump_restart_timer(dev);
+		break;
+	case IMXCAM_EOF_TIMEOUT_NOTIFY:
+		if (!dev->stop) {
+			/*
+			 * cancel a running restart timer since we are
+			 * restarting now anyway
+			 */
+			del_timer_sync(&dev->restart_timer);
+			/* and restart now */
+			schedule_work(&dev->restart_work);
+		}
+		break;
+	case V4L2_DEVICE_NOTIFY_EVENT:
+		ev = (struct v4l2_event *)arg;
+		if (ev && ev->type == V4L2_EVENT_SOURCE_CHANGE) {
+			atomic_set(&dev->status_change, 1);
+			if (!dev->stop) {
+				v4l2_warn(&dev->v4l2_dev,
+					  "decoder status change\n");
+				imxcam_bump_restart_timer(dev);
+			}
+			/* send decoder status events to userspace */
+			v4l2_event_queue(dev->vfd, ev);
+		}
+		break;
+	}
+
+	spin_unlock_irqrestore(&dev->notify_lock, flags);
+}
+
+
+static void imxcam_unregister_sync_subdevs(struct imxcam_dev *dev)
+{
+	if (!IS_ERR_OR_NULL(dev->smfc_sd))
+		v4l2_device_unregister_subdev(dev->smfc_sd);
+
+	if (!IS_ERR_OR_NULL(dev->prpenc_sd))
+		v4l2_device_unregister_subdev(dev->prpenc_sd);
+
+	if (!IS_ERR_OR_NULL(dev->vdic_sd))
+		v4l2_device_unregister_subdev(dev->vdic_sd);
+}
+
+static int imxcam_register_sync_subdevs(struct imxcam_dev *dev)
+{
+	int ret;
+
+	dev->smfc_sd = imxcam_smfc_init(dev);
+	if (IS_ERR(dev->smfc_sd))
+		return PTR_ERR(dev->smfc_sd);
+
+	dev->prpenc_sd = imxcam_ic_prpenc_init(dev);
+	if (IS_ERR(dev->prpenc_sd))
+		return PTR_ERR(dev->prpenc_sd);
+
+	dev->vdic_sd = imxcam_vdic_init(dev);
+	if (IS_ERR(dev->vdic_sd))
+		return PTR_ERR(dev->vdic_sd);
+
+	ret = v4l2_device_register_subdev(&dev->v4l2_dev, dev->smfc_sd);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "failed to register subdev %s\n",
+			 dev->smfc_sd->name);
+		goto unreg;
+	}
+	v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", dev->smfc_sd->name);
+
+	ret = v4l2_device_register_subdev(&dev->v4l2_dev, dev->prpenc_sd);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "failed to register subdev %s\n",
+			 dev->prpenc_sd->name);
+		goto unreg;
+	}
+	v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", dev->prpenc_sd->name);
+
+	ret = v4l2_device_register_subdev(&dev->v4l2_dev, dev->vdic_sd);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev, "failed to register subdev %s\n",
+			 dev->vdic_sd->name);
+		goto unreg;
+	}
+	v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", dev->vdic_sd->name);
+
+	return 0;
+
+unreg:
+	imxcam_unregister_sync_subdevs(dev);
+	return ret;
+}
+
+/* async subdev bound notifier */
+static int imxcam_subdev_bound(struct v4l2_async_notifier *notifier,
+			       struct v4l2_subdev *sd,
+			       struct v4l2_async_subdev *asd)
+{
+	struct imxcam_dev *dev = notifier2dev(notifier);
+	struct imxcam_sensor_input *sinput;
+	struct imxcam_sensor *sensor;
+	int i, ret = -EINVAL;
+
+	if (dev->csi2_asd &&
+	    sd->dev->of_node == dev->csi2_asd->match.of.node) {
+		dev->csi2_sd = sd;
+		ret = 0;
+		goto out;
+	}
+
+	for (i = 0; i < dev->num_csi; i++) {
+		if (dev->csi_asd[i] &&
+		    sd->dev->of_node == dev->csi_asd[i]->match.of.node) {
+			dev->csi_list[i] = sd;
+			ret = 0;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < dev->num_vidmux; i++) {
+		if (dev->vidmux_asd[i] &&
+		    sd->dev->of_node == dev->vidmux_asd[i]->match.of.node) {
+			dev->vidmux_list[i] = sd;
+			ret = 0;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < dev->num_sensors; i++) {
+		sensor = &dev->sensor_list[i];
+		if (sensor->asd &&
+		    sd->dev->of_node == sensor->asd->match.of.node) {
+			sensor->sd = sd;
+
+			/* set sensor input names if needed */
+			sinput = &sensor->input;
+			for (i = 0; i < sinput->num; i++) {
+				if (strlen(sinput->name[i]))
+					continue;
+				snprintf(sinput->name[i],
+					 sizeof(sinput->name[i]),
+					 "%s-%d", sd->name, i);
+			}
+
+			ret = 0;
+			break;
+		}
+	}
+
+out:
+	if (ret)
+		v4l2_warn(&dev->v4l2_dev, "Received unknown subdev %s\n",
+			  sd->name);
+	else
+		v4l2_info(&dev->v4l2_dev, "Registered subdev %s\n", sd->name);
+
+	return ret;
+}
+
+/* async subdev complete notifier */
+static int imxcam_probe_complete(struct v4l2_async_notifier *notifier)
+{
+	struct imxcam_dev *dev = notifier2dev(notifier);
+	struct vb2_queue *vq = &dev->buffer_queue;
+	struct imxcam_sensor *sensor;
+	int i, j, ret;
+
+	/* assign CSI subdevs to every sensor */
+	for (i = 0; i < dev->num_sensors; i++) {
+		sensor = &dev->sensor_list[i];
+		for (j = 0; j < dev->num_csi; j++) {
+			if (sensor->csi_np == dev->csi_asd[j]->match.of.node) {
+				sensor->csi_sd = dev->csi_list[j];
+				break;
+			}
+		}
+		if (j >= dev->num_csi) {
+			v4l2_err(&dev->v4l2_dev,
+				 "Failed to find a CSI for sensor %s\n",
+				 sensor->sd->name);
+			return -ENODEV;
+		}
+	}
+
+	/* make default sensor the first in list */
+	dev->sensor = &dev->sensor_list[0];
+
+	/* setup our controls */
+	ret = v4l2_ctrl_handler_setup(&dev->ctrl_hdlr);
+	if (ret)
+		goto free_ctrls;
+
+	ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		goto free_ctrls;
+	}
+
+	ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
+	if (ret)
+		goto unreg;
+
+	/* set video mux(es) in the pipeline to this sensor */
+	ret = imxcam_set_video_muxes(dev);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to set video muxes\n");
+		goto unreg;
+	}
+
+	dev->v4l2_dev.notify = imxcam_subdev_notification;
+
+	dev->alloc_ctx = vb2_dma_contig_init_ctx(dev->dev);
+	if (IS_ERR(dev->alloc_ctx)) {
+		v4l2_err(&dev->v4l2_dev, "failed to alloc vb2 context\n");
+		ret = PTR_ERR(dev->alloc_ctx);
+		goto unreg;
+	}
+
+	vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	vq->drv_priv = dev;
+	vq->buf_struct_size = sizeof(struct imxcam_buffer);
+	vq->ops = &imxcam_qops;
+	vq->mem_ops = &vb2_dma_contig_memops;
+	vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	ret = vb2_queue_init(vq);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "vb2_queue_init failed\n");
+		goto alloc_ctx_free;
+	}
+
+	INIT_LIST_HEAD(&dev->ready_q);
+	INIT_WORK(&dev->restart_work, restart_work_handler);
+	INIT_WORK(&dev->stop_work, stop_work_handler);
+	__init_timer(&dev->restart_timer, TIMER_IRQSAFE);
+	dev->restart_timer.data = (unsigned long)dev;
+	dev->restart_timer.function = imxcam_restart_timeout;
+
+	v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n",
+		  dev->vfd->num);
+
+	return 0;
+
+alloc_ctx_free:
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+unreg:
+	video_unregister_device(dev->vfd);
+free_ctrls:
+	v4l2_ctrl_handler_free(&dev->ctrl_hdlr);
+	return ret;
+}
+
+static int imxcam_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct imxcam_dev *dev;
+	struct video_device *vfd;
+	struct pinctrl *pinctrl;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->dev = &pdev->dev;
+	mutex_init(&dev->mutex);
+	spin_lock_init(&dev->irqlock);
+	spin_lock_init(&dev->notify_lock);
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		return ret;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto unreg_dev;
+	}
+
+	*vfd = imxcam_videodev;
+	vfd->lock = &dev->mutex;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+ 	vfd->queue = &dev->buffer_queue;
+
+	video_set_drvdata(vfd, dev);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", imxcam_videodev.name);
+	dev->vfd = vfd;
+
+	platform_set_drvdata(pdev, dev);
+
+	/* Get any pins needed */
+	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+
+	/* setup some defaults */
+	dev->user_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dev->user_fmt.fmt.pix.width = 640;
+	dev->user_fmt.fmt.pix.height = 480;
+	dev->user_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+	dev->user_fmt.fmt.pix.bytesperline = (640 * 12) >> 3;
+	dev->user_fmt.fmt.pix.sizeimage =
+		(480 * dev->user_fmt.fmt.pix.bytesperline);
+	dev->user_pixfmt =
+		imxcam_get_format(dev->user_fmt.fmt.pix.pixelformat, 0);
+	dev->current_std = V4L2_STD_UNKNOWN;
+
+	dev->sensor_set_stream = sensor_set_stream;
+
+	ret = imxcam_of_parse(dev, node);
+	if (ret)
+		goto unreg_dev;
+
+	if (dev->fim.icap_channel < 0)
+		dev->fim.eof = fim_eof_handler;
+
+	/* init our controls */
+	ret = imxcam_init_controls(dev);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "init controls failed\n");
+		goto unreg_dev;
+	}
+
+	ret = imxcam_register_sync_subdevs(dev);
+	if (ret)
+		goto unreg_dev;
+
+	/* prepare the async subdev notifier and register it */
+	dev->subdev_notifier.subdevs = dev->async_ptrs;
+	dev->subdev_notifier.bound = imxcam_subdev_bound;
+	dev->subdev_notifier.complete = imxcam_probe_complete;
+	ret = v4l2_async_notifier_register(&dev->v4l2_dev,
+					   &dev->subdev_notifier);
+	if (ret)
+		goto unreg_dev;
+
+	return 0;
+
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+	return ret;
+}
+
+static int imxcam_remove(struct platform_device *pdev)
+{
+	struct imxcam_dev *dev =
+		(struct imxcam_dev *)platform_get_drvdata(pdev);
+
+	v4l2_info(&dev->v4l2_dev, "Removing " DEVICE_NAME "\n");
+	v4l2_ctrl_handler_free(&dev->ctrl_hdlr);
+	v4l2_async_notifier_unregister(&dev->subdev_notifier);
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+	video_unregister_device(dev->vfd);
+	imxcam_unregister_sync_subdevs(dev);
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	return 0;
+}
+
+static const struct of_device_id imxcam_dt_ids[] = {
+	{ .compatible = "fsl,imx-video-capture" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imxcam_dt_ids);
+
+static struct platform_driver imxcam_pdrv = {
+	.probe		= imxcam_probe,
+	.remove		= imxcam_remove,
+	.driver		= {
+		.name	= DEVICE_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table	= imxcam_dt_ids,
+	},
+};
+
+module_platform_driver(imxcam_pdrv);
+
+MODULE_DESCRIPTION("i.MX5/6 v4l2 capture driver");
+MODULE_AUTHOR("Mentor Graphics Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/imx/capture/imx-camif.h b/drivers/staging/media/imx/capture/imx-camif.h
new file mode 100644
index 0000000..73fe1aa
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-camif.h
@@ -0,0 +1,270 @@
+/*
+ * Video Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _IMX_CAMIF_H
+#define _IMX_CAMIF_H
+
+#define dprintk(dev, fmt, arg...)					\
+	v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+/*
+ * These numbers are somewhat arbitrary, but we need at least:
+ * - 1 mipi-csi2 receiver subdev
+ * - 2 video-mux subdevs
+ * - 3 sensor subdevs (2 parallel, 1 mipi-csi2)
+ * - 4 CSI subdevs
+ */
+#define IMXCAM_MAX_SUBDEVS       16
+#define IMXCAM_MAX_SENSORS        8
+#define IMXCAM_MAX_VIDEOMUX       4
+#define IMXCAM_MAX_CSI            4
+
+/*
+ * How long before no EOF interrupts cause a stream restart, or a buffer
+ * dequeue timeout, in msec. The dequeue timeout should be longer than
+ * the EOF timeout.
+ */
+#define IMXCAM_EOF_TIMEOUT       1000
+#define IMXCAM_DQ_TIMEOUT        5000
+
+/*
+ * How long to delay a restart on ADV718x status changes or NFB4EOF,
+ * in msec.
+ */
+#define IMXCAM_RESTART_DELAY      200
+
+/*
+ * Internal subdev notifications
+ */
+#define IMXCAM_NFB4EOF_NOTIFY         _IO('6', 0)
+#define IMXCAM_EOF_TIMEOUT_NOTIFY     _IO('6', 1)
+#define IMXCAM_FRAME_INTERVAL_NOTIFY  _IO('6', 2)
+
+/*
+ * Frame Interval Monitor Control Indexes and default values
+ */
+enum {
+	FIM_CL_ENABLE = 0,
+	FIM_CL_NUM,
+	FIM_CL_TOLERANCE_MIN,
+	FIM_CL_TOLERANCE_MAX,
+	FIM_CL_NUM_SKIP,
+	FIM_NUM_CONTROLS,
+};
+
+#define FIM_CL_ENABLE_DEF      0 /* FIM disabled by default */
+#define FIM_CL_NUM_DEF         8 /* average 8 frames */
+#define FIM_CL_NUM_SKIP_DEF    8 /* skip 8 frames after restart */
+#define FIM_CL_TOLERANCE_MIN_DEF  50 /* usec */
+#define FIM_CL_TOLERANCE_MAX_DEF   0 /* no max tolerance (unbounded) */
+
+struct imxcam_buffer {
+	struct vb2_buffer vb; /* v4l buffer must be first */
+	struct list_head  list;
+};
+
+static inline struct imxcam_buffer *to_imxcam_vb(struct vb2_buffer *vb)
+{
+	return container_of(vb, struct imxcam_buffer, vb);
+}
+
+struct imxcam_pixfmt {
+	char	*name;
+	u32	fourcc;
+	u32     codes[4];
+	int     bpp;     /* total bpp */
+	int     y_depth; /* depth of first Y plane for planar formats */
+};
+
+struct imxcam_dma_buf {
+	void          *virt;
+	dma_addr_t     phys;
+	unsigned long  len;
+};
+
+/*
+ * A sensor's inputs parsed from v4l2_of_endpoint nodes in devicetree
+ */
+#define IMXCAM_MAX_INPUTS 16
+
+struct imxcam_sensor_input {
+	/* input values passed to s_routing */
+	u32 value[IMXCAM_MAX_INPUTS];
+	/* input capabilities (V4L2_IN_CAP_*) */
+	u32 caps[IMXCAM_MAX_INPUTS];
+	/* input names */
+	char name[IMXCAM_MAX_INPUTS][32];
+
+	/* number of inputs */
+	int num;
+	/* first and last input indexes from imxcam perspective */
+	int first;
+	int last;
+};
+
+struct imxcam_sensor {
+	struct v4l2_subdev       *sd;
+	struct v4l2_async_subdev *asd;
+	struct v4l2_of_endpoint  ep;     /* sensor's endpoint info */
+
+	/* csi node and subdev this sensor is connected to */
+	struct device_node       *csi_np;
+	struct v4l2_subdev       *csi_sd; 
+	struct v4l2_of_endpoint  csi_ep; /* parsed endpoint info of csi port */
+
+	struct imxcam_sensor_input input;
+
+	/* input indeces of all video-muxes required to access this sensor */
+	int vidmux_input[IMXCAM_MAX_VIDEOMUX];
+
+	int power_count;                 /* power use counter */
+	int stream_count;                /* stream use counter */
+};
+
+struct imxcam_dev;
+
+/* frame interval monitor */
+struct imxcam_fim {
+	/* control cluster */
+	struct v4l2_ctrl  *ctrl[FIM_NUM_CONTROLS];
+
+	/* default ctrl values parsed from device tree */
+	u32               of_defaults[FIM_NUM_CONTROLS];
+
+	/* current control values */
+	bool              enabled;
+	int               num_avg;
+	int               num_skip;
+	unsigned long     tolerance_min; /* usec */
+	unsigned long     tolerance_max; /* usec */
+
+	int               counter;
+	struct timespec   last_ts;
+	unsigned long     sum;       /* usec */
+	unsigned long     nominal;   /* usec */
+
+	/*
+	 * input capture method of measuring FI (channel and flags
+	 * from device tree)
+	 */
+	int               icap_channel;
+	int               icap_flags;
+
+	/*
+	 * otherwise, the EOF method of measuring FI, called by
+	 * streaming subdevs from eof irq
+	 */
+	int (*eof)(struct imxcam_dev *dev, struct timeval *now);
+};
+
+struct imxcam_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vfd;
+	struct device           *dev;
+
+	struct mutex		mutex;
+	spinlock_t		irqlock;
+	spinlock_t		notify_lock;
+
+	/* buffer queue used in videobuf2 */
+	struct vb2_queue        buffer_queue;
+
+	struct vb2_alloc_ctx    *alloc_ctx;
+
+	/* streaming buffer queue */
+	struct list_head        ready_q;
+
+	/* stream stop and restart handling */
+	struct work_struct      restart_work;
+	struct work_struct      stop_work;
+	struct timer_list       restart_timer;
+
+	/* v4l2 controls */
+	struct v4l2_ctrl_handler ctrl_hdlr;
+	int                      rotation; /* degrees */
+	bool                     hflip;
+	bool                     vflip;
+	enum ipu_motion_sel      motion;
+
+	/* derived from rotation, hflip, vflip controls */
+	enum ipu_rotate_mode     rot_mode;
+
+	struct imxcam_fim        fim;
+
+	/* the format from sensor and from userland */
+	struct v4l2_format        user_fmt;
+	struct imxcam_pixfmt      *user_pixfmt;
+	struct v4l2_mbus_framefmt sensor_fmt;
+	struct v4l2_fract         sensor_tpf;
+	struct imxcam_pixfmt      *sensor_pixfmt;
+	struct v4l2_mbus_config   mbus_cfg;
+
+	/*
+	 * the crop rectangle (from s_crop) specifies the crop dimensions
+	 * and position over the raw capture frame boundaries.
+	 */
+	struct v4l2_rect        crop_bounds;
+	struct v4l2_rect        crop_defrect;
+	struct v4l2_rect        crop;
+
+	/* misc status */
+	int                     current_input; /* the current input */
+	v4l2_std_id             current_std;   /* current video standard */
+	atomic_t                status_change; /* sensor status change */
+	atomic_t                pending_restart; /* a restart is pending */
+	bool                    signal_locked; /* sensor signal lock */
+	bool                    encoder_on;    /* encode is on */
+	bool                    stop;          /* streaming is stopping */
+	bool                    using_ic;      /* IC is being used for encode */
+	bool                    using_vdic;    /* VDIC is used for encode */
+	bool                    vdic_direct;   /* VDIC is using the direct
+						  CSI->VDIC pipeline */
+
+	/* master descriptor list for async subdev registration */
+	struct v4l2_async_subdev async_desc[IMXCAM_MAX_SUBDEVS];
+	struct v4l2_async_subdev *async_ptrs[IMXCAM_MAX_SUBDEVS];
+
+	/* for async subdev registration */
+	struct v4l2_async_notifier subdev_notifier;
+
+	/* camera sensor subdev list */
+	struct imxcam_sensor    sensor_list[IMXCAM_MAX_SENSORS];
+	struct imxcam_sensor    *sensor; /* the current active sensor */
+	int                     num_sensor_inputs;
+	int                     num_sensors;
+
+	/* mipi-csi2 receiver subdev */
+	struct v4l2_subdev      *csi2_sd;
+	struct v4l2_async_subdev *csi2_asd;
+
+	/* CSI subdev list */
+	struct v4l2_subdev      *csi_list[IMXCAM_MAX_CSI];
+	struct v4l2_async_subdev *csi_asd[IMXCAM_MAX_CSI];
+	int                     num_csi;
+
+	/* video-mux subdev list */
+	struct v4l2_subdev      *vidmux_list[IMXCAM_MAX_VIDEOMUX];
+	struct v4l2_async_subdev *vidmux_asd[IMXCAM_MAX_VIDEOMUX];
+	int                     num_vidmux;
+
+	/* synchronous prpenc, smfc, and vdic subdevs */
+	struct v4l2_subdev      *smfc_sd;
+	struct v4l2_subdev      *prpenc_sd;
+	struct v4l2_subdev      *vdic_sd;
+
+	int (*sensor_set_stream)(struct imxcam_dev *dev, int on);
+};
+
+
+struct v4l2_subdev *imxcam_smfc_init(struct imxcam_dev *dev);
+struct v4l2_subdev *imxcam_ic_prpenc_init(struct imxcam_dev *dev);
+struct v4l2_subdev *imxcam_vdic_init(struct imxcam_dev *dev);
+
+#endif /* _IMX_CAMIF_H */
diff --git a/drivers/staging/media/imx/capture/imx-csi.c b/drivers/staging/media/imx/capture/imx-csi.c
new file mode 100644
index 0000000..23973a6
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-csi.c
@@ -0,0 +1,195 @@
+/*
+ * V4L2 Capture CSI Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2014-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include "imx-camif.h"
+
+struct csi_priv {
+	struct device *dev;
+	struct imxcam_dev *camif;
+	struct v4l2_subdev sd;
+	struct ipu_soc *ipu;
+	struct ipu_csi *csi;
+};
+
+static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev)
+{
+	return container_of(sdev, struct csi_priv, sd);
+}
+
+/*
+ * Update the CSI whole sensor and active windows, and initialize
+ * the CSI interface and muxes.
+ */
+static void csi_setup(struct csi_priv *priv)
+{
+	struct imxcam_dev *camif = priv->camif;
+	int vc_num = camif->sensor->csi_ep.base.id;
+	bool is_csi2 = camif->sensor->ep.bus_type == V4L2_MBUS_CSI2;
+	enum ipu_csi_dest dest;
+
+	ipu_csi_set_window(priv->csi, &camif->crop);
+	ipu_csi_init_interface(priv->csi, &camif->mbus_cfg,
+			       &camif->sensor_fmt);
+	if (is_csi2)
+		ipu_csi_set_mipi_datatype(priv->csi, vc_num,
+					  &camif->sensor_fmt);
+
+	/* select either parallel or MIPI-CSI2 as input to our CSI */
+	ipu_csi_set_src(priv->csi, vc_num, is_csi2);
+
+	/* set CSI destination */
+	if (camif->using_vdic && camif->vdic_direct)
+		dest = IPU_CSI_DEST_VDIC;
+	else if (camif->using_ic && !camif->using_vdic)
+		dest = IPU_CSI_DEST_IC;
+	else
+		dest = IPU_CSI_DEST_IDMAC;
+	ipu_csi_set_dest(priv->csi, dest);
+
+	ipu_csi_dump(priv->csi);
+}
+
+static void csi_put_ipu_resources(struct csi_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->csi))
+		ipu_csi_put(priv->csi);
+	priv->csi = NULL;
+}
+
+static int csi_get_ipu_resources(struct csi_priv *priv)
+{
+	struct imxcam_dev *camif = priv->camif;
+	int csi_id = camif->sensor->csi_ep.base.port;
+
+	priv->ipu = dev_get_drvdata(priv->dev->parent);
+
+	priv->csi = ipu_csi_get(priv->ipu, csi_id);
+	if (IS_ERR(priv->csi)) {
+		v4l2_err(&priv->sd, "failed to get CSI %d\n", csi_id);
+		return PTR_ERR(priv->csi);
+	}
+
+	return 0;
+}
+
+static int csi_start(struct csi_priv *priv)
+{
+	int err;
+
+	err = csi_get_ipu_resources(priv);
+	if (err)
+		return err;
+
+	csi_setup(priv);
+
+	err = ipu_csi_enable(priv->csi);
+	if (err) {
+		v4l2_err(&priv->sd, "CSI enable error: %d\n", err);
+		goto out_put_ipu;
+	}
+
+	return 0;
+
+out_put_ipu:
+	csi_put_ipu_resources(priv);
+	return err;
+}
+
+static int csi_stop(struct csi_priv *priv)
+{
+	ipu_csi_disable(priv->csi);
+
+	csi_put_ipu_resources(priv);
+
+	return 0;
+}
+
+static int csi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct csi_priv *priv = v4l2_get_subdevdata(sd);
+
+	if (!sd->v4l2_dev || !sd->v4l2_dev->dev)
+		return -ENODEV;
+
+	/* get imxcam host device */
+	priv->camif = dev_get_drvdata(sd->v4l2_dev->dev);
+
+	return enable ? csi_start(priv) : csi_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops csi_video_ops = {
+	.s_stream = csi_s_stream,
+};
+
+static struct v4l2_subdev_ops csi_subdev_ops = {
+	.video = &csi_video_ops,
+};
+
+static int imxcam_csi_probe(struct platform_device *pdev)
+{
+	struct csi_priv *priv;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, &priv->sd);
+
+	priv->dev = &pdev->dev;
+
+	v4l2_subdev_init(&priv->sd, &csi_subdev_ops);
+	v4l2_set_subdevdata(&priv->sd, priv);
+	priv->sd.dev = &pdev->dev;
+	priv->sd.owner = THIS_MODULE;
+	strlcpy(priv->sd.name, "imx-camera-csi", sizeof(priv->sd.name));
+
+	return v4l2_async_register_subdev(&priv->sd);
+}
+
+static int imxcam_csi_remove(struct platform_device *pdev)
+{
+	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+	struct csi_priv *priv = sd_to_dev(sd);
+
+	v4l2_async_unregister_subdev(&priv->sd);
+	v4l2_device_unregister_subdev(sd);
+
+	return 0;
+}
+
+static const struct platform_device_id imxcam_csi_ids[] = {
+	{ .name = "imx-ipuv3-csi" },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, imxcam_csi_ids);
+
+static struct platform_driver imxcam_csi_driver = {
+	.probe = imxcam_csi_probe,
+	.remove = imxcam_csi_remove,
+	.id_table = imxcam_csi_ids,
+	.driver = {
+		.name = "imx-ipuv3-csi",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(imxcam_csi_driver);
+
+MODULE_AUTHOR("Mentor Graphics Inc.");
+MODULE_DESCRIPTION("i.MX CSI subdev driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-ipuv3-csi");
diff --git a/drivers/staging/media/imx/capture/imx-ic-prpenc.c b/drivers/staging/media/imx/capture/imx-ic-prpenc.c
new file mode 100644
index 0000000..b34b9d5
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-ic-prpenc.c
@@ -0,0 +1,661 @@
+/*
+ * V4L2 Capture Encoder Subdev for Freescale i.MX5/6 SOC
+ *
+ * This subdevice handles capture of video frames from the CSI, which
+ * routed directly to the Image Converter preprocess encode task, for
+ * resizing, colorspace conversion, and rotation.
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+
+struct prpenc_priv {
+	struct imxcam_dev    *dev;
+	struct v4l2_subdev    sd;
+
+	struct ipu_soc       *ipu;
+	struct ipuv3_channel *enc_ch;
+	struct ipuv3_channel *enc_rot_in_ch;
+	struct ipuv3_channel *enc_rot_out_ch;
+	struct ipu_ic *ic_enc;
+	struct ipu_smfc *smfc;
+
+	struct v4l2_mbus_framefmt inf; /* input sensor format */
+	struct v4l2_pix_format outf;   /* output user format */
+	enum ipu_color_space in_cs;    /* input colorspace */
+	enum ipu_color_space out_cs;   /* output colorspace */
+
+	/* active (undergoing DMA) buffers, one for each IPU buffer */
+	struct imxcam_buffer *active_frame[2];
+
+	struct imxcam_dma_buf rot_buf[2];
+	struct imxcam_dma_buf underrun_buf;
+	int buf_num;
+
+	struct timer_list eof_timeout_timer;
+	int eof_irq;
+	int nfb4eof_irq;
+
+	bool last_eof;  /* waiting for last EOF at stream off */
+	struct completion last_eof_comp;
+};
+
+static void prpenc_put_ipu_resources(struct prpenc_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->ic_enc))
+		ipu_ic_put(priv->ic_enc);
+	priv->ic_enc = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->enc_ch))
+		ipu_idmac_put(priv->enc_ch);
+	priv->enc_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->enc_rot_in_ch))
+		ipu_idmac_put(priv->enc_rot_in_ch);
+	priv->enc_rot_in_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->enc_rot_out_ch))
+		ipu_idmac_put(priv->enc_rot_out_ch);
+	priv->enc_rot_out_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->smfc))
+		ipu_smfc_put(priv->smfc);
+	priv->smfc = NULL;
+}
+
+static int prpenc_get_ipu_resources(struct prpenc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct v4l2_subdev *csi_sd = dev->sensor->csi_sd;
+	int ret;
+
+	priv->ipu = dev_get_drvdata(csi_sd->dev->parent);
+
+	priv->ic_enc = ipu_ic_get(priv->ipu, IC_TASK_ENCODER);
+	if (IS_ERR(priv->ic_enc)) {
+		v4l2_err(&priv->sd, "failed to get IC ENC\n");
+		ret = PTR_ERR(priv->ic_enc);
+		goto out;
+	}
+
+	priv->enc_ch = ipu_idmac_get(priv->ipu,
+				     IPUV3_CHANNEL_IC_PRP_ENC_MEM);
+	if (IS_ERR(priv->enc_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 IPUV3_CHANNEL_IC_PRP_ENC_MEM);
+		ret = PTR_ERR(priv->enc_ch);
+		goto out;
+	}
+
+	priv->enc_rot_in_ch = ipu_idmac_get(priv->ipu,
+					    IPUV3_CHANNEL_MEM_ROT_ENC);
+	if (IS_ERR(priv->enc_rot_in_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 IPUV3_CHANNEL_MEM_ROT_ENC);
+		ret = PTR_ERR(priv->enc_rot_in_ch);
+		goto out;
+	}
+
+	priv->enc_rot_out_ch = ipu_idmac_get(priv->ipu,
+					     IPUV3_CHANNEL_ROT_ENC_MEM);
+	if (IS_ERR(priv->enc_rot_out_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 IPUV3_CHANNEL_ROT_ENC_MEM);
+		ret = PTR_ERR(priv->enc_rot_out_ch);
+		goto out;
+	}
+
+	return 0;
+out:
+	prpenc_put_ipu_resources(priv);
+	return ret;
+}
+
+static irqreturn_t prpenc_eof_interrupt(int irq, void *dev_id)
+{
+	struct prpenc_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	struct ipuv3_channel *channel;
+	enum vb2_buffer_state state;
+	struct timeval cur_timeval;
+	u64 cur_time_ns;
+	unsigned long flags;
+	dma_addr_t phys;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	cur_time_ns = ktime_get_ns();
+	cur_timeval = ns_to_timeval(cur_time_ns);
+
+	/* timestamp and return the completed frame */
+	frame = priv->active_frame[priv->buf_num];
+	if (frame) {
+		frame->vb.timestamp = cur_time_ns;
+		state = (dev->signal_locked &&
+			 !atomic_read(&dev->pending_restart)) ?
+			VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
+		vb2_buffer_done(&frame->vb, state);
+	}
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	if (dev->fim.eof && dev->fim.eof(dev, &cur_timeval))
+		v4l2_subdev_notify(&priv->sd, IMXCAM_FRAME_INTERVAL_NOTIFY,
+				   NULL);
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	if (!list_empty(&dev->ready_q)) {
+		frame = list_entry(dev->ready_q.next,
+				   struct imxcam_buffer, list);
+		phys = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[priv->buf_num] = frame;
+	} else {
+		phys = priv->underrun_buf.phys;
+		priv->active_frame[priv->buf_num] = NULL;
+	}
+
+	channel = (ipu_rot_mode_is_irt(dev->rot_mode)) ?
+		priv->enc_rot_out_ch : priv->enc_ch;
+
+	if (ipu_idmac_buffer_is_ready(channel, priv->buf_num))
+		ipu_idmac_clear_buffer(channel, priv->buf_num);
+
+	ipu_cpmem_set_buffer(channel, priv->buf_num, phys);
+	ipu_idmac_select_buffer(channel, priv->buf_num);
+
+	priv->buf_num ^= 1;
+
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t prpenc_nfb4eof_interrupt(int irq, void *dev_id)
+{
+	struct prpenc_priv *priv = dev_id;
+
+	v4l2_err(&priv->sd, "NFB4EOF\n");
+
+	/*
+	 * It has been discovered that with rotation, stream off
+	 * creates a single NFB4EOF event which is 100% repeatable. So
+	 * scheduling a restart here causes an endless NFB4EOF-->restart
+	 * cycle. The error itself seems innocuous, capture is not adversely
+	 * affected.
+	 *
+	 * So don't schedule a restart on NFB4EOF error. If the source
+	 * of the NFB4EOF event on disable is ever found, it can
+	 * be re-enabled, but is probably not necessary. Detecting the
+	 * interrupt (and clearing the irq status in the IPU) seems to
+	 * be enough.
+	 */
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function.
+ */
+static void prpenc_eof_timeout(unsigned long data)
+{
+	struct prpenc_priv *priv = (struct prpenc_priv *)data;
+
+	v4l2_err(&priv->sd, "EOF timeout\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_EOF_TIMEOUT_NOTIFY, NULL);
+}
+
+static void prpenc_free_dma_buf(struct prpenc_priv *priv,
+				 struct imxcam_dma_buf *buf)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	if (buf->virt)
+		dma_free_coherent(dev->dev, buf->len, buf->virt, buf->phys);
+
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int prpenc_alloc_dma_buf(struct prpenc_priv *priv,
+				 struct imxcam_dma_buf *buf,
+				 int size)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	prpenc_free_dma_buf(priv, buf);
+
+	buf->len = PAGE_ALIGN(size);
+	buf->virt = dma_alloc_coherent(dev->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		v4l2_err(&priv->sd, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void prpenc_setup_channel(struct prpenc_priv *priv,
+				  struct ipuv3_channel *channel,
+				  enum ipu_rotate_mode rot_mode,
+				  dma_addr_t addr0, dma_addr_t addr1,
+				  bool rot_swap_width_height)
+{
+	struct imxcam_dev *dev = priv->dev;
+	u32 width, height, stride;
+	unsigned int burst_size;
+	struct ipu_image image;
+
+	if (rot_swap_width_height) {
+		width = priv->outf.height;
+		height = priv->outf.width;
+	} else {
+		width = priv->outf.width;
+		height = priv->outf.height;
+	}
+
+	stride = dev->user_pixfmt->y_depth ?
+		(width * dev->user_pixfmt->y_depth) >> 3 :
+		(width * dev->user_pixfmt->bpp) >> 3;
+
+	ipu_cpmem_zero(channel);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = width;
+	image.pix.height = image.rect.height = height;
+	image.pix.bytesperline = stride;
+	image.pix.pixelformat = priv->outf.pixelformat;
+	image.phys0 = addr0;
+	image.phys1 = addr1;
+	ipu_cpmem_set_image(channel, &image);
+
+	if (channel == priv->enc_rot_in_ch ||
+	    channel == priv->enc_rot_out_ch) {
+		burst_size = 8;
+		ipu_cpmem_set_block_mode(channel);
+	} else {
+		burst_size = (width & 0xf) ? 8 : 16;
+	}
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	if (rot_mode)
+		ipu_cpmem_set_rotation(channel, rot_mode);
+
+	if (V4L2_FIELD_HAS_BOTH(priv->inf.field) && channel == priv->enc_ch)
+		ipu_cpmem_interlaced_scan(channel, stride);
+
+	ipu_ic_task_idma_init(priv->ic_enc, channel, width, height,
+			      burst_size, rot_mode);
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, true);
+}
+
+static int prpenc_setup_rotation(struct prpenc_priv *priv,
+				  dma_addr_t phys0, dma_addr_t phys1)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int ret;
+
+	ret = prpenc_alloc_dma_buf(priv, &priv->underrun_buf,
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		return ret;
+	}
+
+	ret = prpenc_alloc_dma_buf(priv, &priv->rot_buf[0],
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc rot_buf[0], %d\n", ret);
+		goto free_underrun;
+	}
+	ret = prpenc_alloc_dma_buf(priv, &priv->rot_buf[1],
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc rot_buf[1], %d\n", ret);
+		goto free_rot0;
+	}
+
+	ret = ipu_ic_task_init(priv->ic_enc,
+			       priv->inf.width, priv->inf.height,
+			       priv->outf.height, priv->outf.width,
+			       priv->in_cs, priv->out_cs);
+	if (ret) {
+		v4l2_err(&priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+		goto free_rot1;
+	}
+
+	/* init the IC ENC-->MEM IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_ch,
+			      IPU_ROTATE_NONE,
+			      priv->rot_buf[0].phys,
+			      priv->rot_buf[1].phys,
+			      true);
+
+	/* init the MEM-->IC ENC ROT IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_rot_in_ch,
+			      dev->rot_mode,
+			      priv->rot_buf[0].phys,
+			      priv->rot_buf[1].phys,
+			      true);
+
+	/* init the destination IC ENC ROT-->MEM IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_rot_out_ch,
+			      IPU_ROTATE_NONE,
+			      phys0, phys1,
+			      false);
+
+	/* now link IC ENC-->MEM to MEM-->IC ENC ROT */
+	ipu_idmac_link(priv->enc_ch, priv->enc_rot_in_ch);
+
+	/* enable the IC */
+	ipu_ic_enable(priv->ic_enc);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->enc_ch, 0);
+	ipu_idmac_select_buffer(priv->enc_ch, 1);
+	ipu_idmac_select_buffer(priv->enc_rot_out_ch, 0);
+	ipu_idmac_select_buffer(priv->enc_rot_out_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->enc_ch);
+	ipu_idmac_enable_channel(priv->enc_rot_in_ch);
+	ipu_idmac_enable_channel(priv->enc_rot_out_ch);
+
+	/* and finally enable the IC PRPENC task */
+	ipu_ic_task_enable(priv->ic_enc);
+
+	return 0;
+
+free_rot1:
+	prpenc_free_dma_buf(priv, &priv->rot_buf[1]);
+free_rot0:
+	prpenc_free_dma_buf(priv, &priv->rot_buf[0]);
+free_underrun:
+	prpenc_free_dma_buf(priv, &priv->underrun_buf);
+	return ret;
+}
+
+static int prpenc_setup_norotation(struct prpenc_priv *priv,
+				    dma_addr_t phys0, dma_addr_t phys1)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int ret;
+
+	ret = prpenc_alloc_dma_buf(priv, &priv->underrun_buf,
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		return ret;
+	}
+
+	ret = ipu_ic_task_init(priv->ic_enc,
+			       priv->inf.width, priv->inf.height,
+			       priv->outf.width, priv->outf.height,
+			       priv->in_cs, priv->out_cs);
+	if (ret) {
+		v4l2_err(&priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+		goto free_underrun;
+	}
+
+	/* init the IC PRP-->MEM IDMAC channel */
+	prpenc_setup_channel(priv, priv->enc_ch, dev->rot_mode,
+			      phys0, phys1, false);
+
+	ipu_cpmem_dump(priv->enc_ch);
+	ipu_ic_dump(priv->ic_enc);
+	ipu_dump(priv->ipu);
+
+	ipu_ic_enable(priv->ic_enc);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->enc_ch, 0);
+	ipu_idmac_select_buffer(priv->enc_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->enc_ch);
+
+	/* enable the IC ENCODE task */
+	ipu_ic_task_enable(priv->ic_enc);
+
+	return 0;
+
+free_underrun:
+	prpenc_free_dma_buf(priv, &priv->underrun_buf);
+	return ret;
+}
+
+static int prpenc_start(struct prpenc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct imxcam_buffer *frame, *tmp;
+	unsigned long flags;
+	dma_addr_t phys[2] = {0};
+	int i = 0, ret;
+
+	ret = prpenc_get_ipu_resources(priv);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	list_for_each_entry_safe(frame, tmp, &dev->ready_q, list) {
+		phys[i] = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[i++] = frame;
+		if (i >= 2)
+			break;
+	}
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	priv->inf = dev->sensor_fmt;
+	priv->inf.width = dev->crop.width;
+	priv->inf.height = dev->crop.height;
+	priv->in_cs = ipu_mbus_code_to_colorspace(priv->inf.code);
+
+	priv->outf = dev->user_fmt.fmt.pix;
+	priv->out_cs = ipu_pixelformat_to_colorspace(priv->outf.pixelformat);
+
+	priv->buf_num = 0;
+
+	/* init EOF completion waitq */
+	init_completion(&priv->last_eof_comp);
+	priv->last_eof = false;
+
+	/* set IC to receive from CSI */
+	ipu_ic_set_src(priv->ic_enc, csi_id, false);
+
+	if (ipu_rot_mode_is_irt(dev->rot_mode))
+		ret = prpenc_setup_rotation(priv, phys[0], phys[1]);
+	else
+		ret = prpenc_setup_norotation(priv, phys[0], phys[1]);
+	if (ret)
+		goto out_put_ipu;
+
+	priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						 priv->enc_ch,
+						 IPU_IRQ_NFB4EOF);
+	ret = devm_request_irq(dev->dev, priv->nfb4eof_irq,
+			       prpenc_nfb4eof_interrupt, 0,
+			       "imxcam-enc-nfb4eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode NFB4EOF irq: %d\n", ret);
+		goto out_put_ipu;
+	}
+
+	if (ipu_rot_mode_is_irt(dev->rot_mode))
+		priv->eof_irq = ipu_idmac_channel_irq(
+			priv->ipu, priv->enc_rot_out_ch, IPU_IRQ_EOF);
+	else
+		priv->eof_irq = ipu_idmac_channel_irq(
+			priv->ipu, priv->enc_ch, IPU_IRQ_EOF);
+
+	ret = devm_request_irq(dev->dev, priv->eof_irq,
+			       prpenc_eof_interrupt, 0,
+			       "imxcam-enc-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode eof irq: %d\n", ret);
+		goto out_free_nfb4eof_irq;
+	}
+
+	/* sensor stream on */
+	ret = dev->sensor_set_stream(dev, 1);
+	if (ret) {
+		v4l2_err(&priv->sd, "sensor stream on failed\n");
+		goto out_free_eof_irq;
+	}
+
+	/* start the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	return 0;
+
+out_free_eof_irq:
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+out_free_nfb4eof_irq:
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+out_put_ipu:
+	prpenc_put_ipu_resources(priv);
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		vb2_buffer_done(&frame->vb, VB2_BUF_STATE_QUEUED);
+	}
+	return ret;
+}
+
+static int prpenc_stop(struct prpenc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+	int i, ret;
+
+	/* mark next EOF interrupt as the last before stream off */
+	spin_lock_irqsave(&dev->irqlock, flags);
+	priv->last_eof = true;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/*
+	 * and then wait for interrupt handler to mark completion.
+	 */
+	ret = wait_for_completion_timeout(&priv->last_eof_comp,
+					  msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+	if (ret == 0)
+		v4l2_warn(&priv->sd, "wait last encode EOF timeout\n");
+
+	/* sensor stream off */
+	ret = dev->sensor_set_stream(dev, 0);
+	if (ret)
+		v4l2_warn(&priv->sd, "sensor stream off failed\n");
+
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+
+	/* disable IC tasks and the channels */
+	ipu_ic_task_disable(priv->ic_enc);
+
+	ipu_idmac_disable_channel(priv->enc_ch);
+	if (ipu_rot_mode_is_irt(dev->rot_mode)) {
+		ipu_idmac_disable_channel(priv->enc_rot_in_ch);
+		ipu_idmac_disable_channel(priv->enc_rot_out_ch);
+	}
+
+	if (ipu_rot_mode_is_irt(dev->rot_mode))
+		ipu_idmac_unlink(priv->enc_ch, priv->enc_rot_in_ch);
+
+	ipu_ic_disable(priv->ic_enc);
+
+	prpenc_free_dma_buf(priv, &priv->rot_buf[0]);
+	prpenc_free_dma_buf(priv, &priv->rot_buf[1]);
+	prpenc_free_dma_buf(priv, &priv->underrun_buf);
+
+	prpenc_put_ipu_resources(priv);
+
+	/* cancel the EOF timeout timer */
+	del_timer_sync(&priv->eof_timeout_timer);
+
+	/* return any remaining active frames with error */
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame && frame->vb.state == VB2_BUF_STATE_ACTIVE) {
+			frame->vb.timestamp = ktime_get_ns();
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+		}
+	}
+
+	return 0;
+}
+
+static int prpenc_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct prpenc_priv *priv = v4l2_get_subdevdata(sd);
+
+	return enable ? prpenc_start(priv) : prpenc_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops prpenc_video_ops = {
+	.s_stream = prpenc_s_stream,
+};
+
+static struct v4l2_subdev_ops prpenc_subdev_ops = {
+	.video = &prpenc_video_ops,
+};
+
+struct v4l2_subdev *imxcam_ic_prpenc_init(struct imxcam_dev *dev)
+{
+	struct prpenc_priv *priv;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	init_timer(&priv->eof_timeout_timer);
+	priv->eof_timeout_timer.data = (unsigned long)priv;
+	priv->eof_timeout_timer.function = prpenc_eof_timeout;
+
+	v4l2_subdev_init(&priv->sd, &prpenc_subdev_ops);
+	strlcpy(priv->sd.name, "imx-camera-prpenc", sizeof(priv->sd.name));
+	v4l2_set_subdevdata(&priv->sd, priv);
+
+	priv->dev = dev;
+	return &priv->sd;
+}
diff --git a/drivers/staging/media/imx/capture/imx-of.c b/drivers/staging/media/imx/capture/imx-of.c
new file mode 100644
index 0000000..b6c5675
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-of.c
@@ -0,0 +1,354 @@
+/*
+ * Video Camera Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Open Firmware parsing.
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/of_platform.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include "imx-camif.h"
+
+/* parse inputs property from a sensor's upstream sink endpoint node */
+static void of_parse_sensor_inputs(struct imxcam_dev *dev,
+				   struct device_node *sink_ep,
+				   struct imxcam_sensor *sensor)
+{
+	struct imxcam_sensor_input *sinput = &sensor->input;
+	int next_input = dev->num_sensor_inputs;
+	int ret, i;
+
+	for (i = 0; i < IMXCAM_MAX_INPUTS; i++) {
+		const char *input_name;
+		u32 val;
+
+		ret = of_property_read_u32_index(sink_ep, "inputs", i, &val);
+		if (ret)
+			break;
+
+		sinput->value[i] = val;
+
+		ret = of_property_read_string_index(sink_ep, "input-names", i,
+						    &input_name);
+		/*
+		 * if input-names not provided, they will be set using
+		 * the subdev name once the subdev is known during
+		 * async bind
+		 */
+		if (!ret)
+			strncpy(sinput->name[i], input_name,
+				sizeof(sinput->name[i]));
+
+		val = 0;
+		ret = of_property_read_u32_index(sink_ep, "input-caps",
+						 i, &val);
+		sinput->caps[i] = val;
+	}
+
+	sinput->num = i;
+
+	/* if no inputs provided just assume a single input */
+	if (sinput->num == 0) {
+		sinput->num = 1;
+		sinput->caps[0] = 0;
+	}
+
+	sinput->first = next_input;
+	sinput->last = next_input + sinput->num - 1;
+
+	dev->num_sensor_inputs = sinput->last + 1;
+}
+
+static int of_parse_sensor(struct imxcam_dev *dev,
+			   struct imxcam_sensor *sensor,
+			   struct device_node *sink_ep,
+			   struct device_node *csi_port,
+			   struct device_node *sensor_node)
+{
+	struct device_node *sensor_ep, *csi_ep;
+
+	sensor_ep = of_graph_get_next_endpoint(sensor_node, NULL);
+	if (!sensor_ep)
+		return -EINVAL;
+	csi_ep = of_get_next_child(csi_port, NULL);
+	if (!csi_ep) {
+		of_node_put(sensor_ep);
+		return -EINVAL;
+	}
+
+	sensor->csi_np = csi_port;
+
+	v4l2_of_parse_endpoint(sensor_ep, &sensor->ep);
+	v4l2_of_parse_endpoint(csi_ep, &sensor->csi_ep);
+
+	of_parse_sensor_inputs(dev, sink_ep, sensor);
+
+	of_node_put(sensor_ep);
+	of_node_put(csi_ep);
+	return 0;
+}
+
+static struct v4l2_async_subdev *add_async_subdev(struct imxcam_dev *dev,
+						  struct device_node *np)
+{
+	struct v4l2_async_subdev *asd;
+	int asd_idx;
+
+	asd_idx = dev->subdev_notifier.num_subdevs;
+	if (asd_idx >= IMXCAM_MAX_SUBDEVS)
+		return ERR_PTR(-ENOSPC);
+
+	asd = &dev->async_desc[asd_idx];
+	dev->async_ptrs[asd_idx] = asd;
+
+	asd->match_type = V4L2_ASYNC_MATCH_OF;
+	asd->match.of.node = np;
+	dev->subdev_notifier.num_subdevs++;
+
+	dev_dbg(dev->dev, "%s: added %s, num %d, node %p\n",
+		__func__, np->name, dev->subdev_notifier.num_subdevs, np);
+
+	return asd;
+}
+
+/* Discover all the subdevices we need downstream from a sink endpoint */
+static int of_discover_subdevs(struct imxcam_dev *dev,
+			       struct device_node *csi_port,
+			       struct device_node *sink_ep,
+			       int *vidmux_input)
+{
+	struct device_node *rpp, *epnode = NULL;
+	struct v4l2_async_subdev *asd;
+	struct imxcam_sensor *sensor;
+	int sensor_idx, num_sink_ports;
+	int i, vidmux_idx = -1, ret = 0;
+
+	rpp = of_graph_get_remote_port_parent(sink_ep);
+	if (!rpp)
+		return 0;
+	if (!of_device_is_available(rpp))
+		goto out;
+
+	asd = add_async_subdev(dev, rpp);
+	if (IS_ERR(asd)) {
+		ret = PTR_ERR(asd);
+		goto out;
+	}
+
+	if (of_device_is_compatible(rpp, "fsl,imx-mipi-csi2")) {
+		/*
+		 * there is only one internal mipi receiver, so exit
+		 * with 0 if we've already passed through here
+		 */
+		if (dev->csi2_asd) {
+			dev->subdev_notifier.num_subdevs--;
+			ret = 0;
+			goto out;
+		}
+
+		/* the mipi csi2 receiver has only one sink port */
+		num_sink_ports = 1;
+		dev->csi2_asd = asd;
+		dev_dbg(dev->dev, "found mipi-csi2 %s\n", rpp->name);
+	} else if (of_device_is_compatible(rpp, "imx-video-mux")) {
+		/* for the video mux, all but the last port are sinks */
+		num_sink_ports = of_get_child_count(rpp) - 1;
+
+		vidmux_idx = dev->num_vidmux;
+		if (vidmux_idx >= IMXCAM_MAX_VIDEOMUX) {
+			ret = -ENOSPC;
+			goto out;
+		}
+
+		dev->vidmux_asd[vidmux_idx] = asd;
+		dev->num_vidmux++;
+		dev_dbg(dev->dev, "found video mux %s\n", rpp->name);
+	} else {
+		/* this rpp must be a sensor, it has no sink ports */
+		num_sink_ports = 0;
+
+		sensor_idx = dev->num_sensors;
+		if (sensor_idx >= IMXCAM_MAX_SENSORS)
+			return -ENOSPC;
+
+		sensor = &dev->sensor_list[sensor_idx];
+
+		ret = of_parse_sensor(dev, sensor, sink_ep, csi_port, rpp);
+		if (ret)
+			goto out;
+
+		/*
+		 * save the input indeces of all video-muxes recorded in
+		 * this pipeline path required to receive data from this
+		 * sensor.
+		 */
+		memcpy(sensor->vidmux_input, vidmux_input,
+		       sizeof(sensor->vidmux_input));
+
+		sensor->asd = asd;
+		dev->num_sensors++;
+		dev_dbg(dev->dev, "found sensor %s\n", rpp->name);
+	}
+
+	/* continue discovery downstream */
+	dev_dbg(dev->dev, "scanning %d sink ports on %s\n",
+		num_sink_ports, rpp->name);
+
+	for (i = 0; i < num_sink_ports; i++) {
+		epnode = of_graph_get_next_endpoint(rpp, epnode);
+		if (!epnode) {
+			v4l2_err(&dev->v4l2_dev,
+				 "no endpoint at port %d on %s\n",
+				 i, rpp->name);
+			ret = -EINVAL;
+			break;
+		}
+
+		if (vidmux_idx >= 0)
+			vidmux_input[vidmux_idx] = i;
+
+		ret = of_discover_subdevs(dev, csi_port, epnode, vidmux_input);
+		of_node_put(epnode);
+		if (ret)
+			break;
+	}
+
+out:
+	of_node_put(rpp);
+	return ret;
+}
+
+static int of_parse_ports(struct imxcam_dev *dev, struct device_node *np)
+{
+	struct device_node *port, *epnode;
+	struct v4l2_async_subdev *asd;
+	int vidmux_inputs[IMXCAM_MAX_VIDEOMUX];
+	int i, j, csi_idx, ret = 0;
+
+	for (i = 0; ; i++) {
+		port = of_parse_phandle(np, "ports", i);
+		if (!port) {
+			ret = 0;
+			break;
+		}
+
+		csi_idx = dev->num_csi;
+		if (csi_idx >= IMXCAM_MAX_CSI) {
+			ret = -ENOSPC;
+			break;
+		}
+		/* register the CSI subdev */
+		asd = add_async_subdev(dev, port);
+		if (IS_ERR(asd)) {
+			ret = PTR_ERR(asd);
+			break;
+		}
+		dev->csi_asd[csi_idx] = asd;
+		dev->num_csi++;
+
+		/*
+		 * discover and register all async subdevs downstream
+		 * from this CSI port.
+		 */
+		for_each_child_of_node(port, epnode) {
+			for (j = 0; j < IMXCAM_MAX_VIDEOMUX; j++)
+				vidmux_inputs[j] = -1;
+
+			ret = of_discover_subdevs(dev, port, epnode,
+						  vidmux_inputs);
+			of_node_put(epnode);
+			if (ret)
+				break;
+		}
+
+		of_node_put(port);
+		if (ret)
+			break;
+	}
+
+	if (ret)
+		return ret;
+
+	if (!dev->num_sensors) {
+		v4l2_err(&dev->v4l2_dev, "no sensors found!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int of_parse_fim(struct imxcam_dev *dev, struct device_node *np)
+{
+	struct imxcam_fim *fim = &dev->fim;
+	struct device_node *fim_np;
+	u32 val, tol[2], icap[2];
+	int ret;
+
+	fim_np = of_get_child_by_name(np, "fim");
+	if (!fim_np) {
+		/* set to the default defaults */
+		fim->of_defaults[FIM_CL_ENABLE] = FIM_CL_ENABLE_DEF;
+		fim->of_defaults[FIM_CL_NUM] = FIM_CL_NUM_DEF;
+		fim->of_defaults[FIM_CL_NUM_SKIP] = FIM_CL_NUM_SKIP_DEF;
+		fim->of_defaults[FIM_CL_TOLERANCE_MIN] =
+			FIM_CL_TOLERANCE_MIN_DEF;
+		fim->of_defaults[FIM_CL_TOLERANCE_MAX] =
+			FIM_CL_TOLERANCE_MAX_DEF;
+		fim->icap_channel = -1;
+		return 0;
+	}
+
+	ret = of_property_read_u32(fim_np, "enable", &val);
+	if (ret)
+		val = FIM_CL_ENABLE_DEF;
+	fim->of_defaults[FIM_CL_ENABLE] = val;
+
+	ret = of_property_read_u32(fim_np, "num-avg", &val);
+	if (ret)
+		val = FIM_CL_NUM_DEF;
+	fim->of_defaults[FIM_CL_NUM] = val;
+
+	ret = of_property_read_u32(fim_np, "num-skip", &val);
+	if (ret)
+		val = FIM_CL_NUM_SKIP_DEF;
+	fim->of_defaults[FIM_CL_NUM_SKIP] = val;
+
+	ret = of_property_read_u32_array(fim_np, "tolerance-range", tol, 2);
+	if (ret) {
+		tol[0] = FIM_CL_TOLERANCE_MIN_DEF;
+		tol[1] = FIM_CL_TOLERANCE_MAX_DEF;
+	}
+	fim->of_defaults[FIM_CL_TOLERANCE_MIN] = tol[0];
+	fim->of_defaults[FIM_CL_TOLERANCE_MAX] = tol[1];
+
+	ret = of_property_read_u32_array(fim_np, "input-capture-channel",
+					 icap, 2);
+	if (!ret) {
+		fim->icap_channel = icap[0];
+		fim->icap_flags = icap[1];
+	} else {
+		fim->icap_channel = -1;
+	}
+
+	of_node_put(fim_np);
+	return 0;
+}
+
+int imxcam_of_parse(struct imxcam_dev *dev, struct device_node *np)
+{
+	int ret = of_parse_fim(dev, np);
+	if (ret)
+		return ret;
+
+	return of_parse_ports(dev, np);
+}
diff --git a/drivers/staging/media/imx/capture/imx-of.h b/drivers/staging/media/imx/capture/imx-of.h
new file mode 100644
index 0000000..6f233bf
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-of.h
@@ -0,0 +1,18 @@
+/*
+ * Video Camera Capture driver for Freescale i.MX5/6 SOC
+ *
+ * Open Firmware parsing.
+ *
+ * Copyright (c) 2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef _IMX_CAM_OF_H
+#define _IMX_CAM_OF_H
+
+extern int imxcam_of_parse(struct imxcam_dev *dev, struct device_node *np);
+
+#endif
diff --git a/drivers/staging/media/imx/capture/imx-smfc.c b/drivers/staging/media/imx/capture/imx-smfc.c
new file mode 100644
index 0000000..c164992
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-smfc.c
@@ -0,0 +1,506 @@
+/*
+ * V4L2 Capture SMFC Subdev for Freescale i.MX5/6 SOC
+ *
+ * This subdevice handles capture of raw/unconverted video frames
+ * from the CSI, directly to memory via the Sensor Multi-FIFO Controller.
+ *
+ * Copyright (c) 2012-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+
+struct imx_smfc_priv {
+	struct imxcam_dev    *dev;
+	struct v4l2_subdev    sd;
+
+	struct ipu_soc *ipu;
+	struct ipuv3_channel *smfc_ch;
+	struct ipu_smfc *smfc;
+
+	struct v4l2_mbus_framefmt inf; /* input sensor format */
+	struct v4l2_pix_format outf;   /* output user format */
+
+	/* active (undergoing DMA) buffers, one for each IPU buffer */
+	struct imxcam_buffer *active_frame[2];
+
+	struct imxcam_dma_buf underrun_buf;
+	int buf_num;
+
+	struct timer_list eof_timeout_timer;
+	int eof_irq;
+	int nfb4eof_irq;
+
+	bool last_eof;  /* waiting for last EOF at stream off */
+	struct completion last_eof_comp;
+};
+
+static void imx_smfc_put_ipu_resources(struct imx_smfc_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->smfc_ch))
+		ipu_idmac_put(priv->smfc_ch);
+	priv->smfc_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->smfc))
+		ipu_smfc_put(priv->smfc);
+	priv->smfc = NULL;
+}
+
+static int imx_smfc_get_ipu_resources(struct imx_smfc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct v4l2_subdev *csi_sd = dev->sensor->csi_sd;
+	int csi_ch_num, ret;
+
+	priv->ipu = dev_get_drvdata(csi_sd->dev->parent);
+
+	/*
+	 * Choose the direct CSI-->SMFC-->MEM channel corresponding
+	 * to the IPU and CSI IDs.
+	 */
+	csi_ch_num = IPUV3_CHANNEL_CSI0 +
+		(ipu_get_num(priv->ipu) << 1) + csi_id;
+
+	priv->smfc = ipu_smfc_get(priv->ipu, csi_ch_num);
+	if (IS_ERR(priv->smfc)) {
+		v4l2_err(&priv->sd, "failed to get SMFC\n");
+		ret = PTR_ERR(priv->smfc);
+		goto out;
+	}
+
+	priv->smfc_ch = ipu_idmac_get(priv->ipu, csi_ch_num);
+	if (IS_ERR(priv->smfc_ch)) {
+		v4l2_err(&priv->sd, "could not get IDMAC channel %u\n",
+			 csi_ch_num);
+		ret = PTR_ERR(priv->smfc_ch);
+		goto out;
+	}
+
+	return 0;
+out:
+	imx_smfc_put_ipu_resources(priv);
+	return ret;
+}
+
+static irqreturn_t imx_smfc_eof_interrupt(int irq, void *dev_id)
+{
+	struct imx_smfc_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	enum vb2_buffer_state state;
+	struct timeval cur_timeval;
+	unsigned long flags;
+	u64 cur_time_ns;
+	dma_addr_t phys;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	cur_time_ns = ktime_get_ns();
+	cur_timeval = ns_to_timeval(cur_time_ns);
+
+	/* timestamp and return the completed frame */
+	frame = priv->active_frame[priv->buf_num];
+	if (frame) {
+		frame->vb.timestamp = cur_time_ns;
+		state = (dev->signal_locked &&
+			 !atomic_read(&dev->pending_restart)) ?
+			VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
+		vb2_buffer_done(&frame->vb, state);
+	}
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	if (dev->fim.eof && dev->fim.eof(dev, &cur_timeval))
+		v4l2_subdev_notify(&priv->sd, IMXCAM_FRAME_INTERVAL_NOTIFY,
+				   NULL);
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	if (!list_empty(&dev->ready_q)) {
+		frame = list_entry(dev->ready_q.next,
+				   struct imxcam_buffer, list);
+		phys = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[priv->buf_num] = frame;
+	} else {
+		phys = priv->underrun_buf.phys;
+		priv->active_frame[priv->buf_num] = NULL;
+	}
+
+	if (ipu_idmac_buffer_is_ready(priv->smfc_ch, priv->buf_num))
+		ipu_idmac_clear_buffer(priv->smfc_ch, priv->buf_num);
+
+	ipu_cpmem_set_buffer(priv->smfc_ch, priv->buf_num, phys);
+	ipu_idmac_select_buffer(priv->smfc_ch, priv->buf_num);
+
+	priv->buf_num ^= 1;
+
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_smfc_nfb4eof_interrupt(int irq, void *dev_id)
+{
+	struct imx_smfc_priv *priv = dev_id;
+
+	v4l2_err(&priv->sd, "NFB4EOF\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_NFB4EOF_NOTIFY, NULL);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function.
+ */
+static void imx_smfc_eof_timeout(unsigned long data)
+{
+	struct imx_smfc_priv *priv = (struct imx_smfc_priv *)data;
+
+	v4l2_err(&priv->sd, "EOF timeout\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_EOF_TIMEOUT_NOTIFY, NULL);
+}
+
+static void imx_smfc_free_dma_buf(struct imx_smfc_priv *priv,
+				 struct imxcam_dma_buf *buf)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	if (buf->virt)
+		dma_free_coherent(dev->dev, buf->len, buf->virt, buf->phys);
+
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int imx_smfc_alloc_dma_buf(struct imx_smfc_priv *priv,
+				 struct imxcam_dma_buf *buf,
+				 int size)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	imx_smfc_free_dma_buf(priv, buf);
+
+	buf->len = PAGE_ALIGN(size);
+	buf->virt = dma_alloc_coherent(dev->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		v4l2_err(&priv->sd, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/* init the IC PRPENC-->MEM IDMAC channel */
+static void imx_smfc_setup_channel(struct imx_smfc_priv *priv,
+				  dma_addr_t addr0, dma_addr_t addr1,
+				  bool rot_swap_width_height)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	int vc_num = dev->sensor->csi_ep.base.id;
+	u32 width, height, stride;
+	unsigned int burst_size;
+	struct ipu_image image;
+	bool passthrough;
+
+	width = priv->outf.width;
+	height = priv->outf.height;
+
+	stride = dev->user_pixfmt->y_depth ?
+		(width * dev->user_pixfmt->y_depth) >> 3 :
+		(width * dev->user_pixfmt->bpp) >> 3;
+
+	ipu_cpmem_zero(priv->smfc_ch);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = width;
+	image.pix.height = image.rect.height = height;
+	image.pix.bytesperline = stride;
+	image.pix.pixelformat = priv->outf.pixelformat;
+	image.phys0 = addr0;
+	image.phys1 = addr1;
+	ipu_cpmem_set_image(priv->smfc_ch, &image);
+
+	burst_size = (width & 0xf) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(priv->smfc_ch, burst_size);
+
+	/*
+	 * If the sensor uses 16-bit parallel CSI bus, we must handle
+	 * the data internally in the IPU as 16-bit generic, aka
+	 * passthrough mode.
+	 */
+	passthrough = (dev->sensor->ep.bus_type != V4L2_MBUS_CSI2 &&
+		       dev->sensor->ep.bus.parallel.bus_width >= 16);
+
+	if (passthrough)
+		ipu_cpmem_set_format_passthrough(priv->smfc_ch, 16);
+
+	if (dev->sensor->ep.bus_type == V4L2_MBUS_CSI2)
+		ipu_smfc_map_channel(priv->smfc, csi_id, vc_num);
+	else
+		ipu_smfc_map_channel(priv->smfc, csi_id, 0);
+
+	/*
+	 * Set the channel for the direct CSI-->memory via SMFC
+	 * use-case to very high priority, by enabling the watermark
+	 * signal in the SMFC, enabling WM in the channel, and setting
+	 * the channel priority to high.
+	 *
+	 * Refer to the i.mx6 rev. D TRM Table 36-8: Calculated priority
+	 * value.
+	 *
+	 * The WM's are set very low by intention here to ensure that
+	 * the SMFC FIFOs do not overflow.
+	 */
+	ipu_smfc_set_watermark(priv->smfc, 0x02, 0x01);
+	ipu_cpmem_set_high_priority(priv->smfc_ch);
+	ipu_idmac_enable_watermark(priv->smfc_ch, true);
+	ipu_cpmem_set_axi_id(priv->smfc_ch, 0);
+	ipu_idmac_lock_enable(priv->smfc_ch, 8);
+
+	burst_size = ipu_cpmem_get_burstsize(priv->smfc_ch);
+	burst_size = passthrough ? (burst_size >> 3) - 1 : (burst_size >> 2) - 1;
+
+	ipu_smfc_set_burstsize(priv->smfc, burst_size);
+
+	if (V4L2_FIELD_HAS_BOTH(priv->inf.field))
+		ipu_cpmem_interlaced_scan(priv->smfc_ch, stride);
+
+	ipu_idmac_set_double_buffer(priv->smfc_ch, true);
+}
+
+static int imx_smfc_setup(struct imx_smfc_priv *priv,
+			  dma_addr_t phys0, dma_addr_t phys1)
+{
+	int ret;
+
+	ret = imx_smfc_alloc_dma_buf(priv, &priv->underrun_buf,
+				    priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		return ret;
+	}
+
+	imx_smfc_setup_channel(priv, phys0, phys1, false);
+
+	ipu_cpmem_dump(priv->smfc_ch);
+	ipu_dump(priv->ipu);
+
+	ipu_smfc_enable(priv->smfc);
+
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->smfc_ch, 0);
+	ipu_idmac_select_buffer(priv->smfc_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->smfc_ch);
+
+	return 0;
+}
+
+static int imx_smfc_start(struct imx_smfc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame, *tmp;
+	dma_addr_t phys[2] = {0};
+	unsigned long flags;
+	int i = 0, ret;
+
+	ret = imx_smfc_get_ipu_resources(priv);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	list_for_each_entry_safe(frame, tmp, &dev->ready_q, list) {
+		phys[i] = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[i++] = frame;
+		if (i >= 2)
+			break;
+	}
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	priv->inf = dev->sensor_fmt;
+	priv->inf.width = dev->crop.width;
+	priv->inf.height = dev->crop.height;
+	priv->outf = dev->user_fmt.fmt.pix;
+
+	priv->buf_num = 0;
+
+	/* init EOF completion waitq */
+	init_completion(&priv->last_eof_comp);
+	priv->last_eof = false;
+
+	ret = imx_smfc_setup(priv, phys[0], phys[1]);
+	if (ret)
+		goto out_put_ipu;
+
+	priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						 priv->smfc_ch,
+						 IPU_IRQ_NFB4EOF);
+	ret = devm_request_irq(dev->dev, priv->nfb4eof_irq,
+			       imx_smfc_nfb4eof_interrupt, 0,
+			       "imxcam-enc-nfb4eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode NFB4EOF irq: %d\n", ret);
+		goto out_put_ipu;
+	}
+
+	priv->eof_irq = ipu_idmac_channel_irq(priv->ipu, priv->smfc_ch,
+					      IPU_IRQ_EOF);
+
+	ret = devm_request_irq(dev->dev, priv->eof_irq,
+			       imx_smfc_eof_interrupt, 0,
+			       "imxcam-enc-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering encode eof irq: %d\n", ret);
+		goto out_free_nfb4eof_irq;
+	}
+
+	/* sensor stream on */
+	ret = dev->sensor_set_stream(dev, 1);
+	if (ret) {
+		v4l2_err(&priv->sd, "sensor stream on failed\n");
+		goto out_free_eof_irq;
+	}
+
+	/* start the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	return 0;
+
+out_free_eof_irq:
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+out_free_nfb4eof_irq:
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+out_put_ipu:
+	imx_smfc_put_ipu_resources(priv);
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		vb2_buffer_done(&frame->vb, VB2_BUF_STATE_QUEUED);
+	}
+	return ret;
+}
+
+static int imx_smfc_stop(struct imx_smfc_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+	int i, ret;
+
+	/* mark next EOF interrupt as the last before stream off */
+	spin_lock_irqsave(&dev->irqlock, flags);
+	priv->last_eof = true;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/*
+	 * and then wait for interrupt handler to mark completion.
+	 */
+	ret = wait_for_completion_timeout(&priv->last_eof_comp,
+					  msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+	if (ret == 0)
+		v4l2_warn(&priv->sd, "wait last encode EOF timeout\n");
+
+	/* sensor stream off */
+	ret = dev->sensor_set_stream(dev, 0);
+	if (ret)
+		v4l2_warn(&priv->sd, "sensor stream off failed\n");
+
+	devm_free_irq(dev->dev, priv->eof_irq, priv);
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+
+	ipu_idmac_disable_channel(priv->smfc_ch);
+
+	ipu_smfc_disable(priv->smfc);
+
+	imx_smfc_free_dma_buf(priv, &priv->underrun_buf);
+
+	imx_smfc_put_ipu_resources(priv);
+
+	/* cancel the EOF timeout timer */
+	del_timer_sync(&priv->eof_timeout_timer);
+
+	/* return any remaining active frames with error */
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame && frame->vb.state == VB2_BUF_STATE_ACTIVE) {
+			frame->vb.timestamp = ktime_get_ns();
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+		}
+	}
+
+	return 0;
+}
+
+static int imx_smfc_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imx_smfc_priv *priv = v4l2_get_subdevdata(sd);
+
+	return enable ? imx_smfc_start(priv) : imx_smfc_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops imx_smfc_video_ops = {
+	.s_stream = imx_smfc_s_stream,
+};
+
+static struct v4l2_subdev_ops imx_smfc_subdev_ops = {
+	.video = &imx_smfc_video_ops,
+};
+
+struct v4l2_subdev *imxcam_smfc_init(struct imxcam_dev *dev)
+{
+	struct imx_smfc_priv *priv;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	init_timer(&priv->eof_timeout_timer);
+	priv->eof_timeout_timer.data = (unsigned long)priv;
+	priv->eof_timeout_timer.function = imx_smfc_eof_timeout;
+
+	v4l2_subdev_init(&priv->sd, &imx_smfc_subdev_ops);
+	strlcpy(priv->sd.name, "imx-camera-smfc", sizeof(priv->sd.name));
+	v4l2_set_subdevdata(&priv->sd, priv);
+
+	priv->dev = dev;
+	return &priv->sd;
+}
diff --git a/drivers/staging/media/imx/capture/imx-vdic.c b/drivers/staging/media/imx/capture/imx-vdic.c
new file mode 100644
index 0000000..60a3192
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-vdic.c
@@ -0,0 +1,995 @@
+/*
+ * V4L2 Capture Deinterlacer Subdev for Freescale i.MX5/6 SOC
+ *
+ * Copyright (c) 2014-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+#include "imx-camif.h"
+
+/*
+ * This subdev implements two different video pipelines:
+ *
+ * CSI -> VDIC -> IC -> CH21 -> MEM
+ *
+ * In this pipeline, the CSI sends a single interlaced field F(n-1)
+ * directly to the VDIC (and optionally the following field F(n)
+ * can be sent to memory via IDMAC channel 13). So only two fields
+ * can be processed by the VDIC. This pipeline only works in VDIC's
+ * high motion mode, which only requires a single field for processing.
+ * The other motion modes (low and medium) require three fields, so this
+ * pipeline does not work in those modes. Also, it is not clear how this
+ * pipeline can deal with the various field orders (sequential BT/TB,
+ * interlaced BT/TB) and there are reported image quality issues output
+ * from the VDIC in this pipeline.
+ *
+ * CSI -> CH[0-3] -> MEM -> CH8,9,10 -> VDIC -> IC -> CH21 -> MEM
+ *
+ * In this pipeline, the CSI sends raw and full frames to memory buffers
+ * via the IDMAC SMFC channels 0-3. Fields from these frames are then
+ * transferred to the VDIC via IDMAC channels 8,9,10. The VDIC requires
+ * three fields: previous field F(n-1), current field F(n), and next
+ * field F(n+1), so we need three raw frames in memory: two completed frames
+ * to send F(n-1), F(n), F(n+1) to the VDIC, and a third frame for active
+ * CSI capture while the completed fields are sent through the VDIC->IC for
+ * processing.
+ *
+ * While the "direct" CSI->VDIC pipeline requires less memory bus bandwidth
+ * (just 1 channel vs. 5 channels for indirect pipeline), it can't be used
+ * for all motion modes, it only processes a single field (so half the
+ * original image resolution is lost), and it has the image quality issues
+ * mentioned above. With the indirect pipeline we have full control over
+ * field order. So by default the direct pipeline is disabled. Enable with
+ * the module param below, if enabled it will be used by high motion mode.
+ */
+
+static int allow_direct;
+module_param_named(direct, allow_direct, int, 0644);
+MODULE_PARM_DESC(direct, "Allow CSI->VDIC direct pipeline (default: 0)");
+
+struct vdic_priv;
+
+struct vdic_pipeline_ops {
+	int (*setup)(struct vdic_priv *priv);
+	void (*start)(struct vdic_priv *priv);
+	void (*stop)(struct vdic_priv *priv);
+	void (*disable)(struct vdic_priv *priv);
+};
+
+struct vdic_field_addr {
+	dma_addr_t prev; /* F(n-1) */
+	dma_addr_t curr; /* F(n) */
+	dma_addr_t next; /* F(n+1) */
+};
+
+struct vdic_priv {
+	struct imxcam_dev    *dev;
+	struct v4l2_subdev    sd;
+
+	/* IPU and its units we require */
+	struct ipu_soc *ipu;
+	struct ipu_ic *ic_vf;
+	struct ipu_smfc *smfc;
+	struct ipu_vdi *vdi;
+
+	struct ipuv3_channel *csi_ch;      /* raw CSI frames channel */
+	struct ipuv3_channel *vdi_in_ch_p; /* F(n-1) transfer channel */
+	struct ipuv3_channel *vdi_in_ch;   /* F(n) transfer channel */
+	struct ipuv3_channel *vdi_in_ch_n; /* F(n+1) transfer channel */
+	struct ipuv3_channel *prpvf_out_ch;/* final progressive frame channel */
+
+	/* pipeline operations */
+	struct vdic_pipeline_ops *ops;
+
+	/* active (undergoing DMA) buffers */
+	struct imxcam_buffer *active_frame[2];
+	struct imxcam_dma_buf underrun_buf;
+	int out_buf_num;
+
+	/*
+	 * Raw CSI frames for indirect pipeline, and the precalculated field
+	 * addresses for each frame. The VDIC requires three fields: previous
+	 * field F(n-1), current field F(n), and next field F(n+1), so we need
+	 * three frames in memory: two completed frames to send F(n-1), F(n),
+	 * F(n+1) to the VDIC, and a third frame for active CSI capture while
+	 * the completed fields are sent through the VDIC->IC for processing.
+	 */
+	struct imxcam_dma_buf csi_frame[3];
+	struct vdic_field_addr field[3];
+
+	int csi_frame_num; /* csi_frame index, 0-2 */
+	int csi_buf_num;   /* CSI channel double buffer index, 0-1 */
+
+	struct v4l2_mbus_framefmt inf; /* input sensor format */
+	struct v4l2_pix_format outf;   /* final output user format */
+	enum ipu_color_space in_cs;    /* input colorspace */
+	enum ipu_color_space out_cs;   /* output colorspace */
+	u32 in_pixfmt;
+
+	u32 in_stride;  /* input and output line strides */
+	u32 out_stride;
+	int field_size; /* 1/2 full image size */
+	bool direct;    /* using direct CSI->VDIC->IC pipeline */
+
+	struct timer_list eof_timeout_timer;
+
+	int csi_eof_irq; /* CSI channel EOF IRQ */
+	int nfb4eof_irq; /* CSI or PRPVF channel NFB4EOF IRQ */
+	int out_eof_irq; /* PRPVF channel EOF IRQ */
+
+	bool last_eof;  /* waiting for last EOF at vdic off */
+	struct completion last_eof_comp;
+};
+
+static void vdic_put_ipu_resources(struct vdic_priv *priv)
+{
+	if (!IS_ERR_OR_NULL(priv->ic_vf))
+		ipu_ic_put(priv->ic_vf);
+	priv->ic_vf = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->csi_ch))
+		ipu_idmac_put(priv->csi_ch);
+	priv->csi_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi_in_ch_p))
+		ipu_idmac_put(priv->vdi_in_ch_p);
+	priv->vdi_in_ch_p = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi_in_ch))
+		ipu_idmac_put(priv->vdi_in_ch);
+	priv->vdi_in_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi_in_ch_n))
+		ipu_idmac_put(priv->vdi_in_ch_n);
+	priv->vdi_in_ch_n = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->prpvf_out_ch))
+		ipu_idmac_put(priv->prpvf_out_ch);
+	priv->prpvf_out_ch = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->vdi))
+		ipu_vdi_put(priv->vdi);
+	priv->vdi = NULL;
+
+	if (!IS_ERR_OR_NULL(priv->smfc))
+		ipu_smfc_put(priv->smfc);
+	priv->smfc = NULL;
+}
+
+static int vdic_get_ipu_resources(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct v4l2_subdev *csi_sd = dev->sensor->csi_sd;
+	int ret, err_chan;
+
+	priv->ipu = dev_get_drvdata(csi_sd->dev->parent);
+
+	priv->ic_vf = ipu_ic_get(priv->ipu, IC_TASK_VIEWFINDER);
+	if (IS_ERR(priv->ic_vf)) {
+		v4l2_err(&priv->sd, "failed to get IC VF\n");
+		ret = PTR_ERR(priv->ic_vf);
+		goto out;
+	}
+
+	priv->vdi = ipu_vdi_get(priv->ipu);
+	if (IS_ERR(priv->vdi)) {
+		v4l2_err(&priv->sd, "failed to get VDIC\n");
+		ret = PTR_ERR(priv->vdi);
+		goto out;
+	}
+
+	priv->prpvf_out_ch = ipu_idmac_get(priv->ipu,
+					   IPUV3_CHANNEL_IC_PRP_VF_MEM);
+	if (IS_ERR(priv->prpvf_out_ch)) {
+		err_chan = IPUV3_CHANNEL_IC_PRP_VF_MEM;
+		ret = PTR_ERR(priv->prpvf_out_ch);
+		goto out_err_chan;
+	}
+
+	if (!priv->direct) {
+		/*
+		 * Choose the CSI-->SMFC-->MEM channel corresponding
+		 * to the IPU and CSI IDs.
+		 */
+		int csi_ch_num = IPUV3_CHANNEL_CSI0 +
+			(ipu_get_num(priv->ipu) << 1) + csi_id;
+
+		priv->csi_ch = ipu_idmac_get(priv->ipu, csi_ch_num);
+		if (IS_ERR(priv->csi_ch)) {
+			err_chan = csi_ch_num;
+			ret = PTR_ERR(priv->csi_ch);
+			goto out_err_chan;
+		}
+
+		priv->smfc = ipu_smfc_get(priv->ipu, csi_ch_num);
+		if (IS_ERR(priv->smfc)) {
+			v4l2_err(&priv->sd, "failed to get SMFC\n");
+			ret = PTR_ERR(priv->smfc);
+			goto out;
+		}
+
+		priv->vdi_in_ch_p = ipu_idmac_get(priv->ipu,
+						  IPUV3_CHANNEL_MEM_VDI_P);
+		if (IS_ERR(priv->vdi_in_ch_p)) {
+			err_chan = IPUV3_CHANNEL_MEM_VDI_P;
+			ret = PTR_ERR(priv->vdi_in_ch_p);
+			goto out_err_chan;
+		}
+
+		priv->vdi_in_ch = ipu_idmac_get(priv->ipu,
+						IPUV3_CHANNEL_MEM_VDI);
+		if (IS_ERR(priv->vdi_in_ch)) {
+			err_chan = IPUV3_CHANNEL_MEM_VDI;
+			ret = PTR_ERR(priv->vdi_in_ch);
+			goto out_err_chan;
+		}
+
+		priv->vdi_in_ch_n = ipu_idmac_get(priv->ipu,
+						  IPUV3_CHANNEL_MEM_VDI_N);
+		if (IS_ERR(priv->vdi_in_ch_n)) {
+			err_chan = IPUV3_CHANNEL_MEM_VDI_N;
+			ret = PTR_ERR(priv->vdi_in_ch_n);
+			goto out_err_chan;
+		}
+	}
+
+	return 0;
+
+out_err_chan:
+	v4l2_err(&priv->sd, "could not get IDMAC channel %u\n", err_chan);
+out:
+	vdic_put_ipu_resources(priv);
+	return ret;
+}
+
+static void prepare_csi_buffer(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int next_frame, curr_frame;
+
+	curr_frame = priv->csi_frame_num;
+	next_frame = (curr_frame + 2) % 3;
+
+	dev_dbg(dev->dev, "%d - %d %d\n",
+		priv->csi_buf_num, curr_frame, next_frame);
+
+	ipu_cpmem_set_buffer(priv->csi_ch, priv->csi_buf_num,
+			     priv->csi_frame[next_frame].phys);
+	ipu_idmac_select_buffer(priv->csi_ch, priv->csi_buf_num);
+}
+
+static void prepare_vdi_in_buffers(struct vdic_priv *priv)
+{
+	int last_frame, curr_frame;
+
+	curr_frame = priv->csi_frame_num;
+	last_frame = curr_frame - 1;
+	if (last_frame < 0)
+		last_frame = 2;
+
+	ipu_cpmem_set_buffer(priv->vdi_in_ch_p, 0,
+			     priv->field[last_frame].prev);
+	ipu_cpmem_set_buffer(priv->vdi_in_ch,   0,
+			     priv->field[curr_frame].curr);
+	ipu_cpmem_set_buffer(priv->vdi_in_ch_n, 0,
+			     priv->field[curr_frame].next);
+
+	ipu_idmac_select_buffer(priv->vdi_in_ch_p, 0);
+	ipu_idmac_select_buffer(priv->vdi_in_ch, 0);
+	ipu_idmac_select_buffer(priv->vdi_in_ch_n, 0);
+}
+
+static void prepare_prpvf_out_buffer(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	dma_addr_t phys;
+
+	if (!list_empty(&dev->ready_q)) {
+		frame = list_entry(dev->ready_q.next,
+				   struct imxcam_buffer, list);
+		phys = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[priv->out_buf_num] = frame;
+	} else {
+		phys = priv->underrun_buf.phys;
+		priv->active_frame[priv->out_buf_num] = NULL;
+	}
+
+	ipu_cpmem_set_buffer(priv->prpvf_out_ch, priv->out_buf_num, phys);
+	ipu_idmac_select_buffer(priv->prpvf_out_ch, priv->out_buf_num);
+}
+
+/* prpvf_out_ch EOF interrupt (progressive frame ready) */
+static irqreturn_t prpvf_out_eof_interrupt(int irq, void *dev_id)
+{
+	struct vdic_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	enum vb2_buffer_state state;
+	struct timeval cur_timeval;
+	u64 cur_time_ns;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	cur_time_ns = ktime_get_ns();
+	cur_timeval = ns_to_timeval(cur_time_ns);
+
+	/* timestamp and return the completed frame */
+	frame = priv->active_frame[priv->out_buf_num];
+	if (frame) {
+		frame->vb.timestamp = cur_time_ns;
+		state = (dev->signal_locked &&
+			 !atomic_read(&dev->pending_restart)) ?
+			VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
+		vb2_buffer_done(&frame->vb, state);
+	}
+
+	if (!priv->direct)
+		goto flip;
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->out_buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	prepare_prpvf_out_buffer(priv);
+
+flip:
+	priv->out_buf_num ^= 1;
+
+	if (dev->fim.eof && dev->fim.eof(dev, &cur_timeval))
+		v4l2_subdev_notify(&priv->sd, IMXCAM_FRAME_INTERVAL_NOTIFY,
+				   NULL);
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+/* csi_ch EOF interrupt */
+static irqreturn_t csi_eof_interrupt(int irq, void *dev_id)
+{
+	struct vdic_priv *priv = dev_id;
+	struct imxcam_dev *dev = priv->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+
+	if (priv->last_eof) {
+		complete(&priv->last_eof_comp);
+		priv->active_frame[priv->out_buf_num] = NULL;
+		priv->last_eof = false;
+		goto unlock;
+	}
+
+	/* bump the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	/* prepare next buffers */
+	prepare_csi_buffer(priv);
+	prepare_prpvf_out_buffer(priv);
+	prepare_vdi_in_buffers(priv);
+
+	/* increment double-buffer index and frame index */
+	priv->csi_buf_num ^= 1;
+	priv->csi_frame_num = (priv->csi_frame_num + 1) % 3;
+
+unlock:
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t nfb4eof_interrupt(int irq, void *dev_id)
+{
+	struct vdic_priv *priv = dev_id;
+
+	v4l2_err(&priv->sd, "NFB4EOF\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_NFB4EOF_NOTIFY, NULL);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * EOF timeout timer function.
+ */
+static void vdic_eof_timeout(unsigned long data)
+{
+	struct vdic_priv *priv = (struct vdic_priv *)data;
+
+	v4l2_err(&priv->sd, "EOF timeout\n");
+
+	v4l2_subdev_notify(&priv->sd, IMXCAM_EOF_TIMEOUT_NOTIFY, NULL);
+}
+
+static void vdic_free_dma_buf(struct vdic_priv *priv,
+			      struct imxcam_dma_buf *buf)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	if (buf->virt)
+		dma_free_coherent(dev->dev, buf->len, buf->virt, buf->phys);
+
+	buf->virt = NULL;
+	buf->phys = 0;
+}
+
+static int vdic_alloc_dma_buf(struct vdic_priv *priv,
+			      struct imxcam_dma_buf *buf,
+			      int size)
+{
+	struct imxcam_dev *dev = priv->dev;
+
+	vdic_free_dma_buf(priv, buf);
+
+	buf->len = PAGE_ALIGN(size);
+	buf->virt = dma_alloc_coherent(dev->dev, buf->len, &buf->phys,
+				       GFP_DMA | GFP_KERNEL);
+	if (!buf->virt) {
+		v4l2_err(&priv->sd, "failed to alloc dma buffer\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void setup_csi_channel(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_sensor *sensor = dev->sensor;
+	struct ipuv3_channel *channel = priv->csi_ch;
+	struct v4l2_mbus_framefmt *inf = &priv->inf;
+	int csi_id = sensor->csi_ep.base.port;
+	int vc_num = sensor->csi_ep.base.id;
+	unsigned int burst_size;
+	struct ipu_image image;
+	bool passthrough;
+
+	ipu_cpmem_zero(channel);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = inf->width;
+	image.pix.height = image.rect.height = inf->height;
+	image.pix.bytesperline = priv->in_stride;
+	image.pix.pixelformat = priv->in_pixfmt;
+	image.phys0 = priv->csi_frame[0].phys;
+	image.phys1 = priv->csi_frame[1].phys;
+	ipu_cpmem_set_image(channel, &image);
+
+	burst_size = (inf->width & 0xf) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	/*
+	 * If the sensor uses 16-bit parallel CSI bus, we must handle
+	 * the data internally in the IPU as 16-bit generic, aka
+	 * passthrough mode.
+	 */
+	passthrough = (sensor->ep.bus_type != V4L2_MBUS_CSI2 &&
+		       sensor->ep.bus.parallel.bus_width >= 16);
+
+	if (passthrough)
+		ipu_cpmem_set_format_passthrough(channel, 16);
+
+	if (sensor->ep.bus_type == V4L2_MBUS_CSI2)
+		ipu_smfc_map_channel(priv->smfc, csi_id, vc_num);
+	else
+		ipu_smfc_map_channel(priv->smfc, csi_id, 0);
+
+	/*
+	 * Set the channel for the direct CSI-->memory via SMFC
+	 * use-case to very high priority, by enabling the watermark
+	 * signal in the SMFC, enabling WM in the channel, and setting
+	 * the channel priority to high.
+	 *
+	 * Refer to the i.mx6 rev. D TRM Table 36-8: Calculated priority
+	 * value.
+	 *
+	 * The WM's are set very low by intention here to ensure that
+	 * the SMFC FIFOs do not overflow.
+	 */
+	ipu_smfc_set_watermark(priv->smfc, 0x02, 0x01);
+	ipu_cpmem_set_high_priority(channel);
+	ipu_idmac_enable_watermark(channel, true);
+	ipu_cpmem_set_axi_id(channel, 0);
+	ipu_idmac_lock_enable(channel, 8);
+
+	burst_size = ipu_cpmem_get_burstsize(channel);
+	burst_size = passthrough ?
+		(burst_size >> 3) - 1 : (burst_size >> 2) - 1;
+
+	ipu_smfc_set_burstsize(priv->smfc, burst_size);
+
+	ipu_idmac_set_double_buffer(channel, true);
+}
+
+static void setup_vdi_channel(struct vdic_priv *priv,
+			      struct ipuv3_channel *channel,
+			      dma_addr_t phys0, dma_addr_t phys1,
+			      bool out_chan)
+{
+	u32 stride, width, height, pixfmt;
+	unsigned int burst_size;
+	struct ipu_image image;
+
+	if (out_chan) {
+		width = priv->outf.width;
+		height = priv->outf.height;
+		pixfmt = priv->outf.pixelformat;
+		stride = priv->out_stride;
+	} else {
+		width = priv->inf.width;
+		height = priv->inf.height / 2;
+		pixfmt = priv->in_pixfmt;
+		stride = priv->in_stride;
+	}
+
+	ipu_cpmem_zero(channel);
+
+	memset(&image, 0, sizeof(image));
+	image.pix.width = image.rect.width = width;
+	image.pix.height = image.rect.height = height;
+	image.pix.bytesperline = stride;
+	image.pix.pixelformat = pixfmt;
+	image.phys0 = phys0;
+	image.phys1 = phys1;
+	ipu_cpmem_set_image(channel, &image);
+
+	burst_size = (width & 0xf) ? 8 : 16;
+
+	ipu_cpmem_set_burstsize(channel, burst_size);
+
+	if (out_chan)
+		ipu_ic_task_idma_init(priv->ic_vf, channel, width, height,
+				      burst_size, IPU_ROTATE_NONE);
+
+	ipu_cpmem_set_axi_id(channel, 1);
+
+	ipu_idmac_set_double_buffer(channel, out_chan);
+}
+
+static int vdic_setup_direct(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame, *tmp;
+	dma_addr_t phys[2] = {0};
+	unsigned long flags;
+	int i = 0;
+
+	priv->out_buf_num = 0;
+
+	spin_lock_irqsave(&dev->irqlock, flags);
+	list_for_each_entry_safe(frame, tmp, &dev->ready_q, list) {
+		phys[i] = vb2_dma_contig_plane_dma_addr(&frame->vb, 0);
+		list_del(&frame->list);
+		priv->active_frame[i++] = frame;
+		if (i >= 2)
+			break;
+	}
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/* init the prpvf out channel */
+	setup_vdi_channel(priv, priv->prpvf_out_ch, phys[0], phys[1], true);
+
+	return 0;
+}
+
+static void vdic_start_direct(struct vdic_priv *priv)
+{
+	/* set buffers ready */
+	ipu_idmac_select_buffer(priv->prpvf_out_ch, 0);
+	ipu_idmac_select_buffer(priv->prpvf_out_ch, 1);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->prpvf_out_ch);
+}
+
+static void vdic_stop_direct(struct vdic_priv *priv)
+{
+	ipu_idmac_disable_channel(priv->prpvf_out_ch);
+}
+
+static void vdic_disable_direct(struct vdic_priv *priv)
+{
+	/* nothing to do */
+}
+
+static int vdic_setup_indirect(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct vdic_field_addr *field;
+	struct imxcam_dma_buf *frame;
+	int ret, in_size, i;
+
+	/*
+	 * FIXME: following in_size calc would not be correct for planar pixel
+	 * formats, but all mbus pixel codes are packed formats, so so far this
+	 * is OK.
+	 */
+	in_size = priv->in_stride * priv->inf.height;
+
+	priv->csi_buf_num = priv->csi_frame_num = priv->out_buf_num = 0;
+	priv->field_size = in_size / 2;
+
+	/* request EOF irq for vdi out channel */
+	priv->csi_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						  priv->csi_ch,
+						  IPU_IRQ_EOF);
+	ret = devm_request_irq(dev->dev, priv->csi_eof_irq,
+			       csi_eof_interrupt, 0,
+			       "imxcam-csi-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd, "Error registering CSI eof irq: %d\n",
+			 ret);
+		return ret;
+	}
+
+	for (i = 0; i < 3; i++) {
+		frame = &priv->csi_frame[i];
+
+		ret = vdic_alloc_dma_buf(priv, frame, in_size);
+		if (ret) {
+			v4l2_err(&priv->sd,
+				 "failed to alloc csi_frame[%d], %d\n", i, ret);
+			while (--i >= 0)
+				vdic_free_dma_buf(priv, &priv->csi_frame[i]);
+			goto out_free_irq;
+		}
+
+		/* precalculate the field addresses for this frame */
+		field = &priv->field[i];
+		switch (priv->inf.field) {
+		case V4L2_FIELD_SEQ_TB:
+			field->prev = frame->phys + priv->field_size;
+			field->curr = frame->phys;
+			field->next = frame->phys + priv->field_size;
+			break;
+		case V4L2_FIELD_SEQ_BT:
+			field->prev = frame->phys;
+			field->curr = frame->phys + priv->field_size;
+			field->next = frame->phys;
+			break;
+		case V4L2_FIELD_INTERLACED_BT:
+			field->prev = frame->phys;
+			field->curr = frame->phys + priv->in_stride;
+			field->next = frame->phys;
+			break;
+		default:
+			/* assume V4L2_FIELD_INTERLACED_TB */
+			field->prev = frame->phys + priv->in_stride;
+			field->curr = frame->phys;
+			field->next = frame->phys + priv->in_stride;
+			break;
+		}
+	}
+
+	priv->active_frame[0] = priv->active_frame[1] = NULL;
+
+	/* init the CSI channel */
+	setup_csi_channel(priv);
+
+	/* init the vdi-in channels */
+	setup_vdi_channel(priv, priv->vdi_in_ch_p, 0, 0, false);
+	setup_vdi_channel(priv, priv->vdi_in_ch, 0, 0, false);
+	setup_vdi_channel(priv, priv->vdi_in_ch_n, 0, 0, false);
+
+	/* init the prpvf out channel */
+	setup_vdi_channel(priv, priv->prpvf_out_ch, 0, 0, true);
+
+	return 0;
+
+out_free_irq:
+	devm_free_irq(dev->dev, priv->csi_eof_irq, priv);
+	return ret;
+}
+
+static void vdic_start_indirect(struct vdic_priv *priv)
+{
+	int i;
+
+	/* set buffers ready */
+	for (i = 0; i < 2; i++)
+		ipu_idmac_select_buffer(priv->csi_ch, i);
+
+	/* enable SMFC */
+	ipu_smfc_enable(priv->smfc);
+
+	/* enable the channels */
+	ipu_idmac_enable_channel(priv->csi_ch);
+	ipu_idmac_enable_channel(priv->prpvf_out_ch);
+	ipu_idmac_enable_channel(priv->vdi_in_ch_p);
+	ipu_idmac_enable_channel(priv->vdi_in_ch);
+	ipu_idmac_enable_channel(priv->vdi_in_ch_n);
+}
+
+static void vdic_stop_indirect(struct vdic_priv *priv)
+{
+	/* disable channels */
+	ipu_idmac_disable_channel(priv->prpvf_out_ch);
+	ipu_idmac_disable_channel(priv->vdi_in_ch_p);
+	ipu_idmac_disable_channel(priv->vdi_in_ch);
+	ipu_idmac_disable_channel(priv->vdi_in_ch_n);
+	ipu_idmac_disable_channel(priv->csi_ch);
+
+	/* disable SMFC */
+	ipu_smfc_disable(priv->smfc);
+}
+
+static void vdic_disable_indirect(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int i;
+
+	devm_free_irq(dev->dev, priv->csi_eof_irq, priv);
+
+	for (i = 0; i < 3; i++)
+		vdic_free_dma_buf(priv, &priv->csi_frame[i]);
+}
+
+static struct vdic_pipeline_ops direct_ops = {
+	.setup = vdic_setup_direct,
+	.start = vdic_start_direct,
+	.stop = vdic_stop_direct,
+	.disable = vdic_disable_direct,
+};
+
+static struct vdic_pipeline_ops indirect_ops = {
+	.setup = vdic_setup_indirect,
+	.start = vdic_start_indirect,
+	.stop = vdic_stop_indirect,
+	.disable = vdic_disable_indirect,
+};
+
+static int vdic_start(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	int csi_id = dev->sensor->csi_ep.base.port;
+	struct imxcam_buffer *frame;
+	int i, ret;
+
+	priv->direct = (allow_direct && dev->motion == HIGH_MOTION);
+	/* this info is needed by CSI subdev for destination routing */
+	dev->vdic_direct = priv->direct;
+
+	priv->ops = priv->direct ? &direct_ops : &indirect_ops;
+
+	ret = vdic_get_ipu_resources(priv);
+	if (ret)
+		return ret;
+
+	priv->inf = dev->sensor_fmt;
+	priv->in_pixfmt = dev->sensor_pixfmt->fourcc;
+	priv->inf.width = dev->crop.width;
+	priv->inf.height = dev->crop.height;
+	priv->in_stride = dev->sensor_pixfmt->y_depth ?
+		(priv->inf.width * dev->sensor_pixfmt->y_depth) >> 3 :
+		(priv->inf.width * dev->sensor_pixfmt->bpp) >> 3;
+	priv->in_cs = ipu_mbus_code_to_colorspace(priv->inf.code);
+
+	priv->outf = dev->user_fmt.fmt.pix;
+	priv->out_cs = ipu_pixelformat_to_colorspace(priv->outf.pixelformat);
+	priv->out_stride = dev->user_pixfmt->y_depth ?
+		(priv->outf.width * dev->user_pixfmt->y_depth) >> 3 :
+		(priv->outf.width * dev->user_pixfmt->bpp) >> 3;
+
+	/* set IC to receive from VDIC */
+	ipu_ic_set_src(priv->ic_vf, csi_id, true);
+
+	/*
+	 * set VDIC to receive from CSI for direct path, and memory
+	 * for indirect.
+	 */
+	ipu_vdi_set_src(priv->vdi, priv->direct);
+
+	ret = vdic_alloc_dma_buf(priv, &priv->underrun_buf,
+				 priv->outf.sizeimage);
+	if (ret) {
+		v4l2_err(&priv->sd, "failed to alloc underrun_buf, %d\n", ret);
+		goto out_put_ipu;
+	}
+
+	/* init EOF completion waitq */
+	init_completion(&priv->last_eof_comp);
+	priv->last_eof = false;
+
+	/* request EOF irq for prpvf out channel */
+	priv->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+						  priv->prpvf_out_ch,
+						  IPU_IRQ_EOF);
+	ret = devm_request_irq(dev->dev, priv->out_eof_irq,
+			       prpvf_out_eof_interrupt, 0,
+			       "imxcam-prpvf-out-eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering prpvf out eof irq: %d\n", ret);
+		goto out_free_underrun;
+	}
+
+	/* request NFB4EOF irq */
+	priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu, priv->direct ?
+						  priv->prpvf_out_ch :
+						  priv->csi_ch,
+						  IPU_IRQ_NFB4EOF);
+	ret = devm_request_irq(dev->dev, priv->nfb4eof_irq,
+			       nfb4eof_interrupt, 0,
+			       "imxcam-vdic-nfb4eof", priv);
+	if (ret) {
+		v4l2_err(&priv->sd,
+			 "Error registering NFB4EOF irq: %d\n", ret);
+		goto out_free_eof_irq;
+	}
+
+	/* init the VDIC */
+	ipu_vdi_setup(priv->vdi, priv->inf.code,
+		      priv->inf.width, priv->inf.height, priv->inf.field,
+		      dev->motion);
+
+	ret = ipu_ic_task_init(priv->ic_vf,
+			       priv->inf.width, priv->inf.height,
+			       priv->outf.width, priv->outf.height,
+			       priv->in_cs, priv->out_cs);
+	if (ret) {
+		v4l2_err(&priv->sd, "ipu_ic_task_init failed, %d\n", ret);
+		goto out_free_nfb4eof_irq;
+	}
+
+	ret = priv->ops->setup(priv);
+	if (ret)
+		goto out_free_nfb4eof_irq;
+
+	ipu_vdi_enable(priv->vdi);
+	ipu_ic_enable(priv->ic_vf);
+
+	priv->ops->start(priv);
+
+	/* enable the IC VF task */
+	ipu_ic_task_enable(priv->ic_vf);
+
+	/* sensor stream on */
+	ret = dev->sensor_set_stream(dev, 1);
+	if (ret) {
+		v4l2_err(&priv->sd, "sensor stream on failed\n");
+		goto out_stop;
+	}
+
+	/* start the EOF timeout timer */
+	mod_timer(&priv->eof_timeout_timer,
+		  jiffies + msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+
+	return 0;
+
+out_stop:
+	ipu_ic_task_disable(priv->ic_vf);
+	priv->ops->stop(priv);
+	ipu_ic_disable(priv->ic_vf);
+	ipu_vdi_disable(priv->vdi);
+	priv->ops->disable(priv);
+out_free_nfb4eof_irq:
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+out_free_eof_irq:
+	devm_free_irq(dev->dev, priv->out_eof_irq, priv);
+out_free_underrun:
+	vdic_free_dma_buf(priv, &priv->underrun_buf);
+out_put_ipu:
+	vdic_put_ipu_resources(priv);
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame)
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_QUEUED);
+	}
+	return ret;
+}
+
+static int vdic_stop(struct vdic_priv *priv)
+{
+	struct imxcam_dev *dev = priv->dev;
+	struct imxcam_buffer *frame;
+	unsigned long flags;
+	int i, ret;
+
+	/* mark next EOF interrupt as the last before vdic off */
+	spin_lock_irqsave(&dev->irqlock, flags);
+	priv->last_eof = true;
+	spin_unlock_irqrestore(&dev->irqlock, flags);
+
+	/*
+	 * and then wait for interrupt handler to mark completion.
+	 */
+	ret = wait_for_completion_timeout(&priv->last_eof_comp,
+					  msecs_to_jiffies(IMXCAM_EOF_TIMEOUT));
+	if (ret == 0)
+		v4l2_warn(&priv->sd, "wait last encode EOF timeout\n");
+
+	/* sensor stream off */
+	ret = dev->sensor_set_stream(dev, 0);
+	if (ret)
+		v4l2_warn(&priv->sd, "sensor stream off failed\n");
+
+	ipu_ic_task_disable(priv->ic_vf);
+	priv->ops->stop(priv);
+	ipu_ic_disable(priv->ic_vf);
+	ipu_vdi_disable(priv->vdi);
+	priv->ops->disable(priv);
+	devm_free_irq(dev->dev, priv->nfb4eof_irq, priv);
+	devm_free_irq(dev->dev, priv->out_eof_irq, priv);
+	vdic_free_dma_buf(priv, &priv->underrun_buf);
+	vdic_put_ipu_resources(priv);
+
+	/* cancel the EOF timeout timer */
+	del_timer_sync(&priv->eof_timeout_timer);
+
+	/* return any remaining active frames with error */
+	for (i = 0; i < 2; i++) {
+		frame = priv->active_frame[i];
+		if (frame && frame->vb.state == VB2_BUF_STATE_ACTIVE) {
+			frame->vb.timestamp = ktime_get_ns();
+			vb2_buffer_done(&frame->vb, VB2_BUF_STATE_ERROR);
+		}
+	}
+
+	return 0;
+}
+
+static int vdic_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct vdic_priv *priv = v4l2_get_subdevdata(sd);
+
+	return enable ? vdic_start(priv) : vdic_stop(priv);
+}
+
+static struct v4l2_subdev_video_ops vdic_video_ops = {
+	.s_stream = vdic_s_stream,
+};
+
+static struct v4l2_subdev_ops vdic_subdev_ops = {
+	.video = &vdic_video_ops,
+};
+
+struct v4l2_subdev *imxcam_vdic_init(struct imxcam_dev *dev)
+{
+	struct vdic_priv *priv;
+
+	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	init_timer(&priv->eof_timeout_timer);
+	priv->eof_timeout_timer.data = (unsigned long)priv;
+	priv->eof_timeout_timer.function = vdic_eof_timeout;
+
+	v4l2_subdev_init(&priv->sd, &vdic_subdev_ops);
+	strlcpy(priv->sd.name, "imx-camera-vdic", sizeof(priv->sd.name));
+	v4l2_set_subdevdata(&priv->sd, priv);
+
+	priv->dev = dev;
+	return &priv->sd;
+}
diff --git a/include/media/imx.h b/include/media/imx.h
new file mode 100644
index 0000000..5025a72
--- /dev/null
+++ b/include/media/imx.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2014-2015 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef __MEDIA_IMX_H__
+#define __MEDIA_IMX_H__
+
+#include <uapi/media/imx.h>
+
+#endif
diff --git a/include/uapi/Kbuild b/include/uapi/Kbuild
index 245aa6e..9a51957 100644
--- a/include/uapi/Kbuild
+++ b/include/uapi/Kbuild
@@ -6,6 +6,7 @@
 header-y += asm-generic/
 header-y += linux/
 header-y += sound/
+header-y += media/
 header-y += mtd/
 header-y += rdma/
 header-y += video/
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b6a357a..9343950 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -180,6 +180,10 @@ enum v4l2_colorfx {
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_TC358743_BASE		(V4L2_CID_USER_BASE + 0x1080)
 
+/* The base for the imx driver controls.
+ * We reserve 16 controls for this driver. */
+#define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x1090)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */
diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild
new file mode 100644
index 0000000..fa78958
--- /dev/null
+++ b/include/uapi/media/Kbuild
@@ -0,0 +1,2 @@
+# UAPI Header export list
+header-y += imx.h
diff --git a/include/uapi/media/imx.h b/include/uapi/media/imx.h
new file mode 100644
index 0000000..de1447c
--- /dev/null
+++ b/include/uapi/media/imx.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2014-2015 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+
+#ifndef __UAPI_MEDIA_IMX_H__
+#define __UAPI_MEDIA_IMX_H__
+
+enum imx_ctrl_id {
+	V4L2_CID_IMX_MOTION = (V4L2_CID_USER_IMX_BASE + 0),
+	V4L2_CID_IMX_FIM_ENABLE,
+	V4L2_CID_IMX_FIM_NUM,
+	V4L2_CID_IMX_FIM_TOLERANCE_MIN,
+	V4L2_CID_IMX_FIM_TOLERANCE_MAX,
+	V4L2_CID_IMX_FIM_NUM_SKIP,
+};
+
+#endif
-- 
1.9.1


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

* [PATCH 20/28] media: imx: Add MIPI CSI-2 Receiver driver
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (18 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 19/28] media: Add i.MX5/6 camera interface driver Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 21/28] media: imx: Add video switch Steve Longerbeam
                     ` (7 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds MIPI CSI-2 Receiver subdev driver. This subdev is required
for sensors with a MIPI CSI2 interface.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig     |   8 +
 drivers/staging/media/imx/capture/Makefile    |   1 +
 drivers/staging/media/imx/capture/mipi-csi2.c | 373 ++++++++++++++++++++++++++
 3 files changed, 382 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/mipi-csi2.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index ee2cbab..ac6fce0 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -1,3 +1,11 @@
 menu "i.MX5/6 Camera Sub devices"
 
+config IMX_MIPI_CSI2
+       tristate "MIPI CSI2 Receiver Driver"
+       depends on VIDEO_IMX_CAMERA
+       default y
+       ---help---
+         MIPI CSI-2 Receiver driver support. This driver is required
+	 for sensor drivers with a MIPI CSI2 interface.
+
 endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index 5c965f9..8961a4f 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -3,3 +3,4 @@ imx-camera-objs := imx-camif.o imx-ic-prpenc.o imx-of.o \
 
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
+obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
diff --git a/drivers/staging/media/imx/capture/mipi-csi2.c b/drivers/staging/media/imx/capture/mipi-csi2.c
new file mode 100644
index 0000000..8e03c1f
--- /dev/null
+++ b/drivers/staging/media/imx/capture/mipi-csi2.c
@@ -0,0 +1,373 @@
+/*
+ * MIPI CSI-2 Receiver Subdev for Freescale i.MX5/6 SOC.
+ *
+ * Copyright (c) 2012-2014 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+#include <asm/mach/irq.h>
+#include <video/imx-ipu-v3.h>
+
+struct imxcsi2_dev {
+	struct device          *dev;
+	struct v4l2_subdev      sd;
+	struct clk             *dphy_clk;
+	struct clk             *cfg_clk;
+	struct clk             *pix_clk; /* what is this? */
+	void __iomem           *base;
+	int                     intr1;
+	int                     intr2;
+	struct v4l2_of_bus_mipi_csi2 bus;
+	spinlock_t              lock;
+	bool                    on;
+};
+
+#define DEVICE_NAME "imx-mipi-csi2"
+
+/* Register offsets */
+#define CSI2_VERSION            0x000
+#define CSI2_N_LANES            0x004
+#define CSI2_PHY_SHUTDOWNZ      0x008
+#define CSI2_DPHY_RSTZ          0x00c
+#define CSI2_RESETN             0x010
+#define CSI2_PHY_STATE          0x014
+#define CSI2_DATA_IDS_1         0x018
+#define CSI2_DATA_IDS_2         0x01c
+#define CSI2_ERR1               0x020
+#define CSI2_ERR2               0x024
+#define CSI2_MSK1               0x028
+#define CSI2_MSK2               0x02c
+#define CSI2_PHY_TST_CTRL0      0x030
+#define CSI2_PHY_TST_CTRL1      0x034
+#define CSI2_SFT_RESET          0xf00
+
+static inline struct imxcsi2_dev *sd_to_dev(struct v4l2_subdev *sdev)
+{
+	return container_of(sdev, struct imxcsi2_dev, sd);
+}
+
+static inline u32 imxcsi2_read(struct imxcsi2_dev *csi2, unsigned int regoff)
+{
+	return readl(csi2->base + regoff);
+}
+
+static inline void imxcsi2_write(struct imxcsi2_dev *csi2, u32 val,
+				 unsigned int regoff)
+{
+	writel(val, csi2->base + regoff);
+}
+
+static void imxcsi2_set_lanes(struct imxcsi2_dev *csi2)
+{
+	int lanes = csi2->bus.num_data_lanes;
+	unsigned long flags;
+
+	spin_lock_irqsave(&csi2->lock, flags);
+
+	imxcsi2_write(csi2, lanes - 1, CSI2_N_LANES);
+
+	spin_unlock_irqrestore(&csi2->lock, flags);
+}
+
+static void __imxcsi2_enable(struct imxcsi2_dev *csi2, bool enable)
+{
+	if (enable) {
+		imxcsi2_write(csi2, 0xffffffff, CSI2_PHY_SHUTDOWNZ);
+		imxcsi2_write(csi2, 0xffffffff, CSI2_DPHY_RSTZ);
+		imxcsi2_write(csi2, 0xffffffff, CSI2_RESETN);
+	} else {
+		imxcsi2_write(csi2, 0x0, CSI2_PHY_SHUTDOWNZ);
+		imxcsi2_write(csi2, 0x0, CSI2_DPHY_RSTZ);
+		imxcsi2_write(csi2, 0x0, CSI2_RESETN);
+	}
+}
+
+static void imxcsi2_enable(struct imxcsi2_dev *csi2, bool enable)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&csi2->lock, flags);
+	__imxcsi2_enable(csi2, enable);
+	spin_unlock_irqrestore(&csi2->lock, flags);
+}
+
+static void imxcsi2_reset(struct imxcsi2_dev *csi2)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&csi2->lock, flags);
+
+	__imxcsi2_enable(csi2, false);
+
+	imxcsi2_write(csi2, 0x00000001, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL1);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000002, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00010044, CSI2_PHY_TST_CTRL1);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000014, CSI2_PHY_TST_CTRL1);
+	imxcsi2_write(csi2, 0x00000002, CSI2_PHY_TST_CTRL0);
+	imxcsi2_write(csi2, 0x00000000, CSI2_PHY_TST_CTRL0);
+
+	__imxcsi2_enable(csi2, true);
+
+	spin_unlock_irqrestore(&csi2->lock, flags);
+}
+
+static int imxcsi2_dphy_wait(struct imxcsi2_dev *csi2)
+{
+	u32 reg;
+	int i;
+
+	/* wait for mipi sensor ready */
+	for (i = 0; i < 50; i++) {
+		reg = imxcsi2_read(csi2, CSI2_PHY_STATE);
+		if (reg != 0x200)
+			break;
+		usleep_range(10000, 20000);
+	}
+
+	if (i >= 50) {
+		v4l2_err(&csi2->sd,
+			 "wait for clock lane timeout, phy_state = 0x%08x\n",
+			 reg);
+		return -ETIME;
+	}
+
+	/* wait for mipi stable */
+	for (i = 0; i < 50; i++) {
+		reg = imxcsi2_read(csi2, CSI2_ERR1);
+		if (reg == 0x0)
+			break;
+		usleep_range(10000, 20000);
+	}
+
+	if (i >= 50) {
+		v4l2_err(&csi2->sd,
+			 "wait for controller timeout, err1 = 0x%08x\n",
+			 reg);
+		return -ETIME;
+	}
+
+	/* finally let's wait for active clock on the clock lane */
+	for (i = 0; i < 50; i++) {
+		reg = imxcsi2_read(csi2, CSI2_PHY_STATE);
+		if (reg & (1 << 8))
+			break;
+		usleep_range(10000, 20000);
+	}
+
+	if (i >= 50) {
+		v4l2_err(&csi2->sd,
+			 "wait for active clock timeout, phy_state = 0x%08x\n",
+			 reg);
+		return -ETIME;
+	}
+
+	v4l2_info(&csi2->sd, "ready, dphy version 0x%x\n",
+		  imxcsi2_read(csi2, CSI2_VERSION));
+
+	return 0;
+}
+
+/*
+ * V4L2 subdev operations
+ */
+static int imxcsi2_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct imxcsi2_dev *csi2 = sd_to_dev(sd);
+
+	if (on && !csi2->on) {
+		v4l2_info(&csi2->sd, "power on\n");
+		clk_prepare_enable(csi2->cfg_clk);
+		clk_prepare_enable(csi2->dphy_clk);
+		imxcsi2_set_lanes(csi2);
+		imxcsi2_reset(csi2);
+	} else if (!on && csi2->on) {
+		v4l2_info(&csi2->sd, "power off\n");
+		imxcsi2_enable(csi2, false);
+		clk_disable_unprepare(csi2->dphy_clk);
+		clk_disable_unprepare(csi2->cfg_clk);
+	}
+
+	csi2->on = on;
+	return 0;
+}
+
+static int imxcsi2_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct imxcsi2_dev *csi2 = sd_to_dev(sd);
+	int ret = 0;
+
+	if (enable) {
+		clk_prepare_enable(csi2->pix_clk);
+		ret = imxcsi2_dphy_wait(csi2);
+	} else {
+		clk_disable_unprepare(csi2->pix_clk);
+	}
+
+	return ret;
+}
+
+static struct v4l2_subdev_core_ops imxcsi2_core_ops = {
+	.s_power = imxcsi2_s_power,
+};
+
+static struct v4l2_subdev_video_ops imxcsi2_video_ops = {
+	.s_stream = imxcsi2_s_stream,
+};
+
+static struct v4l2_subdev_ops imxcsi2_subdev_ops = {
+	.core = &imxcsi2_core_ops,
+	.video = &imxcsi2_video_ops,
+};
+
+static int imxcsi2_parse_endpoints(struct imxcsi2_dev *csi2)
+{
+	struct device_node *node = csi2->dev->of_node;
+	struct device_node *epnode;
+	struct v4l2_of_endpoint ep;
+	int ret = 0;
+
+	epnode = of_graph_get_next_endpoint(node, NULL);
+	if (!epnode) {
+		v4l2_err(&csi2->sd, "failed to get endpoint node\n");
+		return -EINVAL;
+	}
+
+	v4l2_of_parse_endpoint(epnode, &ep);
+	if (ep.bus_type != V4L2_MBUS_CSI2) {
+		v4l2_err(&csi2->sd, "invalid bus type, must be MIPI CSI2\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	csi2->bus = ep.bus.mipi_csi2;
+
+	v4l2_info(&csi2->sd, "data lanes: %d\n", csi2->bus.num_data_lanes);
+	v4l2_info(&csi2->sd, "flags: 0x%08x\n", csi2->bus.flags);
+out:
+	of_node_put(epnode);
+	return ret;
+}
+
+static int imxcsi2_probe(struct platform_device *pdev)
+{
+	struct imxcsi2_dev *csi2;
+	struct resource *res;
+	int ret;
+
+	csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
+	if (!csi2)
+		return -ENOMEM;
+
+	csi2->dev = &pdev->dev;
+	spin_lock_init(&csi2->lock);
+
+	v4l2_subdev_init(&csi2->sd, &imxcsi2_subdev_ops);
+	v4l2_set_subdevdata(&csi2->sd, &pdev->dev);
+	csi2->sd.dev = &pdev->dev;
+	csi2->sd.owner = THIS_MODULE;
+	strcpy(csi2->sd.name, DEVICE_NAME);
+
+	ret = imxcsi2_parse_endpoints(csi2);
+	if (ret)
+		return ret;
+
+	csi2->cfg_clk = devm_clk_get(&pdev->dev, "cfg_clk");
+	if (IS_ERR(csi2->cfg_clk)) {
+		v4l2_err(&csi2->sd, "failed to get cfg clock\n");
+		ret = PTR_ERR(csi2->cfg_clk);
+		return ret;
+	}
+
+	csi2->dphy_clk = devm_clk_get(&pdev->dev, "dphy_clk");
+	if (IS_ERR(csi2->dphy_clk)) {
+		v4l2_err(&csi2->sd, "failed to get dphy clock\n");
+		ret = PTR_ERR(csi2->dphy_clk);
+		return ret;
+	}
+
+	csi2->pix_clk = devm_clk_get(&pdev->dev, "pix_clk");
+	if (IS_ERR(csi2->pix_clk)) {
+		v4l2_err(&csi2->sd, "failed to get pixel clock\n");
+		ret = PTR_ERR(csi2->pix_clk);
+		return ret;
+	}
+
+	csi2->intr1 = platform_get_irq(pdev, 0);
+	csi2->intr2 = platform_get_irq(pdev, 1);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!res || csi2->intr1 < 0 || csi2->intr2 < 0) {
+		v4l2_err(&csi2->sd, "failed to get platform resources\n");
+		return -ENODEV;
+	}
+
+	csi2->base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
+	if (!csi2->base) {
+		v4l2_err(&csi2->sd, "failed to map CSI-2 registers\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, &csi2->sd);
+
+	return v4l2_async_register_subdev(&csi2->sd);
+}
+
+static int imxcsi2_remove(struct platform_device *pdev)
+{
+	struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+	struct imxcsi2_dev *csi2 = sd_to_dev(sd);
+
+	imxcsi2_s_power(sd, 0);
+
+	v4l2_async_unregister_subdev(&csi2->sd);
+	v4l2_device_unregister_subdev(sd);
+
+	return 0;
+}
+
+static const struct of_device_id imxcsi2_dt_ids[] = {
+	{ .compatible = "fsl,imx-mipi-csi2", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imxcsi2_dt_ids);
+
+static struct platform_driver imxcsi2_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = imxcsi2_dt_ids,
+	},
+	.probe = imxcsi2_probe,
+	.remove = imxcsi2_remove,
+};
+
+module_platform_driver(imxcsi2_driver);
+
+MODULE_DESCRIPTION("i.MX5/6 MIPI CSI-2 Receiver driver");
+MODULE_AUTHOR("Mentor Graphics Inc.");
+MODULE_LICENSE("GPL");
+
-- 
1.9.1


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

* [PATCH 21/28] media: imx: Add video switch
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (19 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 20/28] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 22/28] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
                     ` (6 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Philipp Zabel, Sascha Hauer, Steve Longerbeam

From: Philipp Zabel <p.zabel@pengutronix.de>

This driver can handle SoC internal and extern video bus multiplexers,
controlled either by register bit fields or by GPIO.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig          |   9 +
 drivers/staging/media/imx/capture/Makefile         |   1 +
 .../staging/media/imx/capture/imx-video-switch.c   | 347 +++++++++++++++++++++
 3 files changed, 357 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index ac6fce0..1a64011 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -8,4 +8,13 @@ config IMX_MIPI_CSI2
          MIPI CSI-2 Receiver driver support. This driver is required
 	 for sensor drivers with a MIPI CSI2 interface.
 
+config IMX_VIDEO_SWITCH
+	tristate "i.MX5/6 Video Bus Multiplexer"
+	depends on GPIOLIB && VIDEO_IMX_CAMERA
+	default y
+	---help---
+	  This driver provides support for the i.MX5/6 internal video bus
+	  multiplexer controlled by register bitfields as well as
+	  external multiplexers controller by a GPIO.
+
 endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index 8961a4f..f17b199 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -4,3 +4,4 @@ imx-camera-objs := imx-camif.o imx-ic-prpenc.o imx-of.o \
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
 obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
+obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
diff --git a/drivers/staging/media/imx/capture/imx-video-switch.c b/drivers/staging/media/imx/capture/imx-video-switch.c
new file mode 100644
index 0000000..365616c
--- /dev/null
+++ b/drivers/staging/media/imx/capture/imx-video-switch.c
@@ -0,0 +1,347 @@
+/*
+ * devicetree probed mediacontrol video multiplexer.
+ *
+ * Copyright (C) 2013 Sascha Hauer, Pengutronix
+ * Copyright (c) 2014-2016 Mentor Graphics Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/of_graph.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-of.h>
+
+struct vidsw {
+	struct device *dev;
+	struct v4l2_subdev subdev;
+#ifdef CONFIG_MEDIA_CONTROLLER
+	struct media_pad *pads;
+#endif
+	struct v4l2_mbus_framefmt *format_mbus;
+	struct v4l2_of_endpoint *endpoint;
+	struct regmap_field *field;
+	struct gpio_desc *gpio;
+	int output_pad;
+	int numpads;
+	int active;
+};
+
+#define to_vidsw(sd) container_of(sd, struct vidsw, subdev)
+
+static int vidsw_set_mux(struct vidsw *vidsw, int input_index)
+{
+	if (vidsw->active >= 0) {
+		if (vidsw->active == input_index)
+			return 0;
+		else
+			return -EBUSY;
+	}
+
+	vidsw->active = input_index;
+
+	dev_dbg(vidsw->dev, "setting %d active\n", vidsw->active);
+
+	if (vidsw->field)
+		regmap_field_write(vidsw->field, vidsw->active);
+	else if (vidsw->gpio)
+		gpiod_set_value(vidsw->gpio, vidsw->active);
+
+	return 0;
+}
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+static int vidsw_link_setup(struct media_entity *entity,
+		const struct media_pad *local,
+		const struct media_pad *remote, u32 flags)
+{
+	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+	struct vidsw *vidsw = to_vidsw(sd);
+
+	dev_dbg(vidsw->dev, "link setup %s -> %s", remote->entity->name,
+		local->entity->name);
+
+	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+		if (local->index == vidsw->active) {
+			dev_dbg(vidsw->dev, "going inactive\n");
+			vidsw->active = -1;
+		}
+		return 0;
+	}
+
+	return vidsw_set_mux(vidsw, local->index);
+}
+
+static struct media_entity_operations vidsw_ops = {
+	.link_setup = vidsw_link_setup,
+};
+#endif
+
+static int vidsw_s_routing(struct v4l2_subdev *sd, u32 input,
+			   u32 output, u32 config)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	return vidsw_set_mux(vidsw, input);
+}
+
+static int vidsw_async_init(struct vidsw *vidsw, struct device_node *node)
+{
+	struct v4l2_of_endpoint endpoint;
+	struct device_node *epnode;
+	int pad, numpads;
+#ifdef CONFIG_MEDIA_CONTROLLER
+	int ret;
+#endif
+
+	numpads = of_get_child_count(node);
+	if (numpads < 2) {
+		dev_err(vidsw->dev, "Not enough ports %d\n", numpads);
+		return -EINVAL;
+	}
+
+	vidsw->numpads = numpads;
+
+	/*
+	 * the last endpoint must define the mux output pad,
+	 * the rest are the mux input pads.
+	 */
+	vidsw->output_pad = numpads - 1;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+	vidsw->pads = devm_kzalloc(vidsw->dev,
+				   numpads * sizeof(*vidsw->pads),
+				   GFP_KERNEL);
+	if (!vidsw->pads)
+		return -ENOMEM;
+#endif
+
+	vidsw->endpoint = devm_kzalloc(vidsw->dev,
+				       numpads * sizeof(*vidsw->endpoint),
+				       GFP_KERNEL);
+	if (!vidsw->endpoint)
+		return -ENOMEM;
+
+	vidsw->format_mbus = devm_kzalloc(vidsw->dev,
+					  numpads * sizeof(*vidsw->format_mbus),
+					  GFP_KERNEL);
+	if (!vidsw->format_mbus)
+		return -ENOMEM;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+	vidsw->subdev.entity.ops = &vidsw_ops;
+
+	/* init the pad directions */
+	for (pad = 0; pad < vidsw->output_pad; pad++)
+		vidsw->pads[pad].flags = MEDIA_PAD_FL_SINK;
+	vidsw->pads[vidsw->output_pad].flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&vidsw->subdev.entity,
+				     vidsw->numpads, vidsw->pads);
+	if (ret < 0)
+		return ret;
+#endif
+
+	epnode = NULL;
+	for (pad = 0; pad < vidsw->numpads; pad++) {
+		epnode = of_graph_get_next_endpoint(node, epnode);
+		if (!epnode)
+			return -EINVAL;
+
+		v4l2_of_parse_endpoint(epnode, &endpoint);
+		vidsw->endpoint[pad] = endpoint;
+		of_node_put(epnode);
+	}
+
+	return 0;
+}
+
+static int vidsw_registered(struct v4l2_subdev *sd)
+{
+	return 0;
+}
+
+int vidsw_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	dev_dbg(vidsw->dev, "reporting configration %d\n", vidsw->active);
+
+	/* Mirror the input side on the output side */
+	cfg->type = vidsw->endpoint[vidsw->active].bus_type;
+	if (cfg->type == V4L2_MBUS_PARALLEL || cfg->type == V4L2_MBUS_BT656)
+		cfg->flags = vidsw->endpoint[vidsw->active].bus.parallel.flags;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops vidsw_subdev_video_ops = {
+	.g_mbus_config = vidsw_g_mbus_config,
+	.s_routing = vidsw_s_routing,
+};
+
+static int vidsw_get_format(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *sdformat)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	sdformat->format = vidsw->format_mbus[sdformat->pad];
+
+	return 0;
+}
+
+static int vidsw_set_format(struct v4l2_subdev *sd,
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *sdformat)
+{
+	struct vidsw *vidsw = container_of(sd, struct vidsw, subdev);
+
+	if (sdformat->pad >= vidsw->numpads)
+		return -EINVAL;
+
+	/* Output pad mirrors active input pad, no limitations on input pads */
+	if (sdformat->pad == vidsw->output_pad && vidsw->active >= 0)
+		sdformat->format = vidsw->format_mbus[vidsw->active];
+
+	if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY)
+		cfg->try_fmt = sdformat->format;
+	else
+		vidsw->format_mbus[sdformat->pad] = sdformat->format;
+
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops vidsw_pad_ops = {
+	.get_fmt = vidsw_get_format,
+	.set_fmt = vidsw_set_format,
+};
+
+static struct v4l2_subdev_ops vidsw_subdev_ops = {
+	.pad = &vidsw_pad_ops,
+	.video = &vidsw_subdev_video_ops,
+};
+
+static struct v4l2_subdev_internal_ops vidsw_internal_ops = {
+	.registered = vidsw_registered,
+};
+
+static int of_get_reg_field(struct device_node *node, struct reg_field *field)
+{
+	u32 reg_bit_mask[2];
+	int ret;
+
+	ret = of_property_read_u32_array(node, "reg", reg_bit_mask, 2);
+	if (ret < 0)
+		return ret;
+
+	field->reg = reg_bit_mask[0];
+	field->lsb = __ffs(reg_bit_mask[1]);
+	field->msb = __fls(reg_bit_mask[1]);
+
+	return 0;
+}
+
+static int vidsw_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct reg_field field;
+	struct vidsw *vidsw;
+	struct regmap *map;
+	int ret;
+
+	vidsw = devm_kzalloc(&pdev->dev, sizeof(*vidsw), GFP_KERNEL);
+	if (!vidsw)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, vidsw);
+
+	v4l2_subdev_init(&vidsw->subdev, &vidsw_subdev_ops);
+	v4l2_set_subdevdata(&vidsw->subdev, &pdev->dev);
+	vidsw->subdev.internal_ops = &vidsw_internal_ops;
+	snprintf(vidsw->subdev.name, sizeof(vidsw->subdev.name), "%s",
+			np->name);
+	vidsw->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	vidsw->subdev.dev = &pdev->dev;
+	vidsw->dev = &pdev->dev;
+	vidsw->active = -1;
+
+	ret = of_get_reg_field(np, &field);
+	if (ret == 0) {
+		map = syscon_regmap_lookup_by_phandle(np, "gpr");
+		if (!map) {
+			dev_err(&pdev->dev,
+				"Failed to get syscon register map\n");
+			return PTR_ERR(map);
+		}
+
+		vidsw->field = devm_regmap_field_alloc(&pdev->dev, map, field);
+		if (IS_ERR(vidsw->field)) {
+			dev_err(&pdev->dev,
+				"Failed to allocate regmap field\n");
+			return PTR_ERR(vidsw->field);
+		}
+	} else {
+		vidsw->gpio = devm_gpiod_get_optional(&pdev->dev, "mux",
+						      GPIOD_OUT_LOW);
+		if (IS_ERR(vidsw->gpio)) {
+			dev_err(&pdev->dev, "request for gpio failed\n");
+			return PTR_ERR(vidsw->gpio);
+		}
+
+		if (!vidsw->gpio)
+			dev_warn(&pdev->dev, "no control gpio defined\n");
+	}
+
+	ret = vidsw_async_init(vidsw, np);
+	if (ret)
+		return ret;
+
+	ret = v4l2_async_register_subdev(&vidsw->subdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int vidsw_remove(struct platform_device *pdev)
+{
+	/* FIXME */
+
+	return -EBUSY;
+}
+
+static const struct of_device_id vidsw_dt_ids[] = {
+	{ .compatible = "imx-video-mux", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vidsw_dt_ids);
+
+static struct platform_driver vidsw_driver = {
+	.probe		= vidsw_probe,
+	.remove		= vidsw_remove,
+	.driver		= {
+		.of_match_table = vidsw_dt_ids,
+		.name	= "imx-video-mux",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(vidsw_driver);
+
+MODULE_DESCRIPTION("i.MX video stream multiplexer");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
-- 
1.9.1


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

* [PATCH 22/28] media: imx: Add support for MIPI CSI-2 OV5640
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (20 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 21/28] media: imx: Add video switch Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 23/28] media: imx: Add support for Parallel OV5642 Steve Longerbeam
                     ` (5 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This driver is based on ov5640_mipi.c from Freescale imx_3.10.17_1.0.0_beta
branch, modified heavily for code cleanup and converted from int-device
to subdev.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig       |    8 +
 drivers/staging/media/imx/capture/Makefile      |    1 +
 drivers/staging/media/imx/capture/ov5640-mipi.c | 2303 +++++++++++++++++++++++
 3 files changed, 2312 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/ov5640-mipi.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index 1a64011..13d7231 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -17,4 +17,12 @@ config IMX_VIDEO_SWITCH
 	  multiplexer controlled by register bitfields as well as
 	  external multiplexers controller by a GPIO.
 
+config IMX_CAMERA_OV5640_MIPI
+       tristate "OmniVision OV5640 MIPI CSI-2 camera support"
+       depends on GPIOLIB && VIDEO_IMX_CAMERA
+       select IMX_MIPI_CSI2
+       default y
+       ---help---
+         MIPI CSI-2 OV5640 Camera support.
+
 endmenu
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index f17b199..1ad4fd2 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-camera.o
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
 obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
 obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
+obj-$(CONFIG_IMX_CAMERA_OV5640_MIPI) += ov5640-mipi.o
diff --git a/drivers/staging/media/imx/capture/ov5640-mipi.c b/drivers/staging/media/imx/capture/ov5640-mipi.c
new file mode 100644
index 0000000..acc6353
--- /dev/null
+++ b/drivers/staging/media/imx/capture/ov5640-mipi.c
@@ -0,0 +1,2303 @@
+/*
+ * Copyright (c) 2014 Mentor Graphics Inc.
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV5640_VOLTAGE_ANALOG               2800000
+#define OV5640_VOLTAGE_DIGITAL_CORE         1500000
+#define OV5640_VOLTAGE_DIGITAL_IO           1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+/* min/typical/max system clock (xclk) frequencies */
+#define OV5640_XCLK_MIN  6000000
+#define OV5640_XCLK_TYP 24000000
+#define OV5640_XCLK_MAX 54000000
+
+/* min/typical/max pixel clock (mclk) frequencies */
+#define OV5640_MCLK_MIN 48000000
+#define OV5640_MCLK_TYP 48000000
+#define OV5640_MCLK_MAX 96000000
+
+#define OV5640_CHIP_ID  0x300A
+
+#define OV5640_MAX_CONTROLS 64
+
+enum ov5640_mode {
+	ov5640_mode_MIN = 0,
+	ov5640_mode_QCIF_176_144 = 0,
+	ov5640_mode_QVGA_320_240,
+	ov5640_mode_VGA_640_480,
+	ov5640_mode_NTSC_720_480,
+	ov5640_mode_PAL_720_576,
+	ov5640_mode_XGA_1024_768,
+	ov5640_mode_720P_1280_720,
+	ov5640_mode_1080P_1920_1080,
+	ov5640_mode_QSXGA_2592_1944,
+	ov5640_num_modes,
+	ov5640_mode_INIT = 0xff, /*only for sensor init*/
+};
+
+enum ov5640_frame_rate {
+	ov5640_15_fps,
+	ov5640_30_fps
+};
+
+static int ov5640_framerates[] = {
+	[ov5640_15_fps] = 15,
+	[ov5640_30_fps] = 30,
+};
+#define ov5640_num_framerates ARRAY_SIZE(ov5640_framerates)
+
+/* image size under 1280 * 960 are SUBSAMPLING
+ * image size upper 1280 * 960 are SCALING
+ */
+enum ov5640_downsize_mode {
+	SUBSAMPLING,
+	SCALING,
+};
+
+struct reg_value {
+	u16 reg_addr;
+	u8 val;
+	u8 mask;
+	u32 delay_ms;
+};
+
+struct ov5640_mode_info {
+	enum ov5640_mode mode;
+	enum ov5640_downsize_mode dn_mode;
+	u32 width;
+	u32 height;
+	struct reg_value *init_data_ptr;
+	u32 init_data_size;
+};
+
+struct ov5640_dev {
+	struct i2c_client *i2c_client;
+	struct device *dev;
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_of_endpoint ep; /* the parsed DT endpoint info */
+	struct v4l2_mbus_framefmt fmt;
+	struct v4l2_captureparm streamcap;
+	struct clk *xclk; /* system clock to OV5640 */
+	int xclk_freq;    /* requested xclk freq from devicetree */
+
+	enum ov5640_mode current_mode;
+	enum ov5640_frame_rate current_fr;
+
+	bool on;
+	bool awb_on;
+	bool agc_on;
+
+	/* cached control settings */
+	int ctrl_cache[OV5640_MAX_CONTROLS];
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *pwdn_gpio;
+	struct gpio_desc *gp_gpio;
+
+	int prev_sysclk, prev_hts;
+	int ae_low, ae_high, ae_target;
+
+	struct regulator *io_regulator;
+	struct regulator *core_regulator;
+	struct regulator *analog_regulator;
+	struct regulator *gpo_regulator;
+};
+
+static inline struct ov5640_dev *to_ov5640_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5640_dev, sd);
+}
+
+static inline struct ov5640_dev *ctrl_to_ov5640_dev(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct ov5640_dev, ctrl_hdl);
+}
+
+struct ov5640_control {
+	struct v4l2_queryctrl ctrl;
+	int (*set)(struct ov5640_dev *sensor, int value);
+};
+
+static void ov5640_power(struct ov5640_dev *sensor, bool enable);
+static void ov5640_reset(struct ov5640_dev *sensor);
+static int ov5640_restore_ctrls(struct ov5640_dev *sensor);
+static int ov5640_set_agc(struct ov5640_dev *sensor, int value);
+static int ov5640_set_exposure(struct ov5640_dev *sensor, int value);
+static int ov5640_get_exposure(struct ov5640_dev *sensor);
+static int ov5640_set_gain(struct ov5640_dev *sensor, int value);
+static int ov5640_get_gain(struct ov5640_dev *sensor);
+
+static struct reg_value ov5640_init_setting_30fps_VGA[] = {
+
+	{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
+	{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
+	{0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},
+	{0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0},
+	{0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0},
+	{0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0},
+	{0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0},
+	{0x3705, 0x1a, 0, 0}, {0x3905, 0x02, 0, 0}, {0x3906, 0x10, 0, 0},
+	{0x3901, 0x0a, 0, 0}, {0x3731, 0x12, 0, 0}, {0x3600, 0x08, 0, 0},
+	{0x3601, 0x33, 0, 0}, {0x302d, 0x60, 0, 0}, {0x3620, 0x52, 0, 0},
+	{0x371b, 0x20, 0, 0}, {0x471c, 0x50, 0, 0}, {0x3a13, 0x43, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0xf8, 0, 0}, {0x3635, 0x13, 0, 0},
+	{0x3636, 0x03, 0, 0}, {0x3634, 0x40, 0, 0}, {0x3622, 0x01, 0, 0},
+	{0x3c01, 0xa4, 0, 0}, {0x3c04, 0x28, 0, 0}, {0x3c05, 0x98, 0, 0},
+	{0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0},
+	{0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0},
+	{0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0},
+	{0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0},
+	{0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0},
+	{0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0},
+	{0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0},
+	{0x5187, 0x09, 0, 0}, {0x5188, 0x09, 0, 0}, {0x5189, 0x88, 0, 0},
+	{0x518a, 0x54, 0, 0}, {0x518b, 0xee, 0, 0}, {0x518c, 0xb2, 0, 0},
+	{0x518d, 0x50, 0, 0}, {0x518e, 0x34, 0, 0}, {0x518f, 0x6b, 0, 0},
+	{0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0},
+	{0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0},
+	{0x5199, 0x6c, 0, 0}, {0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0},
+	{0x519c, 0x09, 0, 0}, {0x519d, 0x2b, 0, 0}, {0x519e, 0x38, 0, 0},
+	{0x5381, 0x1e, 0, 0}, {0x5382, 0x5b, 0, 0}, {0x5383, 0x08, 0, 0},
+	{0x5384, 0x0a, 0, 0}, {0x5385, 0x7e, 0, 0}, {0x5386, 0x88, 0, 0},
+	{0x5387, 0x7c, 0, 0}, {0x5388, 0x6c, 0, 0}, {0x5389, 0x10, 0, 0},
+	{0x538a, 0x01, 0, 0}, {0x538b, 0x98, 0, 0}, {0x5300, 0x08, 0, 0},
+	{0x5301, 0x30, 0, 0}, {0x5302, 0x10, 0, 0}, {0x5303, 0x00, 0, 0},
+	{0x5304, 0x08, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x08, 0, 0},
+	{0x5307, 0x16, 0, 0}, {0x5309, 0x08, 0, 0}, {0x530a, 0x30, 0, 0},
+	{0x530b, 0x04, 0, 0}, {0x530c, 0x06, 0, 0}, {0x5480, 0x01, 0, 0},
+	{0x5481, 0x08, 0, 0}, {0x5482, 0x14, 0, 0}, {0x5483, 0x28, 0, 0},
+	{0x5484, 0x51, 0, 0}, {0x5485, 0x65, 0, 0}, {0x5486, 0x71, 0, 0},
+	{0x5487, 0x7d, 0, 0}, {0x5488, 0x87, 0, 0}, {0x5489, 0x91, 0, 0},
+	{0x548a, 0x9a, 0, 0}, {0x548b, 0xaa, 0, 0}, {0x548c, 0xb8, 0, 0},
+	{0x548d, 0xcd, 0, 0}, {0x548e, 0xdd, 0, 0}, {0x548f, 0xea, 0, 0},
+	{0x5490, 0x1d, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x10, 0, 0}, {0x5589, 0x10, 0, 0}, {0x558a, 0x00, 0, 0},
+	{0x558b, 0xf8, 0, 0}, {0x5800, 0x23, 0, 0}, {0x5801, 0x14, 0, 0},
+	{0x5802, 0x0f, 0, 0}, {0x5803, 0x0f, 0, 0}, {0x5804, 0x12, 0, 0},
+	{0x5805, 0x26, 0, 0}, {0x5806, 0x0c, 0, 0}, {0x5807, 0x08, 0, 0},
+	{0x5808, 0x05, 0, 0}, {0x5809, 0x05, 0, 0}, {0x580a, 0x08, 0, 0},
+	{0x580b, 0x0d, 0, 0}, {0x580c, 0x08, 0, 0}, {0x580d, 0x03, 0, 0},
+	{0x580e, 0x00, 0, 0}, {0x580f, 0x00, 0, 0}, {0x5810, 0x03, 0, 0},
+	{0x5811, 0x09, 0, 0}, {0x5812, 0x07, 0, 0}, {0x5813, 0x03, 0, 0},
+	{0x5814, 0x00, 0, 0}, {0x5815, 0x01, 0, 0}, {0x5816, 0x03, 0, 0},
+	{0x5817, 0x08, 0, 0}, {0x5818, 0x0d, 0, 0}, {0x5819, 0x08, 0, 0},
+	{0x581a, 0x05, 0, 0}, {0x581b, 0x06, 0, 0}, {0x581c, 0x08, 0, 0},
+	{0x581d, 0x0e, 0, 0}, {0x581e, 0x29, 0, 0}, {0x581f, 0x17, 0, 0},
+	{0x5820, 0x11, 0, 0}, {0x5821, 0x11, 0, 0}, {0x5822, 0x15, 0, 0},
+	{0x5823, 0x28, 0, 0}, {0x5824, 0x46, 0, 0}, {0x5825, 0x26, 0, 0},
+	{0x5826, 0x08, 0, 0}, {0x5827, 0x26, 0, 0}, {0x5828, 0x64, 0, 0},
+	{0x5829, 0x26, 0, 0}, {0x582a, 0x24, 0, 0}, {0x582b, 0x22, 0, 0},
+	{0x582c, 0x24, 0, 0}, {0x582d, 0x24, 0, 0}, {0x582e, 0x06, 0, 0},
+	{0x582f, 0x22, 0, 0}, {0x5830, 0x40, 0, 0}, {0x5831, 0x42, 0, 0},
+	{0x5832, 0x24, 0, 0}, {0x5833, 0x26, 0, 0}, {0x5834, 0x24, 0, 0},
+	{0x5835, 0x22, 0, 0}, {0x5836, 0x22, 0, 0}, {0x5837, 0x26, 0, 0},
+	{0x5838, 0x44, 0, 0}, {0x5839, 0x24, 0, 0}, {0x583a, 0x26, 0, 0},
+	{0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
+	{0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
+	{0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
+	{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
+};
+
+static struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
+
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
+
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+	{0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0},
+	{0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_QCIF_176_144[] = {
+	{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+static struct reg_value ov5640_setting_15fps_QCIF_176_144[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_NTSC_720_480[] = {
+	{0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_NTSC_720_480[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_PAL_720_576[] = {
+	{0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_PAL_720_576[] = {
+	{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_720P_1280_720[] = {
+	{0x3008, 0x42, 0, 0},
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+	{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0},
+	{0x3008, 0x02, 0, 0}, {0x3503, 0,    0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_720P_1280_720[] = {
+	{0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+	{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0},
+	{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
+	{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
+	{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
+};
+
+static struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = {
+	{0x3008, 0x42, 0, 0},
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+	{0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+	{0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0},
+	{0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
+	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
+	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+	{0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
+	{0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
+	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
+	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
+	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
+	{0x3503, 0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = {
+	{0x3008, 0x42, 0, 0},
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+	{0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+	{0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0},
+	{0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0},
+	{0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0},
+	{0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+	{0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0},
+	{0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0},
+	{0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0},
+	{0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0},
+	{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
+	{0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
+	{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0},
+};
+
+static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = {
+	{0x4202, 0x0f, 0, 0},	/* stream off the sensor */
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, /*disable flip*/
+	{0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0},
+	{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
+	{0x3820, 0x40, 0, 0}, {0x3821, 0x06, 0, 0}, {0x3814, 0x11, 0, 0},
+	{0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
+	{0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0},
+	{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0},
+	{0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0},
+	{0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0},
+	{0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0},
+	{0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0},
+	{0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0},
+	{0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
+	{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
+	{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+	{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
+	{0x4202, 0x00, 0, 0},	/* stream on the sensor */
+};
+
+static struct ov5640_mode_info
+ov5640_mode_info_data[ov5640_num_framerates][ov5640_num_modes] = {
+	{
+		{ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144,
+		 ov5640_setting_15fps_QCIF_176_144,
+		 ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
+		{ov5640_mode_QVGA_320_240, SUBSAMPLING, 320,  240,
+		 ov5640_setting_15fps_QVGA_320_240,
+		 ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
+		{ov5640_mode_VGA_640_480, SUBSAMPLING, 640,  480,
+		 ov5640_setting_15fps_VGA_640_480,
+		 ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
+		{ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
+		 ov5640_setting_15fps_NTSC_720_480,
+		 ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
+		{ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576,
+		 ov5640_setting_15fps_PAL_720_576,
+		 ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
+		{ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768,
+		 ov5640_setting_15fps_XGA_1024_768,
+		 ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
+		{ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
+		 ov5640_setting_15fps_720P_1280_720,
+		 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
+		{ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
+		 ov5640_setting_15fps_1080P_1920_1080,
+		 ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
+		{ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944,
+		 ov5640_setting_15fps_QSXGA_2592_1944,
+		 ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
+	}, {
+		{ov5640_mode_QCIF_176_144, SUBSAMPLING, 176, 144,
+		 ov5640_setting_30fps_QCIF_176_144,
+		 ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
+		{ov5640_mode_QVGA_320_240, SUBSAMPLING, 320,  240,
+		 ov5640_setting_30fps_QVGA_320_240,
+		 ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
+		{ov5640_mode_VGA_640_480, SUBSAMPLING, 640,  480,
+		 ov5640_setting_30fps_VGA_640_480,
+		 ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
+		{ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480,
+		 ov5640_setting_30fps_NTSC_720_480,
+		 ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)},
+		{ov5640_mode_PAL_720_576, SUBSAMPLING, 720, 576,
+		 ov5640_setting_30fps_PAL_720_576,
+		 ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)},
+		{ov5640_mode_XGA_1024_768, SUBSAMPLING, 1024, 768,
+		 ov5640_setting_30fps_XGA_1024_768,
+		 ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)},
+		{ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720,
+		 ov5640_setting_30fps_720P_1280_720,
+		 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},
+		{ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080,
+		 ov5640_setting_30fps_1080P_1920_1080,
+		 ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)},
+		{ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0},
+	},
+};
+
+static int ov5640_probe(struct i2c_client *adapter,
+			const struct i2c_device_id *device_id);
+static int ov5640_remove(struct i2c_client *client);
+
+static int ov5640_write_reg(struct ov5640_dev *sensor, u16 reg, u8 val)
+{
+	u8 buf[3] = {0};
+	int ret;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	buf[2] = val;
+
+	ret = i2c_master_send(sensor->i2c_client, buf, 3);
+	if (ret < 0) {
+		v4l2_err(&sensor->sd, "%s: error: reg=%x, val=%x\n",
+			__func__, reg, val);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ov5640_read_reg(struct ov5640_dev *sensor, u16 reg, u8 *val)
+{
+	u8 reg_buf[2] = {0};
+	u8 read_val = 0;
+
+	reg_buf[0] = reg >> 8;
+	reg_buf[1] = reg & 0xff;
+
+	if (2 != i2c_master_send(sensor->i2c_client, reg_buf, 2)) {
+		v4l2_err(&sensor->sd, "%s: write reg error: reg=%x\n",
+			__func__, reg);
+		return -EIO;
+	}
+
+	if (1 != i2c_master_recv(sensor->i2c_client, &read_val, 1)) {
+		v4l2_err(&sensor->sd, "%s: read reg error: reg=%x, val=%x\n",
+			__func__, reg, read_val);
+		return -EIO;
+	}
+
+	*val = read_val;
+	return 0;
+}
+
+#define OV5640_READ_REG(s, r, v) {				\
+		ret = ov5640_read_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+#define OV5640_WRITE_REG(s, r, v) {				\
+		ret = ov5640_write_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5640_read_reg16(struct ov5640_dev *sensor, u16 reg, u16 *val)
+{
+	u8 hi, lo;
+	int ret;
+
+	OV5640_READ_REG(sensor, reg, &hi);
+	OV5640_READ_REG(sensor, reg+1, &lo);
+
+	*val = ((u16)hi << 8) | (u16)lo;
+	return 0;
+}
+#define OV5640_READ_REG16(s, r, v) {				\
+		ret = ov5640_read_reg16((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5640_write_reg16(struct ov5640_dev *sensor, u16 reg, u16 val)
+{
+	int ret;
+
+	OV5640_WRITE_REG(sensor, reg, val >> 8);
+	OV5640_WRITE_REG(sensor, reg+1, val & 0xff);
+	return 0;
+}
+#define OV5640_WRITE_REG16(s, r, v) {				\
+		ret = ov5640_write_reg16((s), (r), (v));	\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg,
+			  u8 mask, u8 val)
+{
+	u8 readval;
+	int ret;
+
+	OV5640_READ_REG(sensor, reg, &readval);
+
+	readval &= ~mask;
+	val &= mask;
+	val |= readval;
+
+	OV5640_WRITE_REG(sensor, reg, val);
+	return 0;
+}
+#define OV5640_MOD_REG(s, r, m, v) {				\
+		ret = ov5640_mod_reg((s), (r), (m), (v));	\
+		if (ret)					\
+			return ret;				\
+	}
+
+/* download ov5640 settings to sensor through i2c */
+static int ov5640_load_regs(struct ov5640_dev *sensor,
+			    struct reg_value *regs,
+			    int size)
+{
+	register u32 delay_ms = 0;
+	register u16 reg_addr = 0;
+	register u8 mask = 0;
+	register u8 val = 0;
+	int i, ret;
+
+	for (i = 0; i < size; ++i, ++regs) {
+		delay_ms = regs->delay_ms;
+		reg_addr = regs->reg_addr;
+		val = regs->val;
+		mask = regs->mask;
+
+		if (mask) {
+			OV5640_MOD_REG(sensor, reg_addr, mask, val);
+		} else {
+			OV5640_WRITE_REG(sensor, reg_addr, val);
+		}
+		if (delay_ms)
+			usleep_range(1000*delay_ms, 1000*delay_ms+100);
+	}
+
+	return 0;
+}
+
+static int ov5640_set_stream(struct ov5640_dev *sensor, bool on)
+{
+	int ret;
+
+	OV5640_WRITE_REG(sensor, 0x4202, on ? 0x00 : 0x0f);
+	return 0;
+}
+
+static int ov5640_get_sysclk(struct ov5640_dev *sensor)
+{
+	 /* calculate sysclk */
+	int xvclk = sensor->xclk_freq / 10000;
+	int multiplier, prediv, VCO, sysdiv, pll_rdiv;
+	int sclk_rdiv_map[] = {1, 2, 4, 8};
+	int bit_div2x = 1, sclk_rdiv, sysclk;
+	u8 temp1, temp2;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3034, &temp1);
+	temp2 = temp1 & 0x0f;
+	if (temp2 == 8 || temp2 == 10)
+		bit_div2x = temp2 / 2;
+
+	OV5640_READ_REG(sensor, 0x3035, &temp1);
+	sysdiv = temp1>>4;
+	if (sysdiv == 0)
+		sysdiv = 16;
+
+	OV5640_READ_REG(sensor, 0x3036, &temp1);
+	multiplier = temp1;
+
+	OV5640_READ_REG(sensor, 0x3037, &temp1);
+	prediv = temp1 & 0x0f;
+	pll_rdiv = ((temp1 >> 4) & 0x01) + 1;
+
+	OV5640_READ_REG(sensor, 0x3108, &temp1);
+	temp2 = temp1 & 0x03;
+	sclk_rdiv = sclk_rdiv_map[temp2];
+
+	VCO = xvclk * multiplier / prediv;
+
+	sysclk = VCO / sysdiv / pll_rdiv * 2 / bit_div2x / sclk_rdiv;
+
+	return sysclk;
+}
+
+static int ov5640_set_night_mode(struct ov5640_dev *sensor)
+{
+	 /* read HTS from register settings */
+	u8 mode;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3a00, &mode);
+	mode &= 0xfb;
+	OV5640_WRITE_REG(sensor, 0x3a00, mode);
+	return 0;
+}
+
+static int ov5640_get_HTS(struct ov5640_dev *sensor)
+{
+	 /* read HTS from register settings */
+	u16 HTS;
+	int ret;
+
+	OV5640_READ_REG16(sensor, 0x380c, &HTS);
+	return HTS;
+}
+
+static int ov5640_get_VTS(struct ov5640_dev *sensor)
+{
+	u16 VTS;
+	int ret;
+
+	OV5640_READ_REG16(sensor, 0x380e, &VTS);
+	return VTS;
+}
+
+static int ov5640_set_VTS(struct ov5640_dev *sensor, int VTS)
+{
+	int ret;
+
+	OV5640_WRITE_REG16(sensor, 0x380e, VTS);
+	return 0;
+}
+
+static int ov5640_get_light_freq(struct ov5640_dev *sensor)
+{
+	/* get banding filter value */
+	u8 temp, temp1;
+	int light_freq = 0;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3c01, &temp);
+
+	if (temp & 0x80) {
+		/* manual */
+		OV5640_READ_REG(sensor, 0x3c00, &temp1);
+		if (temp1 & 0x04) {
+			/* 50Hz */
+			light_freq = 50;
+		} else {
+			/* 60Hz */
+			light_freq = 60;
+		}
+	} else {
+		/* auto */
+		OV5640_READ_REG(sensor, 0x3c0c, &temp1);
+		if (temp1 & 0x01) {
+			/* 50Hz */
+			light_freq = 50;
+		} else {
+			/* 60Hz */
+		}
+	}
+
+	return light_freq;
+}
+
+static int ov5640_set_bandingfilter(struct ov5640_dev *sensor)
+{
+	int prev_vts;
+	int band_step60, max_band60, band_step50, max_band50;
+	int ret;
+
+	/* read preview PCLK */
+	ret = ov5640_get_sysclk(sensor);
+	if (ret < 0)
+		return ret;
+	sensor->prev_sysclk = ret;
+	/* read preview HTS */
+	ret = ov5640_get_HTS(sensor);
+	if (ret < 0)
+		return ret;
+	sensor->prev_hts = ret;
+
+	/* read preview VTS */
+	ret = ov5640_get_VTS(sensor);
+	if (ret < 0)
+		return ret;
+	prev_vts = ret;
+
+	/* calculate banding filter */
+	/* 60Hz */
+	band_step60 = sensor->prev_sysclk * 100 / sensor->prev_hts * 100/120;
+	OV5640_WRITE_REG16(sensor, 0x3a0a, band_step60);
+
+	max_band60 = (int)((prev_vts-4)/band_step60);
+	OV5640_WRITE_REG(sensor, 0x3a0d, max_band60);
+
+	/* 50Hz */
+	band_step50 = sensor->prev_sysclk * 100 / sensor->prev_hts;
+	OV5640_WRITE_REG16(sensor, 0x3a08, band_step50);
+
+	max_band50 = (int)((prev_vts-4)/band_step50);
+	OV5640_WRITE_REG(sensor, 0x3a0e, max_band50);
+
+	return 0;
+}
+
+static int ov5640_set_AE_target(struct ov5640_dev *sensor, int target)
+{
+	/* stable in high */
+	int fast_high, fast_low;
+	int ret;
+
+	sensor->ae_low = target * 23 / 25;	/* 0.92 */
+	sensor->ae_high = target * 27 / 25;	/* 1.08 */
+
+	fast_high = sensor->ae_high<<1;
+	if (fast_high > 255)
+		fast_high = 255;
+
+	fast_low = sensor->ae_low >> 1;
+
+	OV5640_WRITE_REG(sensor, 0x3a0f, sensor->ae_high);
+	OV5640_WRITE_REG(sensor, 0x3a10, sensor->ae_low);
+	OV5640_WRITE_REG(sensor, 0x3a1b, sensor->ae_high);
+	OV5640_WRITE_REG(sensor, 0x3a1e, sensor->ae_low);
+	OV5640_WRITE_REG(sensor, 0x3a11, fast_high);
+	OV5640_WRITE_REG(sensor, 0x3a1f, fast_low);
+
+	return 0;
+}
+
+static int ov5640_binning_on(struct ov5640_dev *sensor)
+{
+	u8 temp;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x3821, &temp);
+	temp &= 0xfe;
+
+	return temp ? 1 : 0;
+}
+
+static int ov5640_set_virtual_channel(struct ov5640_dev *sensor)
+{
+	u8 temp, channel = sensor->ep.base.id;
+	int ret;
+
+	OV5640_READ_REG(sensor, 0x4814, &temp);
+	temp &= ~(3 << 6);
+	temp |= (channel << 6);
+	OV5640_WRITE_REG(sensor, 0x4814, temp);
+
+	return 0;
+}
+
+static enum ov5640_mode
+ov5640_find_nearest_mode(struct ov5640_dev *sensor,
+			 int width, int height)
+{
+	int i;
+
+	for (i = ov5640_num_modes - 1; i >= 0; i--) {
+		if (ov5640_mode_info_data[0][i].width <= width &&
+		    ov5640_mode_info_data[0][i].height <= height)
+			break;
+	}
+
+	if (i < 0)
+		i = 0;
+
+	return (enum ov5640_mode)i;
+}
+
+/*
+ * sensor changes between scaling and subsampling, go through
+ * exposure calculation
+ */
+static int ov5640_change_mode_exposure_calc(struct ov5640_dev *sensor,
+					    enum ov5640_frame_rate frame_rate,
+					    enum ov5640_mode mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	u8 average;
+	int prev_shutter, prev_gain16;
+	int cap_shutter, cap_gain16;
+	int cap_sysclk, cap_hts, cap_vts;
+	int light_freq, cap_bandfilt, cap_maxband;
+	long cap_gain16_shutter;
+	int ret = 0;
+
+	/* check if the input mode and frame rate is valid */
+	mode_data = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+	mode_size = ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+	sensor->fmt.width = ov5640_mode_info_data[frame_rate][mode].width;
+	sensor->fmt.height = ov5640_mode_info_data[frame_rate][mode].height;
+
+	if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
+	    mode_data == NULL || mode_size == 0)
+		return -EINVAL;
+
+	/* auto focus */
+	/* ov5640_auto_focus();//if no af function, just skip it */
+
+	/* turn off AE/AG */
+	ret = ov5640_set_agc(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	/* read preview shutter */
+	ret = ov5640_get_exposure(sensor);
+	if (ret < 0)
+		return ret;
+	prev_shutter = ret;
+	ret = ov5640_binning_on(sensor);
+	if (ret < 0)
+		return ret;
+	if (ret && mode != ov5640_mode_720P_1280_720 &&
+	    mode != ov5640_mode_1080P_1920_1080)
+		prev_shutter *= 2;
+
+	/* read preview gain */
+	ret = ov5640_get_gain(sensor);
+	if (ret < 0)
+		return ret;
+	prev_gain16 = ret;
+
+	/* get average */
+	OV5640_READ_REG(sensor, 0x56a1, &average);
+
+	/* turn off night mode for capture */
+	ret = ov5640_set_night_mode(sensor);
+	if (ret < 0)
+		return ret;
+
+	/* turn off overlay */
+	/* OV5640_WRITE_REG(0x3022, 0x06); //if no af function,
+	   just skip it */
+
+	ret = ov5640_set_stream(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	/* Write capture setting */
+	ret = ov5640_load_regs(sensor, mode_data, mode_size);
+	if (ret < 0)
+		return ret;
+
+	/* read capture VTS */
+	ret = ov5640_get_VTS(sensor);
+	if (ret < 0)
+		return ret;
+	cap_vts = ret;
+	ret = ov5640_get_HTS(sensor);
+	if (ret < 0)
+		return ret;
+	cap_hts = ret;
+	ret = ov5640_get_sysclk(sensor);
+	if (ret < 0)
+		return ret;
+	cap_sysclk = ret;
+
+	/* calculate capture banding filter */
+	ret = ov5640_get_light_freq(sensor);
+	if (ret < 0)
+		return ret;
+	light_freq = ret;
+
+	if (light_freq == 60) {
+		/* 60Hz */
+		cap_bandfilt = cap_sysclk * 100 / cap_hts * 100 / 120;
+	} else {
+		/* 50Hz */
+		cap_bandfilt = cap_sysclk * 100 / cap_hts;
+	}
+	cap_maxband = (int)((cap_vts - 4) / cap_bandfilt);
+
+	/* calculate capture shutter/gain16 */
+	if (average > sensor->ae_low && average < sensor->ae_high) {
+		/* in stable range */
+		cap_gain16_shutter =
+			prev_gain16 * prev_shutter *
+			cap_sysclk / sensor->prev_sysclk *
+			sensor->prev_hts / cap_hts *
+			sensor->ae_target / average;
+	} else {
+		cap_gain16_shutter =
+			prev_gain16 * prev_shutter *
+			cap_sysclk / sensor->prev_sysclk *
+			sensor->prev_hts / cap_hts;
+	}
+
+	/* gain to shutter */
+	if (cap_gain16_shutter < (cap_bandfilt * 16)) {
+		/* shutter < 1/100 */
+		cap_shutter = cap_gain16_shutter / 16;
+		if (cap_shutter < 1)
+			cap_shutter = 1;
+
+		cap_gain16 = cap_gain16_shutter / cap_shutter;
+		if (cap_gain16 < 16)
+			cap_gain16 = 16;
+	} else {
+		if (cap_gain16_shutter > (cap_bandfilt * cap_maxband * 16)) {
+			/* exposure reach max */
+			cap_shutter = cap_bandfilt * cap_maxband;
+			cap_gain16 = cap_gain16_shutter / cap_shutter;
+		} else {
+			/* 1/100 < (cap_shutter = n/100) =< max */
+			cap_shutter =
+				((int)(cap_gain16_shutter / 16 / cap_bandfilt))
+				* cap_bandfilt;
+			cap_gain16 = cap_gain16_shutter / cap_shutter;
+		}
+	}
+
+	/* write capture gain */
+	ret = ov5640_set_gain(sensor, cap_gain16);
+	if (ret < 0)
+		return ret;
+
+	/* write capture shutter */
+	if (cap_shutter > (cap_vts - 4)) {
+		cap_vts = cap_shutter + 4;
+		ret = ov5640_set_VTS(sensor, cap_vts);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = ov5640_set_exposure(sensor, cap_shutter);
+	if (ret < 0)
+		return ret;
+
+	return ov5640_set_stream(sensor, true);
+}
+
+/*
+ * if sensor changes inside scaling or subsampling
+ * change mode directly
+ */
+static int ov5640_change_mode_direct(struct ov5640_dev *sensor,
+				     enum ov5640_frame_rate frame_rate,
+				     enum ov5640_mode mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	int ret = 0;
+
+	/* check if the input mode and frame rate is valid */
+	mode_data = ov5640_mode_info_data[frame_rate][mode].init_data_ptr;
+	mode_size = ov5640_mode_info_data[frame_rate][mode].init_data_size;
+
+	sensor->fmt.width = ov5640_mode_info_data[frame_rate][mode].width;
+	sensor->fmt.height = ov5640_mode_info_data[frame_rate][mode].height;
+
+	if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
+	    mode_data == NULL || mode_size == 0)
+		return -EINVAL;
+
+	/* turn off AE/AG */
+	ret = ov5640_set_agc(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_set_stream(sensor, false);
+	if (ret < 0)
+		return ret;
+
+	/* Write capture setting */
+	ret = ov5640_load_regs(sensor, mode_data, mode_size);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_set_stream(sensor, true);
+	if (ret < 0)
+		return ret;
+
+	return ov5640_set_agc(sensor, true);
+}
+
+static int ov5640_change_mode(struct ov5640_dev *sensor,
+			      enum ov5640_frame_rate frame_rate,
+			      enum ov5640_mode mode,
+			      enum ov5640_mode orig_mode)
+{
+	enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	int ret = 0;
+
+	if ((mode >= ov5640_num_modes || mode < ov5640_mode_MIN) &&
+	    mode != ov5640_mode_INIT) {
+		v4l2_err(&sensor->sd, "Wrong ov5640 mode detected!\n");
+		return -EINVAL;
+	}
+
+	dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode;
+	orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode;
+	if (mode == ov5640_mode_INIT) {
+		mode_data = ov5640_init_setting_30fps_VGA;
+		mode_size = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
+
+		sensor->fmt.width = 640;
+		sensor->fmt.height = 480;
+		ret = ov5640_load_regs(sensor, mode_data, mode_size);
+		if (ret < 0)
+			return ret;
+
+		mode_data = ov5640_setting_30fps_VGA_640_480;
+		mode_size = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480);
+		ret = ov5640_load_regs(sensor, mode_data, mode_size);
+	} else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
+			(dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
+		/* change between subsampling and scaling
+		 * go through exposure calucation */
+		ret = ov5640_change_mode_exposure_calc(sensor, frame_rate,
+							  mode);
+	} else {
+		/* change inside subsampling or scaling
+		 * download firmware directly */
+		ret = ov5640_change_mode_direct(sensor, frame_rate, mode);
+	}
+
+	if (ret < 0)
+		return ret;
+
+	ret = ov5640_set_AE_target(sensor, sensor->ae_target);
+	if (ret < 0)
+		return ret;
+	ret = ov5640_get_light_freq(sensor);
+	if (ret < 0)
+		return ret;
+	ret = ov5640_set_bandingfilter(sensor);
+	if (ret < 0)
+		return ret;
+	ret = ov5640_set_virtual_channel(sensor);
+	if (ret < 0)
+		return ret;
+
+	/* restore controls */
+	ov5640_restore_ctrls(sensor);
+
+	if (ret >= 0 && mode != ov5640_mode_INIT) {
+		sensor->current_mode = mode;
+		sensor->current_fr = frame_rate;
+	}
+
+	return 0;
+}
+
+/* restore the last set video mode after chip power-on */
+static int ov5640_restore_mode(struct ov5640_dev *sensor)
+{
+	int ret = 0;
+
+	/* first we need to set some initial register values */
+	ret = ov5640_change_mode(sensor, sensor->current_fr,
+				    ov5640_mode_INIT, ov5640_mode_INIT);
+	if (ret < 0)
+		return ret;
+
+	/* now restore the last capture mode */
+	return ov5640_change_mode(sensor,
+				  sensor->current_fr,
+				  sensor->current_mode,
+				  ov5640_mode_VGA_640_480);
+}
+
+static int ov5640_regulators_on(struct ov5640_dev *sensor)
+{
+	int ret;
+
+	if (sensor->io_regulator) {
+		ret = regulator_enable(sensor->io_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "io reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->core_regulator) {
+		ret = regulator_enable(sensor->core_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "core reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->gpo_regulator) {
+		ret = regulator_enable(sensor->gpo_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "gpo reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->analog_regulator) {
+		ret = regulator_enable(sensor->analog_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "analog reg enable failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void ov5640_regulators_off(struct ov5640_dev *sensor)
+{
+	if (sensor->analog_regulator)
+		regulator_disable(sensor->analog_regulator);
+	if (sensor->core_regulator)
+		regulator_disable(sensor->core_regulator);
+	if (sensor->io_regulator)
+		regulator_disable(sensor->io_regulator);
+	if (sensor->gpo_regulator)
+		regulator_disable(sensor->gpo_regulator);
+}
+
+/* --------------- Subdev Operations --------------- */
+
+static int ov5640_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	int ret;
+
+	if (on && !sensor->on) {
+		if (sensor->xclk)
+			clk_prepare_enable(sensor->xclk);
+
+		ret = ov5640_regulators_on(sensor);
+		if (ret)
+			return ret;
+
+		ov5640_reset(sensor);
+		ov5640_power(sensor, true);
+		ret = ov5640_restore_mode(sensor);
+		if (ret)
+			return ret;
+		/*
+		 * NOTE: Freescale adds a long delay (600 msec) after
+		 * powering up and programming a mode on the ov5640-mipi
+		 * camera (search for "msec_wait4stable" in FSL's
+		 * ov5640_mipi.c), which equivalently would need to go
+		 * right here. If we run into MIPI CSI-2 receiver dphy
+		 * ready timeouts, it might be a clue to add that delay
+		 * here.
+		 */
+	} else if (!on && sensor->on) {
+		ov5640_power(sensor, false);
+
+		ov5640_regulators_off(sensor);
+
+		if (sensor->xclk)
+			clk_disable_unprepare(sensor->xclk);
+	}
+
+	sensor->on = on;
+
+	return 0;
+}
+
+static int ov5640_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	/* This is the only case currently handled. */
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	cparm->capability = sensor->streamcap.capability;
+	cparm->timeperframe = sensor->streamcap.timeperframe;
+	cparm->capturemode = sensor->streamcap.capturemode;
+
+	return 0;
+}
+
+static int ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	enum ov5640_frame_rate frame_rate;
+	u32 tgt_fps;	/* target frames per secound */
+	int ret = 0;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	/* Check that the new frame rate is allowed. */
+	if ((timeperframe->numerator == 0) ||
+	    (timeperframe->denominator == 0)) {
+		timeperframe->denominator = DEFAULT_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps > MAX_FPS) {
+		timeperframe->denominator = MAX_FPS;
+		timeperframe->numerator = 1;
+	} else if (tgt_fps < MIN_FPS) {
+		timeperframe->denominator = MIN_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	/* Actual frame rate we use */
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps == 15)
+		frame_rate = ov5640_15_fps;
+	else if (tgt_fps == 30)
+		frame_rate = ov5640_30_fps;
+	else {
+		v4l2_err(&sensor->sd, "frame rate %u not supported!\n",
+			 tgt_fps);
+		return -EINVAL;
+	}
+
+	ret = ov5640_change_mode(sensor, frame_rate,
+				 sensor->current_mode,
+				 sensor->current_mode);
+	if (ret < 0)
+		return ret;
+
+	sensor->streamcap.timeperframe = *timeperframe;
+
+	return 0;
+}
+
+static int ov5640_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	if (format->pad)
+		return -EINVAL;
+
+	format->format = sensor->fmt;
+
+	return 0;
+}
+
+static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
+				   struct v4l2_mbus_framefmt *fmt,
+				   enum ov5640_mode *new_mode)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	enum ov5640_mode mode;
+
+	mode = ov5640_find_nearest_mode(sensor, fmt->width, fmt->height);
+
+	fmt->width = ov5640_mode_info_data[0][mode].width;
+	fmt->height = ov5640_mode_info_data[0][mode].height;
+	fmt->code = sensor->fmt.code;
+
+	if (new_mode)
+		*new_mode = mode;
+	return 0;
+}
+
+static int ov5640_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	enum ov5640_mode new_mode;
+	int ret;
+
+	if (format->pad)
+		return -EINVAL;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		ret = ov5640_try_fmt_internal(sd, &format->format, NULL);
+		if (ret)
+			return ret;
+		cfg->try_fmt = format->format;
+		return 0;
+	}
+
+	ret = ov5640_try_fmt_internal(sd, &format->format, &new_mode);
+	if (ret)
+		return ret;
+
+	ret = ov5640_change_mode(sensor, sensor->current_fr,
+				 new_mode, sensor->current_mode);
+	if (ret >= 0)
+		sensor->fmt = format->format;
+
+	return ret;
+}
+
+
+/*
+ * Sensor Controls.
+ */
+
+static int ov5640_set_hue(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 0, 1 << 0);
+		OV5640_WRITE_REG16(sensor, 0x5581, value);
+	} else
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 0, 0);
+
+	return 0;
+}
+
+static int ov5640_set_contrast(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 2, 1 << 2);
+		OV5640_WRITE_REG(sensor, 0x5585, value & 0xff);
+	} else
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 2, 0);
+
+	return 0;
+}
+
+static int ov5640_set_saturation(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 1, 1 << 1);
+		OV5640_WRITE_REG(sensor, 0x5583, value & 0xff);
+		OV5640_WRITE_REG(sensor, 0x5584, value & 0xff);
+	} else
+		OV5640_MOD_REG(sensor, 0x5580, 1 << 1, 0);
+
+	return 0;
+}
+
+static int ov5640_set_awb(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	sensor->awb_on = value ? true : false;
+	OV5640_MOD_REG(sensor, 0x3406, 1 << 0, sensor->awb_on ? 0 : 1);
+	return 0;
+}
+
+static int ov5640_set_red_balance(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG(sensor, 0x3401, value & 0xff);
+	OV5640_WRITE_REG(sensor, 0x3400, (value & 0xf00) >> 8);
+	return 0;
+}
+
+#if 0
+static int ov5640_set_green_balance(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG(sensor, 0x3403, value & 0xff);
+	OV5640_WRITE_REG(sensor, 0x3402, (value & 0xf00) >> 8);
+	return 0;
+}
+#endif
+
+static int ov5640_set_blue_balance(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG(sensor, 0x3405, value & 0xff);
+	OV5640_WRITE_REG(sensor, 0x3404, (value & 0xf00) >> 8);
+	return 0;
+}
+
+static int ov5640_set_exposure(struct ov5640_dev *sensor, int value)
+{
+	u16 max_exp = 0;
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_READ_REG16(sensor, 0x350c, &max_exp);
+	if (value < max_exp) {
+		u32 exp = value << 4;
+
+		OV5640_WRITE_REG(sensor, 0x3502, exp & 0xff);
+		OV5640_WRITE_REG(sensor, 0x3501, (exp >> 8) & 0xff);
+		OV5640_WRITE_REG(sensor, 0x3500, (exp >> 16) & 0x0f);
+	}
+
+	return 0;
+}
+
+/* read exposure, in number of line periods */
+static int ov5640_get_exposure(struct ov5640_dev *sensor)
+{
+	u8 temp;
+	int exp, ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_READ_REG(sensor, 0x3500, &temp);
+	exp = ((int)temp & 0x0f) << 16;
+	OV5640_READ_REG(sensor, 0x3501, &temp);
+	exp |= ((int)temp << 8);
+	OV5640_READ_REG(sensor, 0x3502, &temp);
+	exp |= (int)temp;
+
+	return exp >> 4;
+}
+
+static int ov5640_set_agc(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	/* this enables/disables both AEC and AGC */
+	sensor->agc_on = value ? true : false;
+	OV5640_MOD_REG(sensor, 0x3503, 0x3, sensor->agc_on ? 0 : 0x3);
+
+	return 0;
+}
+
+static int ov5640_set_gain(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_WRITE_REG16(sensor, 0x350a, value & 0x3ff);
+	return 0;
+}
+
+static int ov5640_get_gain(struct ov5640_dev *sensor)
+{
+	u16 gain;
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5640_READ_REG16(sensor, 0x350a, &gain);
+
+	return gain & 0x3ff;
+}
+
+#if 0
+static int ov5640_set_test_pattern(struct ov5640_dev *sensor, int value)
+{
+	int ret;
+
+	OV5640_MOD_REG(sensor, 0x503d, 0xa4, value ? 0xa4 : 0);
+	return 0;
+}
+#endif
+
+static struct ov5640_control ov5640_ctrls[] = {
+	{
+		.set = ov5640_set_agc,
+		.ctrl = {
+			.id = V4L2_CID_AUTOGAIN,
+			.name = "Auto Gain/Exposure Control",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5640_set_exposure,
+		.ctrl = {
+			.id = V4L2_CID_EXPOSURE,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 65535,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_gain,
+		.ctrl = {
+			.id = V4L2_CID_GAIN,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 1023,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_hue,
+		.ctrl = {
+			.id = V4L2_CID_HUE,
+			.name = "Hue",
+			.minimum = 0,
+			.maximum = 359,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_contrast,
+		.ctrl = {
+			.id = V4L2_CID_CONTRAST,
+			.name = "Contrast",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_saturation,
+		.ctrl = {
+			.id = V4L2_CID_SATURATION,
+			.name = "Saturation",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 64,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_awb,
+		.ctrl = {
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5640_set_red_balance,
+		.ctrl = {
+			.id = V4L2_CID_RED_BALANCE,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5640_set_blue_balance,
+		.ctrl = {
+			.id = V4L2_CID_BLUE_BALANCE,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	},
+};
+#define OV5640_NUM_CONTROLS ARRAY_SIZE(ov5640_ctrls)
+
+static struct ov5640_control *ov5640_get_ctrl(int id, int *index)
+{
+	struct ov5640_control *ret = NULL;
+	int i;
+
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		if (id == ov5640_ctrls[i].ctrl.id) {
+			ret = &ov5640_ctrls[i];
+			break;
+		}
+	}
+
+	if (ret && index)
+		*index = i;
+	return ret;
+}
+
+static int ov5640_restore_ctrls(struct ov5640_dev *sensor)
+{
+	struct ov5640_control *c;
+	int i;
+
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		c = &ov5640_ctrls[i];
+		c->set(sensor, sensor->ctrl_cache[i]);
+	}
+
+	return 0;
+}
+
+static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov5640_dev *sensor = ctrl_to_ov5640_dev(ctrl);
+	struct ov5640_control *c;
+	int ret = 0;
+	int i;
+
+	c = ov5640_get_ctrl(ctrl->id, &i);
+	if (!c)
+		return -EINVAL;
+
+	ret = c->set(sensor, ctrl->val);
+	/* update cached value if no error */
+	if (!ret)
+		sensor->ctrl_cache[i] = ctrl->val;
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5640_ctrl_ops = {
+	.s_ctrl = ov5640_s_ctrl,
+};
+
+static int ov5640_init_controls(struct ov5640_dev *sensor)
+{
+	struct ov5640_control *c;
+	int i;
+
+	v4l2_ctrl_handler_init(&sensor->ctrl_hdl, OV5640_NUM_CONTROLS);
+
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		c = &ov5640_ctrls[i];
+
+		v4l2_ctrl_new_std(&sensor->ctrl_hdl, &ov5640_ctrl_ops,
+				  c->ctrl.id, c->ctrl.minimum, c->ctrl.maximum,
+				  c->ctrl.step, c->ctrl.default_value);
+	}
+
+	sensor->sd.ctrl_handler = &sensor->ctrl_hdl;
+	if (sensor->ctrl_hdl.error) {
+		int err = sensor->ctrl_hdl.error;
+
+		v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+		v4l2_err(&sensor->sd, "%s: error %d\n", __func__, err);
+		return err;
+	}
+	v4l2_ctrl_handler_setup(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static int ov5640_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->pad)
+		return -EINVAL;
+	if (fse->index >= ov5640_num_modes)
+		return -EINVAL;
+
+	fse->min_width = fse->max_width =
+		ov5640_mode_info_data[0][fse->index].width;
+	fse->min_height = fse->max_height =
+		ov5640_mode_info_data[0][fse->index].height;
+
+	return 0;
+}
+
+static int ov5640_enum_frame_interval(
+	struct v4l2_subdev *sd,
+	struct v4l2_subdev_pad_config *cfg,
+	struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+	enum ov5640_mode mode;
+
+	if (fie->pad)
+		return -EINVAL;
+	if (fie->index < 0 || fie->index >= ov5640_num_framerates)
+		return -EINVAL;
+
+	if (fie->width == 0 || fie->height == 0)
+		return -EINVAL;
+
+	mode = ov5640_find_nearest_mode(sensor, fie->width, fie->height);
+
+	if (ov5640_mode_info_data[fie->index][mode].init_data_ptr == NULL)
+		return -EINVAL;
+
+	fie->interval.numerator = 1;
+	fie->interval.denominator = ov5640_framerates[fie->index];
+
+	dev_dbg(sensor->dev, "%dx%d: [%d] = %d fps\n",
+		fie->width, fie->height, fie->index, fie->interval.denominator);
+	return 0;
+}
+
+static int ov5640_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	*status = !sensor->on ? V4L2_IN_ST_NO_POWER : 0;
+
+	return 0;
+}
+
+static int ov5640_s_routing(struct v4l2_subdev *sd, u32 input,
+			    u32 output, u32 config)
+{
+	return (input != 0) ? -EINVAL : 0;
+}
+
+static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	if (code->pad)
+		return -EINVAL;
+	if (code->index != 0)
+		return -EINVAL;
+
+	code->code = sensor->fmt.code;
+
+	return 0;
+}
+
+static int ov5640_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	cfg->type = V4L2_MBUS_CSI2;
+	cfg->flags = sensor->ep.bus.mipi_csi2.flags;
+	cfg->flags |= (1 << (sensor->ep.bus.mipi_csi2.num_data_lanes - 1));
+	cfg->flags |= V4L2_MBUS_CSI2_CHANNEL_0;
+
+	return 0;
+}
+
+static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	return ov5640_set_stream(sensor, enable);
+}
+
+static struct v4l2_subdev_core_ops ov5640_core_ops = {
+	.s_power = ov5640_s_power,
+	.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,
+};
+
+static struct v4l2_subdev_video_ops ov5640_video_ops = {
+	.s_parm = ov5640_s_parm,
+	.g_parm = ov5640_g_parm,
+	.g_input_status = ov5640_g_input_status,
+	.s_routing = ov5640_s_routing,
+	.g_mbus_config  = ov5640_g_mbus_config,
+	.s_stream = ov5640_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5640_pad_ops = {
+	.enum_mbus_code = ov5640_enum_mbus_code,
+	.get_fmt = ov5640_get_fmt,
+	.set_fmt = ov5640_set_fmt,
+	.enum_frame_size = ov5640_enum_frame_size,
+	.enum_frame_interval = ov5640_enum_frame_interval,
+};
+
+static struct v4l2_subdev_ops ov5640_subdev_ops = {
+	.core = &ov5640_core_ops,
+	.video = &ov5640_video_ops,
+	.pad = &ov5640_pad_ops,
+};
+
+static void ov5640_power(struct ov5640_dev *sensor, bool enable)
+{
+	gpiod_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
+}
+
+static void ov5640_reset(struct ov5640_dev *sensor)
+{
+	gpiod_set_value(sensor->reset_gpio, 0);
+
+	/* camera power cycle */
+	ov5640_power(sensor, false);
+	usleep_range(5000, 10000);
+	ov5640_power(sensor, true);
+	usleep_range(5000, 10000);
+
+	gpiod_set_value(sensor->reset_gpio, 1);
+	usleep_range(1000, 2000);
+
+	gpiod_set_value(sensor->reset_gpio, 0);
+	usleep_range(5000, 10000);
+}
+
+static void ov5640_get_regulators(struct ov5640_dev *sensor)
+{
+	sensor->io_regulator = devm_regulator_get(sensor->dev, "DOVDD");
+	if (!IS_ERR(sensor->io_regulator)) {
+		regulator_set_voltage(sensor->io_regulator,
+				      OV5640_VOLTAGE_DIGITAL_IO,
+				      OV5640_VOLTAGE_DIGITAL_IO);
+	} else {
+		dev_dbg(sensor->dev, "%s: no io voltage reg found\n",
+			__func__);
+		sensor->io_regulator = NULL;
+	}
+
+	sensor->core_regulator = devm_regulator_get(sensor->dev, "DVDD");
+	if (!IS_ERR(sensor->core_regulator)) {
+		regulator_set_voltage(sensor->core_regulator,
+				      OV5640_VOLTAGE_DIGITAL_CORE,
+				      OV5640_VOLTAGE_DIGITAL_CORE);
+	} else {
+		sensor->core_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no core voltage reg found\n",
+			__func__);
+	}
+
+	sensor->analog_regulator = devm_regulator_get(sensor->dev, "AVDD");
+	if (!IS_ERR(sensor->analog_regulator)) {
+		regulator_set_voltage(sensor->analog_regulator,
+				      OV5640_VOLTAGE_ANALOG,
+				      OV5640_VOLTAGE_ANALOG);
+	} else {
+		sensor->analog_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no analog voltage reg found\n",
+			__func__);
+	}
+}
+
+static int ov5640_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *endpoint;
+	struct ov5640_dev *sensor;
+	int i, xclk, ret;
+
+	sensor = devm_kzalloc(dev, sizeof(struct ov5640_dev), GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->i2c_client = client;
+	sensor->dev = dev;
+	sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
+	sensor->fmt.width = 640;
+	sensor->fmt.height = 480;
+	sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY |
+					   V4L2_CAP_TIMEPERFRAME;
+	sensor->streamcap.capturemode = 0;
+	sensor->streamcap.timeperframe.denominator = DEFAULT_FPS;
+	sensor->streamcap.timeperframe.numerator = 1;
+
+	sensor->current_mode = ov5640_mode_VGA_640_480;
+	sensor->current_fr = ov5640_30_fps;
+
+	sensor->ae_target = 52;
+
+	endpoint = of_graph_get_next_endpoint(client->dev.of_node, NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	v4l2_of_parse_endpoint(endpoint, &sensor->ep);
+	if (sensor->ep.bus_type != V4L2_MBUS_CSI2) {
+		dev_err(dev, "invalid bus type, must be MIPI CSI2\n");
+		return -EINVAL;
+	}
+	of_node_put(endpoint);
+
+	/* get system clock (xclk) frequency */
+	ret = of_property_read_u32(dev->of_node, "xclk", &xclk);
+	if (!ret) {
+		if (xclk < OV5640_XCLK_MIN || xclk > OV5640_XCLK_MAX) {
+			dev_err(dev, "invalid xclk frequency\n");
+			return -EINVAL;
+		}
+		sensor->xclk_freq = xclk;
+	}
+
+	/* get system clock (xclk) */
+	sensor->xclk = devm_clk_get(dev, "xclk");
+	if (!IS_ERR(sensor->xclk)) {
+		if (!sensor->xclk_freq) {
+			dev_err(dev, "xclk requires xclk frequency!\n");
+			return -EINVAL;
+		}
+		clk_set_rate(sensor->xclk, sensor->xclk_freq);
+	} else {
+		/* assume system clock enabled by default */
+		sensor->xclk = NULL;
+	}
+
+	/* request power down pin */
+	sensor->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_HIGH);
+	if (IS_ERR(sensor->pwdn_gpio)) {
+		dev_err(dev, "request for power down gpio failed\n");
+		return PTR_ERR(sensor->pwdn_gpio);
+	}
+
+	/* request reset pin */
+	sensor->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(sensor->reset_gpio)) {
+		dev_err(dev, "request for reset gpio failed\n");
+		return PTR_ERR(sensor->reset_gpio);
+	}
+
+	/* initialize the cached controls to their defaults */
+	for (i = 0; i < OV5640_NUM_CONTROLS; i++) {
+		struct ov5640_control *c = &ov5640_ctrls[i];
+
+		sensor->ctrl_cache[i] = c->ctrl.default_value;
+	}
+	sensor->awb_on = sensor->agc_on = true;
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
+
+	ov5640_get_regulators(sensor);
+
+	ret = ov5640_s_power(&sensor->sd, 1);
+	if (ret)
+		goto reg_off;
+	ret = ov5640_init_controls(sensor);
+	if (ret)
+		goto reg_off;
+	ret = ov5640_s_power(&sensor->sd, 0);
+	if (ret)
+		goto free_ctrls;
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret)
+		goto free_ctrls;
+
+	return 0;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+reg_off:
+	ov5640_regulators_off(sensor);
+	return ret;
+}
+
+/*!
+ * ov5640 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  Error code indicating success or failure
+ */
+static int ov5640_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov5640_dev *sensor = to_ov5640_dev(sd);
+
+	ov5640_regulators_off(sensor);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov5640_id[] = {
+	{"ov5640_mipi", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, ov5640_id);
+
+static const struct of_device_id ov5640_dt_ids[] = {
+	{ .compatible = "ovti,ov5640_mipi" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov5640_dt_ids);
+
+static struct i2c_driver ov5640_i2c_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = "ov5640_mipi",
+		.of_match_table	= ov5640_dt_ids,
+	},
+	.id_table = ov5640_id,
+	.probe    = ov5640_probe,
+	.remove   = ov5640_remove,
+};
+
+module_i2c_driver(ov5640_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5640 MIPI Camera Subdev Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
-- 
1.9.1


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

* [PATCH 23/28] media: imx: Add support for Parallel OV5642
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (21 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 22/28] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 24/28] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
                     ` (4 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

This driver is based on ov5642.c from Freescale imx_3.10.17_1.0.0_beta
branch, modified heavily for code cleanup and converted from int-device
to subdev.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 drivers/staging/media/imx/capture/Kconfig  |    7 +
 drivers/staging/media/imx/capture/Makefile |    1 +
 drivers/staging/media/imx/capture/ov5642.c | 4309 ++++++++++++++++++++++++++++
 3 files changed, 4317 insertions(+)
 create mode 100644 drivers/staging/media/imx/capture/ov5642.c

diff --git a/drivers/staging/media/imx/capture/Kconfig b/drivers/staging/media/imx/capture/Kconfig
index 13d7231..92a091a 100644
--- a/drivers/staging/media/imx/capture/Kconfig
+++ b/drivers/staging/media/imx/capture/Kconfig
@@ -17,6 +17,13 @@ config IMX_VIDEO_SWITCH
 	  multiplexer controlled by register bitfields as well as
 	  external multiplexers controller by a GPIO.
 
+config IMX_CAMERA_OV5642
+       tristate "OmniVision OV5642 Parallel camera support"
+       depends on GPIOLIB && VIDEO_IMX_CAMERA
+       default y
+       ---help---
+         Parallel interface OV5642 camera support.
+
 config IMX_CAMERA_OV5640_MIPI
        tristate "OmniVision OV5640 MIPI CSI-2 camera support"
        depends on GPIOLIB && VIDEO_IMX_CAMERA
diff --git a/drivers/staging/media/imx/capture/Makefile b/drivers/staging/media/imx/capture/Makefile
index 1ad4fd2..07633be 100644
--- a/drivers/staging/media/imx/capture/Makefile
+++ b/drivers/staging/media/imx/capture/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_VIDEO_IMX_CAMERA) += imx-csi.o
 obj-$(CONFIG_IMX_MIPI_CSI2) += mipi-csi2.o
 obj-$(CONFIG_IMX_VIDEO_SWITCH) += imx-video-switch.o
 obj-$(CONFIG_IMX_CAMERA_OV5640_MIPI) += ov5640-mipi.o
+obj-$(CONFIG_IMX_CAMERA_OV5642) += ov5642.o
diff --git a/drivers/staging/media/imx/capture/ov5642.c b/drivers/staging/media/imx/capture/ov5642.c
new file mode 100644
index 0000000..a0e6ccf
--- /dev/null
+++ b/drivers/staging/media/imx/capture/ov5642.c
@@ -0,0 +1,4309 @@
+/*
+ * Copyright (c) 2012-2014 Mentor Graphics Inc.
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-of.h>
+#include <media/v4l2-ctrls.h>
+
+#define OV5642_VOLTAGE_ANALOG               2800000
+#define OV5642_VOLTAGE_DIGITAL_CORE         1500000
+#define OV5642_VOLTAGE_DIGITAL_IO           1800000
+
+#define MIN_FPS 15
+#define MAX_FPS 30
+#define DEFAULT_FPS 30
+
+/* min/typical/max system clock (xclk) frequencies */
+#define OV5642_XCLK_MIN  6000000
+#define OV5642_XCLK_TYP 24000000
+#define OV5642_XCLK_MAX 54000000
+
+/* min/typical/max pixel clock (mclk) frequencies */
+#define OV5642_MCLK_MIN 48000000
+#define OV5642_MCLK_TYP 48000000
+#define OV5642_MCLK_MAX 96000000
+
+#define OV5642_CHIP_ID  0x300A
+
+#define OV5642_MAX_CONTROLS 64
+
+enum ov5642_mode {
+	ov5642_mode_MIN = 0,
+	ov5642_mode_QCIF_176_144 = 0,
+	ov5642_mode_QVGA_320_240,
+	ov5642_mode_VGA_640_480,
+	ov5642_mode_NTSC_720_480,
+	ov5642_mode_PAL_720_576,
+	ov5642_mode_XGA_1024_768,
+	ov5642_mode_720P_1280_720,
+	ov5642_mode_1080P_1920_1080,
+	ov5642_mode_QSXGA_2592_1944,
+	ov5642_num_modes,
+};
+
+enum ov5642_frame_rate {
+	ov5642_15_fps,
+	ov5642_30_fps
+};
+
+static int ov5642_framerates[] = {
+	[ov5642_15_fps] = 15,
+	[ov5642_30_fps] = 30,
+};
+#define ov5642_num_framerates ARRAY_SIZE(ov5642_framerates)
+
+struct reg_value {
+	u16 reg_addr;
+	u8 val;
+	u8 mask;
+	u32 delay_ms;
+};
+
+struct ov5642_mode_info {
+	enum ov5642_mode mode;
+	u32 width;
+	u32 height;
+	struct reg_value *init_data_ptr;
+	u32 init_data_size;
+};
+
+struct ov5642_dev {
+	struct i2c_client *i2c_client;
+	struct device *dev;
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler ctrl_hdl;
+	struct v4l2_of_endpoint ep; /* the parsed DT endpoint info */
+	struct v4l2_mbus_framefmt fmt;
+	struct v4l2_captureparm streamcap;
+	struct clk *xclk; /* system clock to OV5642 */
+	int xclk_freq;    /* requested xclk freq from devicetree */
+
+	enum ov5642_mode current_mode;
+	enum ov5642_frame_rate current_fr;
+
+	bool on;
+	bool awb_on;
+	bool agc_on;
+
+	/* cached control settings */
+	int ctrl_cache[OV5642_MAX_CONTROLS];
+
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *pwdn_gpio;
+	struct gpio_desc *gp_gpio;
+
+	struct regulator *io_regulator;
+	struct regulator *core_regulator;
+	struct regulator *analog_regulator;
+	struct regulator *gpo_regulator;
+};
+
+static inline struct ov5642_dev *to_ov5642_dev(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5642_dev, sd);
+}
+
+static inline struct ov5642_dev *ctrl_to_ov5642_dev(struct v4l2_ctrl *ctrl)
+{
+	return container_of(ctrl->handler, struct ov5642_dev, ctrl_hdl);
+}
+
+struct ov5642_control {
+	struct v4l2_queryctrl ctrl;
+	int (*set)(struct ov5642_dev *sensor, int value);
+};
+
+static void ov5642_power(struct ov5642_dev *sensor, bool enable);
+static int ov5642_restore_ctrls(struct ov5642_dev *sensor);
+static int ov5642_set_agc(struct ov5642_dev *sensor, int value);
+static int ov5642_set_exposure(struct ov5642_dev *sensor, int value);
+static int ov5642_set_gain(struct ov5642_dev *sensor, int value);
+
+#if 0
+/* not used, but keep around */
+static struct reg_value ov5642_rotate_none_VGA[] = {
+	{0x3818, 0xc1, 0x00, 0x00}, {0x3621, 0x87, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_vert_flip_VGA[] = {
+	{0x3818, 0x20, 0xbf, 0x00}, {0x3621, 0x20, 0xff, 0x00},
+};
+static struct reg_value ov5642_rotate_horiz_flip_VGA[] = {
+	{0x3818, 0x81, 0x00, 0x01}, {0x3621, 0xa7, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_180_VGA[] = {
+	{0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00},
+};
+static struct reg_value ov5642_rotate_none_FULL[] = {
+	{0x3818, 0xc0, 0x00, 0x00}, {0x3621, 0x09, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_vert_flip_FULL[] = {
+	{0x3818, 0x20, 0xbf, 0x01}, {0x3621, 0x20, 0xff, 0x00},
+};
+static struct reg_value ov5642_rotate_horiz_flip_FULL[] = {
+	{0x3818, 0x80, 0x00, 0x01}, {0x3621, 0x29, 0x00, 0x00},
+};
+static struct reg_value ov5642_rotate_180_FULL[] = {
+	{0x3818, 0x60, 0xff, 0x00}, {0x3621, 0x00, 0xdf, 0x00},
+};
+#endif
+
+static struct reg_value ov5642_initial_setting[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c00, 0x04, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0},
+	{0x5182, 0x00, 0, 0}, {0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5001, 0xff, 0, 0}, {0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0},
+	{0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0},
+	{0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0},
+	{0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0},
+	{0x380b, 0xe0, 0, 0}, {0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0},
+	{0x501f, 0x00, 0, 0}, {0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0},
+	{0x350b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x0b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 200},
+};
+
+static struct reg_value ov5642_setting_15fps_QCIF_176_144[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_QCIF_176_144[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x10, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0x90, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_QSXGA_2592_1944[] = {
+	{0x3503, 0x07, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0},
+	{0x3002, 0x00, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0},
+	{0x3005, 0xff, 0, 0}, {0x3006, 0xff, 0, 0}, {0x3007, 0x3f, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3818, 0xc0, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3602, 0xe4, 0, 0}, {0x3612, 0xac, 0, 0}, {0x3613, 0x44, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3623, 0x22, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3705, 0xda, 0, 0}, {0x370a, 0x80, 0, 0}, {0x3801, 0x95, 0, 0},
+	{0x3803, 0x0e, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x20, 0, 0},
+	{0x3806, 0x07, 0, 0}, {0x3807, 0x98, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3815, 0x44, 0, 0},
+	{0x3824, 0x11, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x5682, 0x0a, 0, 0}, {0x5683, 0x20, 0, 0}, {0x5686, 0x07, 0, 0},
+	{0x5687, 0x98, 0, 0}, {0x5001, 0xff, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x4407, 0x04, 0, 0}, {0x3008, 0x02, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4713, 0x03, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x3815, 0x01, 0, 0},
+	{0x501f, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0},
+	{0x5002, 0xe0, 0, 0}, {0x530a, 0x01, 0, 0}, {0x530d, 0x10, 0, 0},
+	{0x530c, 0x04, 0, 0}, {0x5312, 0x20, 0, 0}, {0x5282, 0x01, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x3012, 0x00, 0, 0},
+};
+
+
+static struct reg_value ov5642_setting_VGA_2_QVGA[] = {
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x3815, 0x04, 0, 0},
+};
+
+static struct reg_value ov5642_setting_QSXGA_2_VGA[] = {
+	{0x3503, 0x00, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0},
+	{0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0},
+	{0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x3818, 0xc1, 0, 0}, {0x3621, 0x87, 0, 0},
+	{0x350c, 0x03, 0, 0}, {0x350d, 0xe8, 0, 0}, {0x3602, 0xfc, 0, 0},
+	{0x3612, 0xff, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3622, 0x60, 0, 0},
+	{0x3623, 0x01, 0, 0}, {0x3604, 0x48, 0, 0}, {0x3705, 0xdb, 0, 0},
+	{0x370a, 0x81, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0},
+	{0x3807, 0xc0, 0, 0}, {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0},
+	{0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3810, 0x40, 0, 0}, {0x3815, 0x04, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3825, 0xb4, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0},
+	{0x5001, 0xff, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x4407, 0x0c, 0, 0}, {0x3008, 0x02, 0, 0}, {0x460b, 0x37, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x471d, 0x05, 0, 0}, {0x4713, 0x02, 0, 0},
+	{0x471c, 0xd0, 0, 0}, {0x3815, 0x04, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x3002, 0x5c, 0, 0}, {0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0},
+	{0x530a, 0x01, 0, 0}, {0x530d, 0x0c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x5312, 0x40, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x3012, 0x02, 0, 0}, {0x3010, 0x00, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_VGA_640_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_QVGA_320_240[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3808, 0x01, 0, 0},
+	{0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, {0x380b, 0xf0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_NTSC_720_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x302c, 0x60, 0x60, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_PAL_720_576[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, {0x380b, 0x40, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xd8, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x3c, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x5682, 0x04, 0, 0},
+	{0x5683, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0x58, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0x58, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x302c, 0x60, 0x60, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_720P_1280_720[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+	{0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+	{0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+	{0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+	{0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+	{0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+	{0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+	{0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+	{0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+	{0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+	{0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0},
+	{0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0},
+	{0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0},
+	{0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0},
+	{0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0},
+	{0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0},
+	{0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0},
+	{0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0},
+	{0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0},
+	{0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_1080P_1920_1080[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+	{0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+	{0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+	{0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+	{0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+	{0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+	{0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+	{0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+	{0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+	{0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+	{0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x07, 0, 0},
+	{0x350c, 0x04, 0, 0}, {0x350d, 0x58, 0, 0}, {0x3801, 0x8a, 0, 0},
+	{0x3803, 0x0a, 0, 0}, {0x3804, 0x07, 0, 0}, {0x3805, 0x80, 0, 0},
+	{0x3806, 0x04, 0, 0}, {0x3807, 0x39, 0, 0}, {0x3808, 0x07, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0},
+	{0x380c, 0x09, 0, 0}, {0x380d, 0xd6, 0, 0}, {0x380e, 0x04, 0, 0},
+	{0x380f, 0x58, 0, 0}, {0x381c, 0x11, 0, 0}, {0x381d, 0xba, 0, 0},
+	{0x381e, 0x04, 0, 0}, {0x381f, 0x48, 0, 0}, {0x3820, 0x04, 0, 0},
+	{0x3821, 0x18, 0, 0}, {0x3a08, 0x14, 0, 0}, {0x3a09, 0xe0, 0, 0},
+	{0x3a0a, 0x11, 0, 0}, {0x3a0b, 0x60, 0, 0}, {0x3a0d, 0x04, 0, 0},
+	{0x3a0e, 0x03, 0, 0}, {0x5682, 0x07, 0, 0}, {0x5683, 0x60, 0, 0},
+	{0x5686, 0x04, 0, 0}, {0x5687, 0x1c, 0, 0}, {0x5001, 0x7f, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0},
+	{0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0}, {0x501f, 0x00, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3819, 0x80, 0, 0},
+	{0x5002, 0xe0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_VGA_640_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_QVGA_320_240[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0},
+	{0x380b, 0xf0, 0, 0}, {0x3a00, 0x78, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_NTSC_720_480[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3824, 0x11, 0, 0}, {0x3825, 0xb4, 0, 0}, {0x3826, 0x00, 0, 0},
+	{0x3827, 0x3d, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380A, 0x01, 0, 0}, {0x380B, 0xe0, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x3806, 0x03, 0, 0},
+	{0x3807, 0x55, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0x55, 0, 0},
+	{0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_PAL_720_576[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0},
+	{0x3006, 0x43, 0, 0}, {0x3007, 0x37, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3501, 0x1e, 0, 0},
+	{0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0}, {0x380c, 0x0c, 0, 0},
+	{0x380d, 0x80, 0, 0}, {0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0},
+	{0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3818, 0xc1, 0, 0},
+	{0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0}, {0x3801, 0x80, 0, 0},
+	{0x3621, 0x87, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3803, 0x08, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x3810, 0x40, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0},
+	{0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0},
+	{0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a1a, 0x05, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0},
+	{0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0}, {0x3502, 0x00, 0, 0},
+	{0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0}, {0x3503, 0x00, 0, 0},
+	{0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0}, {0x528f, 0x10, 0, 0},
+	{0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x02, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x02, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x02, 0, 0},
+	{0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3c, 0, 0},
+	{0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0}, {0x3a03, 0x7d, 0, 0},
+	{0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0}, {0x3a15, 0x7d, 0, 0},
+	{0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0}, {0x3a08, 0x09, 0, 0},
+	{0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0}, {0x3a0b, 0xd0, 0, 0},
+	{0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x06, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0}, {0x401e, 0x20, 0, 0},
+	{0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0}, {0x528a, 0x01, 0, 0},
+	{0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0}, {0x528d, 0x10, 0, 0},
+	{0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0}, {0x5290, 0x30, 0, 0},
+	{0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0}, {0x5294, 0x00, 0, 0},
+	{0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0}, {0x5297, 0x08, 0, 0},
+	{0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0}, {0x529a, 0x00, 0, 0},
+	{0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0}, {0x529d, 0x28, 0, 0},
+	{0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0}, {0x5282, 0x00, 0, 0},
+	{0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0}, {0x5302, 0x00, 0, 0},
+	{0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0}, {0x530d, 0x0c, 0, 0},
+	{0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0}, {0x5310, 0x20, 0, 0},
+	{0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0}, {0x5309, 0x40, 0, 0},
+	{0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0}, {0x5306, 0x00, 0, 0},
+	{0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0}, {0x5315, 0x20, 0, 0},
+	{0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0}, {0x5317, 0x00, 0, 0},
+	{0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0}, {0x5381, 0x00, 0, 0},
+	{0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0}, {0x5384, 0x00, 0, 0},
+	{0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0}, {0x5387, 0x00, 0, 0},
+	{0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0}, {0x538a, 0x00, 0, 0},
+	{0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0}, {0x538d, 0x00, 0, 0},
+	{0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0}, {0x5390, 0x00, 0, 0},
+	{0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0}, {0x5393, 0xa2, 0, 0},
+	{0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0}, {0x5481, 0x21, 0, 0},
+	{0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0}, {0x5484, 0x65, 0, 0},
+	{0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0}, {0x5487, 0x87, 0, 0},
+	{0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0}, {0x548a, 0xaa, 0, 0},
+	{0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0}, {0x548d, 0xdd, 0, 0},
+	{0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0}, {0x5490, 0x05, 0, 0},
+	{0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0}, {0x5493, 0x20, 0, 0},
+	{0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0}, {0x5496, 0x02, 0, 0},
+	{0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0}, {0x5499, 0x86, 0, 0},
+	{0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0}, {0x549c, 0x02, 0, 0},
+	{0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0}, {0x549f, 0x1c, 0, 0},
+	{0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0}, {0x54a2, 0x01, 0, 0},
+	{0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0}, {0x54a5, 0xc5, 0, 0},
+	{0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0}, {0x54a8, 0x01, 0, 0},
+	{0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0}, {0x54ab, 0x41, 0, 0},
+	{0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0}, {0x54ae, 0x00, 0, 0},
+	{0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0}, {0x54b1, 0x20, 0, 0},
+	{0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0}, {0x54b4, 0x00, 0, 0},
+	{0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0}, {0x54b7, 0xdf, 0, 0},
+	{0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0}, {0x3406, 0x00, 0, 0},
+	{0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0}, {0x5182, 0x11, 0, 0},
+	{0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0},
+	{0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0}, {0x5188, 0x08, 0, 0},
+	{0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0}, {0x518b, 0xb2, 0, 0},
+	{0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0}, {0x518e, 0x3d, 0, 0},
+	{0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0}, {0x5191, 0xf8, 0, 0},
+	{0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x5194, 0xf0, 0, 0},
+	{0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0}, {0x5197, 0x01, 0, 0},
+	{0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0}, {0x519a, 0x04, 0, 0},
+	{0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0}, {0x519d, 0x82, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0}, {0x3a0f, 0x38, 0, 0},
+	{0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0}, {0x3a1e, 0x2e, 0, 0},
+	{0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0}, {0x5688, 0xa6, 0, 0},
+	{0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0}, {0x568b, 0xae, 0, 0},
+	{0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0}, {0x568e, 0x62, 0, 0},
+	{0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0}, {0x5584, 0x40, 0, 0},
+	{0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0}, {0x5800, 0x27, 0, 0},
+	{0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0}, {0x5803, 0x0f, 0, 0},
+	{0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0}, {0x5806, 0x1e, 0, 0},
+	{0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0}, {0x5809, 0x0d, 0, 0},
+	{0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0}, {0x580c, 0x0a, 0, 0},
+	{0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0}, {0x580f, 0x19, 0, 0},
+	{0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0}, {0x5812, 0x04, 0, 0},
+	{0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0}, {0x5815, 0x06, 0, 0},
+	{0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0}, {0x5818, 0x0a, 0, 0},
+	{0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0}, {0x581b, 0x00, 0, 0},
+	{0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0}, {0x581e, 0x08, 0, 0},
+	{0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0}, {0x5821, 0x05, 0, 0},
+	{0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0}, {0x5824, 0x00, 0, 0},
+	{0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0}, {0x5827, 0x0c, 0, 0},
+	{0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0}, {0x582a, 0x06, 0, 0},
+	{0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0}, {0x582d, 0x07, 0, 0},
+	{0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0}, {0x5830, 0x18, 0, 0},
+	{0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0}, {0x5833, 0x0a, 0, 0},
+	{0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0}, {0x5836, 0x15, 0, 0},
+	{0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0}, {0x5839, 0x1f, 0, 0},
+	{0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0}, {0x583c, 0x17, 0, 0},
+	{0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0}, {0x583f, 0x53, 0, 0},
+	{0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0}, {0x5842, 0x0d, 0, 0},
+	{0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0}, {0x5845, 0x09, 0, 0},
+	{0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0}, {0x5848, 0x10, 0, 0},
+	{0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0}, {0x584b, 0x0e, 0, 0},
+	{0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0}, {0x584e, 0x11, 0, 0},
+	{0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0}, {0x5851, 0x0c, 0, 0},
+	{0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0}, {0x5854, 0x10, 0, 0},
+	{0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0}, {0x5857, 0x0b, 0, 0},
+	{0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0}, {0x585a, 0x0d, 0, 0},
+	{0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0}, {0x585d, 0x0c, 0, 0},
+	{0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0}, {0x5860, 0x0c, 0, 0},
+	{0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0}, {0x5863, 0x08, 0, 0},
+	{0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0}, {0x5866, 0x18, 0, 0},
+	{0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0}, {0x5869, 0x19, 0, 0},
+	{0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0}, {0x586c, 0x13, 0, 0},
+	{0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0}, {0x586f, 0x16, 0, 0},
+	{0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0}, {0x5872, 0x10, 0, 0},
+	{0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0}, {0x5875, 0x16, 0, 0},
+	{0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0}, {0x5878, 0x10, 0, 0},
+	{0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0}, {0x587b, 0x14, 0, 0},
+	{0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0}, {0x587e, 0x11, 0, 0},
+	{0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0}, {0x5881, 0x15, 0, 0},
+	{0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0}, {0x5884, 0x15, 0, 0},
+	{0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0}, {0x5887, 0x17, 0, 0},
+	{0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0}, {0x3702, 0x10, 0, 0},
+	{0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0}, {0x370b, 0x40, 0, 0},
+	{0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0}, {0x3632, 0x52, 0, 0},
+	{0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0}, {0x5785, 0x07, 0, 0},
+	{0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0}, {0x3604, 0x48, 0, 0},
+	{0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0}, {0x370f, 0xc0, 0, 0},
+	{0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0}, {0x5007, 0x00, 0, 0},
+	{0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0}, {0x5013, 0x00, 0, 0},
+	{0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0}, {0x5087, 0x00, 0, 0},
+	{0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0}, {0x302b, 0x00, 0, 0},
+	{0x3824, 0x11, 0, 0}, {0x3825, 0xdc, 0, 0}, {0x3826, 0x00, 0, 0},
+	{0x3827, 0x08, 0, 0}, {0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0},
+	{0x380e, 0x03, 0, 0}, {0x380f, 0xe8, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0xd0, 0, 0}, {0x380A, 0x02, 0, 0}, {0x380B, 0x40, 0, 0},
+	{0x3804, 0x04, 0, 0}, {0x3805, 0xb0, 0, 0}, {0x3806, 0x03, 0, 0},
+	{0x3807, 0xc0, 0, 0}, {0x5686, 0x03, 0, 0}, {0x5687, 0xc0, 0, 0},
+	{0x5682, 0x04, 0, 0}, {0x5683, 0xb0, 0, 0},
+};
+
+static struct reg_value ov5642_setting_30fps_XGA_1024_768[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x00, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+	{0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0}, {0x302c, 0x60, 0x60, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_XGA_1024_768[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3615, 0xf0, 0, 0}, {0x3000, 0x00, 0, 0},
+	{0x3001, 0x00, 0, 0}, {0x3002, 0x5c, 0, 0}, {0x3003, 0x00, 0, 0},
+	{0x3004, 0xff, 0, 0}, {0x3005, 0xff, 0, 0}, {0x3006, 0x43, 0, 0},
+	{0x3007, 0x37, 0, 0}, {0x3011, 0x09, 0, 0}, {0x3012, 0x02, 0, 0},
+	{0x3010, 0x10, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3815, 0x04, 0, 0},
+	{0x370c, 0xa0, 0, 0}, {0x3602, 0xfc, 0, 0}, {0x3612, 0xff, 0, 0},
+	{0x3634, 0xc0, 0, 0}, {0x3613, 0x00, 0, 0}, {0x3605, 0x7c, 0, 0},
+	{0x3621, 0x09, 0, 0}, {0x3622, 0x60, 0, 0}, {0x3604, 0x40, 0, 0},
+	{0x3603, 0xa7, 0, 0}, {0x3603, 0x27, 0, 0}, {0x4000, 0x21, 0, 0},
+	{0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0}, {0x3605, 0x04, 0, 0},
+	{0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5001, 0xff, 0, 0},
+	{0x5500, 0x0a, 0, 0}, {0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0},
+	{0x5080, 0x08, 0, 0}, {0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x4708, 0x06, 0, 0}, {0x3808, 0x02, 0, 0},
+	{0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, {0x380b, 0xe0, 0, 0},
+	{0x380e, 0x07, 0, 0}, {0x380f, 0xd0, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x07, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3825, 0xb0, 0, 0},
+	{0x3501, 0x1e, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x7f, 0, 0},
+	{0x380c, 0x07, 0, 0}, {0x380d, 0x2a, 0, 0}, {0x380e, 0x03, 0, 0},
+	{0x380f, 0xe8, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x3818, 0xc1, 0, 0}, {0x3705, 0xdb, 0, 0}, {0x370a, 0x81, 0, 0},
+	{0x3801, 0x80, 0, 0}, {0x3621, 0xc7, 0, 0}, {0x3801, 0x50, 0, 0},
+	{0x3803, 0x08, 0, 0}, {0x3827, 0x08, 0, 0}, {0x3810, 0x80, 0, 0},
+	{0x3804, 0x05, 0, 0}, {0x3805, 0x00, 0, 0}, {0x5682, 0x05, 0, 0},
+	{0x5683, 0x00, 0, 0}, {0x3806, 0x03, 0, 0}, {0x3807, 0xc0, 0, 0},
+	{0x5686, 0x03, 0, 0}, {0x5687, 0xbc, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a1a, 0x05, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3a18, 0x00, 0, 0},
+	{0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0},
+	{0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0}, {0x350c, 0x07, 0, 0},
+	{0x350d, 0xd0, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x528a, 0x02, 0, 0}, {0x528b, 0x04, 0, 0},
+	{0x528c, 0x08, 0, 0}, {0x528d, 0x08, 0, 0}, {0x528e, 0x08, 0, 0},
+	{0x528f, 0x10, 0, 0}, {0x5290, 0x10, 0, 0}, {0x5292, 0x00, 0, 0},
+	{0x5293, 0x02, 0, 0}, {0x5294, 0x00, 0, 0}, {0x5295, 0x02, 0, 0},
+	{0x5296, 0x00, 0, 0}, {0x5297, 0x02, 0, 0}, {0x5298, 0x00, 0, 0},
+	{0x5299, 0x02, 0, 0}, {0x529a, 0x00, 0, 0}, {0x529b, 0x02, 0, 0},
+	{0x529c, 0x00, 0, 0}, {0x529d, 0x02, 0, 0}, {0x529e, 0x00, 0, 0},
+	{0x529f, 0x02, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x30, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x30, 0, 0}, {0x3a11, 0x70, 0, 0},
+	{0x3a1f, 0x10, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x12, 0, 0}, {0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0},
+	{0x3a0b, 0xa0, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a0e, 0x03, 0, 0},
+	{0x5193, 0x70, 0, 0}, {0x589b, 0x04, 0, 0}, {0x589a, 0xc5, 0, 0},
+	{0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3621, 0x87, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0},
+	{0x380b, 0x00, 0, 0}, {0x3815, 0x02, 0, 0},
+};
+
+static struct reg_value ov5642_setting_15fps_720P_1280_720[] = {
+	{0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0},
+	{0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0},
+	{0x3000, 0x00, 0, 0}, {0x3001, 0x00, 0, 0}, {0x3002, 0x00, 0, 0},
+	{0x3003, 0x00, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3030, 0x2b, 0, 0},
+	{0x3011, 0x08, 0, 0}, {0x3010, 0x10, 0, 0}, {0x3604, 0x60, 0, 0},
+	{0x3622, 0x60, 0, 0}, {0x3621, 0x09, 0, 0}, {0x3709, 0x00, 0, 0},
+	{0x4000, 0x21, 0, 0}, {0x401d, 0x22, 0, 0}, {0x3600, 0x54, 0, 0},
+	{0x3605, 0x04, 0, 0}, {0x3606, 0x3f, 0, 0}, {0x3c01, 0x80, 0, 0},
+	{0x300d, 0x22, 0, 0}, {0x3623, 0x22, 0, 0}, {0x5000, 0x4f, 0, 0},
+	{0x5020, 0x04, 0, 0}, {0x5181, 0x79, 0, 0}, {0x5182, 0x00, 0, 0},
+	{0x5185, 0x22, 0, 0}, {0x5197, 0x01, 0, 0}, {0x5500, 0x0a, 0, 0},
+	{0x5504, 0x00, 0, 0}, {0x5505, 0x7f, 0, 0}, {0x5080, 0x08, 0, 0},
+	{0x300e, 0x18, 0, 0}, {0x4610, 0x00, 0, 0}, {0x471d, 0x05, 0, 0},
+	{0x4708, 0x06, 0, 0}, {0x370c, 0xa0, 0, 0}, {0x3808, 0x0a, 0, 0},
+	{0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, {0x380b, 0x98, 0, 0},
+	{0x380c, 0x0c, 0, 0}, {0x380d, 0x80, 0, 0}, {0x380e, 0x07, 0, 0},
+	{0x380f, 0xd0, 0, 0}, {0x5687, 0x94, 0, 0}, {0x501f, 0x00, 0, 0},
+	{0x5000, 0x4f, 0, 0}, {0x5001, 0xcf, 0, 0}, {0x4300, 0x30, 0, 0},
+	{0x4300, 0x30, 0, 0}, {0x460b, 0x35, 0, 0}, {0x471d, 0x00, 0, 0},
+	{0x3002, 0x0c, 0, 0}, {0x3002, 0x00, 0, 0}, {0x4713, 0x03, 0, 0},
+	{0x471c, 0x50, 0, 0}, {0x4721, 0x02, 0, 0}, {0x4402, 0x90, 0, 0},
+	{0x460c, 0x22, 0, 0}, {0x3815, 0x44, 0, 0}, {0x3503, 0x07, 0, 0},
+	{0x3501, 0x73, 0, 0}, {0x3502, 0x80, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3818, 0xc8, 0, 0}, {0x3801, 0x88, 0, 0}, {0x3824, 0x11, 0, 0},
+	{0x3a00, 0x78, 0, 0}, {0x3a1a, 0x04, 0, 0}, {0x3a13, 0x30, 0, 0},
+	{0x3a18, 0x00, 0, 0}, {0x3a19, 0x7c, 0, 0}, {0x3a08, 0x12, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x0f, 0, 0}, {0x3a0b, 0xa0, 0, 0},
+	{0x350c, 0x07, 0, 0}, {0x350d, 0xd0, 0, 0}, {0x3a0d, 0x08, 0, 0},
+	{0x3a0e, 0x06, 0, 0}, {0x3500, 0x00, 0, 0}, {0x3501, 0x00, 0, 0},
+	{0x3502, 0x00, 0, 0}, {0x350a, 0x00, 0, 0}, {0x350b, 0x00, 0, 0},
+	{0x3503, 0x00, 0, 0}, {0x3a0f, 0x3c, 0, 0}, {0x3a10, 0x32, 0, 0},
+	{0x3a1b, 0x3c, 0, 0}, {0x3a1e, 0x32, 0, 0}, {0x3a11, 0x80, 0, 0},
+	{0x3a1f, 0x20, 0, 0}, {0x3030, 0x2b, 0, 0}, {0x3a02, 0x00, 0, 0},
+	{0x3a03, 0x7d, 0, 0}, {0x3a04, 0x00, 0, 0}, {0x3a14, 0x00, 0, 0},
+	{0x3a15, 0x7d, 0, 0}, {0x3a16, 0x00, 0, 0}, {0x3a00, 0x78, 0, 0},
+	{0x3a08, 0x09, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x07, 0, 0},
+	{0x3a0b, 0xd0, 0, 0}, {0x3a0d, 0x10, 0, 0}, {0x3a0e, 0x0d, 0, 0},
+	{0x4407, 0x04, 0, 0}, {0x5193, 0x70, 0, 0}, {0x589b, 0x00, 0, 0},
+	{0x589a, 0xc0, 0, 0}, {0x401e, 0x20, 0, 0}, {0x4001, 0x42, 0, 0},
+	{0x401c, 0x06, 0, 0}, {0x3825, 0xac, 0, 0}, {0x3827, 0x0c, 0, 0},
+	{0x528a, 0x01, 0, 0}, {0x528b, 0x04, 0, 0}, {0x528c, 0x08, 0, 0},
+	{0x528d, 0x10, 0, 0}, {0x528e, 0x20, 0, 0}, {0x528f, 0x28, 0, 0},
+	{0x5290, 0x30, 0, 0}, {0x5292, 0x00, 0, 0}, {0x5293, 0x01, 0, 0},
+	{0x5294, 0x00, 0, 0}, {0x5295, 0x04, 0, 0}, {0x5296, 0x00, 0, 0},
+	{0x5297, 0x08, 0, 0}, {0x5298, 0x00, 0, 0}, {0x5299, 0x10, 0, 0},
+	{0x529a, 0x00, 0, 0}, {0x529b, 0x20, 0, 0}, {0x529c, 0x00, 0, 0},
+	{0x529d, 0x28, 0, 0}, {0x529e, 0x00, 0, 0}, {0x529f, 0x30, 0, 0},
+	{0x5282, 0x00, 0, 0}, {0x5300, 0x00, 0, 0}, {0x5301, 0x20, 0, 0},
+	{0x5302, 0x00, 0, 0}, {0x5303, 0x7c, 0, 0}, {0x530c, 0x00, 0, 0},
+	{0x530d, 0x0c, 0, 0}, {0x530e, 0x20, 0, 0}, {0x530f, 0x80, 0, 0},
+	{0x5310, 0x20, 0, 0}, {0x5311, 0x80, 0, 0}, {0x5308, 0x20, 0, 0},
+	{0x5309, 0x40, 0, 0}, {0x5304, 0x00, 0, 0}, {0x5305, 0x30, 0, 0},
+	{0x5306, 0x00, 0, 0}, {0x5307, 0x80, 0, 0}, {0x5314, 0x08, 0, 0},
+	{0x5315, 0x20, 0, 0}, {0x5319, 0x30, 0, 0}, {0x5316, 0x10, 0, 0},
+	{0x5317, 0x00, 0, 0}, {0x5318, 0x02, 0, 0}, {0x5380, 0x01, 0, 0},
+	{0x5381, 0x00, 0, 0}, {0x5382, 0x00, 0, 0}, {0x5383, 0x4e, 0, 0},
+	{0x5384, 0x00, 0, 0}, {0x5385, 0x0f, 0, 0}, {0x5386, 0x00, 0, 0},
+	{0x5387, 0x00, 0, 0}, {0x5388, 0x01, 0, 0}, {0x5389, 0x15, 0, 0},
+	{0x538a, 0x00, 0, 0}, {0x538b, 0x31, 0, 0}, {0x538c, 0x00, 0, 0},
+	{0x538d, 0x00, 0, 0}, {0x538e, 0x00, 0, 0}, {0x538f, 0x0f, 0, 0},
+	{0x5390, 0x00, 0, 0}, {0x5391, 0xab, 0, 0}, {0x5392, 0x00, 0, 0},
+	{0x5393, 0xa2, 0, 0}, {0x5394, 0x08, 0, 0}, {0x5480, 0x14, 0, 0},
+	{0x5481, 0x21, 0, 0}, {0x5482, 0x36, 0, 0}, {0x5483, 0x57, 0, 0},
+	{0x5484, 0x65, 0, 0}, {0x5485, 0x71, 0, 0}, {0x5486, 0x7d, 0, 0},
+	{0x5487, 0x87, 0, 0}, {0x5488, 0x91, 0, 0}, {0x5489, 0x9a, 0, 0},
+	{0x548a, 0xaa, 0, 0}, {0x548b, 0xb8, 0, 0}, {0x548c, 0xcd, 0, 0},
+	{0x548d, 0xdd, 0, 0}, {0x548e, 0xea, 0, 0}, {0x548f, 0x1d, 0, 0},
+	{0x5490, 0x05, 0, 0}, {0x5491, 0x00, 0, 0}, {0x5492, 0x04, 0, 0},
+	{0x5493, 0x20, 0, 0}, {0x5494, 0x03, 0, 0}, {0x5495, 0x60, 0, 0},
+	{0x5496, 0x02, 0, 0}, {0x5497, 0xb8, 0, 0}, {0x5498, 0x02, 0, 0},
+	{0x5499, 0x86, 0, 0}, {0x549a, 0x02, 0, 0}, {0x549b, 0x5b, 0, 0},
+	{0x549c, 0x02, 0, 0}, {0x549d, 0x3b, 0, 0}, {0x549e, 0x02, 0, 0},
+	{0x549f, 0x1c, 0, 0}, {0x54a0, 0x02, 0, 0}, {0x54a1, 0x04, 0, 0},
+	{0x54a2, 0x01, 0, 0}, {0x54a3, 0xed, 0, 0}, {0x54a4, 0x01, 0, 0},
+	{0x54a5, 0xc5, 0, 0}, {0x54a6, 0x01, 0, 0}, {0x54a7, 0xa5, 0, 0},
+	{0x54a8, 0x01, 0, 0}, {0x54a9, 0x6c, 0, 0}, {0x54aa, 0x01, 0, 0},
+	{0x54ab, 0x41, 0, 0}, {0x54ac, 0x01, 0, 0}, {0x54ad, 0x20, 0, 0},
+	{0x54ae, 0x00, 0, 0}, {0x54af, 0x16, 0, 0}, {0x54b0, 0x01, 0, 0},
+	{0x54b1, 0x20, 0, 0}, {0x54b2, 0x00, 0, 0}, {0x54b3, 0x10, 0, 0},
+	{0x54b4, 0x00, 0, 0}, {0x54b5, 0xf0, 0, 0}, {0x54b6, 0x00, 0, 0},
+	{0x54b7, 0xdf, 0, 0}, {0x5402, 0x3f, 0, 0}, {0x5403, 0x00, 0, 0},
+	{0x3406, 0x00, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0x52, 0, 0},
+	{0x5182, 0x11, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0},
+	{0x5185, 0x24, 0, 0}, {0x5186, 0x06, 0, 0}, {0x5187, 0x08, 0, 0},
+	{0x5188, 0x08, 0, 0}, {0x5189, 0x7c, 0, 0}, {0x518a, 0x60, 0, 0},
+	{0x518b, 0xb2, 0, 0}, {0x518c, 0xb2, 0, 0}, {0x518d, 0x44, 0, 0},
+	{0x518e, 0x3d, 0, 0}, {0x518f, 0x58, 0, 0}, {0x5190, 0x46, 0, 0},
+	{0x5191, 0xf8, 0, 0}, {0x5192, 0x04, 0, 0}, {0x5193, 0x70, 0, 0},
+	{0x5194, 0xf0, 0, 0}, {0x5195, 0xf0, 0, 0}, {0x5196, 0x03, 0, 0},
+	{0x5197, 0x01, 0, 0}, {0x5198, 0x04, 0, 0}, {0x5199, 0x12, 0, 0},
+	{0x519a, 0x04, 0, 0}, {0x519b, 0x00, 0, 0}, {0x519c, 0x06, 0, 0},
+	{0x519d, 0x82, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5025, 0x80, 0, 0},
+	{0x3a0f, 0x38, 0, 0}, {0x3a10, 0x30, 0, 0}, {0x3a1b, 0x3a, 0, 0},
+	{0x3a1e, 0x2e, 0, 0}, {0x3a11, 0x60, 0, 0}, {0x3a1f, 0x10, 0, 0},
+	{0x5688, 0xa6, 0, 0}, {0x5689, 0x6a, 0, 0}, {0x568a, 0xea, 0, 0},
+	{0x568b, 0xae, 0, 0}, {0x568c, 0xa6, 0, 0}, {0x568d, 0x6a, 0, 0},
+	{0x568e, 0x62, 0, 0}, {0x568f, 0x26, 0, 0}, {0x5583, 0x40, 0, 0},
+	{0x5584, 0x40, 0, 0}, {0x5580, 0x02, 0, 0}, {0x5000, 0xcf, 0, 0},
+	{0x5800, 0x27, 0, 0}, {0x5801, 0x19, 0, 0}, {0x5802, 0x12, 0, 0},
+	{0x5803, 0x0f, 0, 0}, {0x5804, 0x10, 0, 0}, {0x5805, 0x15, 0, 0},
+	{0x5806, 0x1e, 0, 0}, {0x5807, 0x2f, 0, 0}, {0x5808, 0x15, 0, 0},
+	{0x5809, 0x0d, 0, 0}, {0x580a, 0x0a, 0, 0}, {0x580b, 0x09, 0, 0},
+	{0x580c, 0x0a, 0, 0}, {0x580d, 0x0c, 0, 0}, {0x580e, 0x12, 0, 0},
+	{0x580f, 0x19, 0, 0}, {0x5810, 0x0b, 0, 0}, {0x5811, 0x07, 0, 0},
+	{0x5812, 0x04, 0, 0}, {0x5813, 0x03, 0, 0}, {0x5814, 0x03, 0, 0},
+	{0x5815, 0x06, 0, 0}, {0x5816, 0x0a, 0, 0}, {0x5817, 0x0f, 0, 0},
+	{0x5818, 0x0a, 0, 0}, {0x5819, 0x05, 0, 0}, {0x581a, 0x01, 0, 0},
+	{0x581b, 0x00, 0, 0}, {0x581c, 0x00, 0, 0}, {0x581d, 0x03, 0, 0},
+	{0x581e, 0x08, 0, 0}, {0x581f, 0x0c, 0, 0}, {0x5820, 0x0a, 0, 0},
+	{0x5821, 0x05, 0, 0}, {0x5822, 0x01, 0, 0}, {0x5823, 0x00, 0, 0},
+	{0x5824, 0x00, 0, 0}, {0x5825, 0x03, 0, 0}, {0x5826, 0x08, 0, 0},
+	{0x5827, 0x0c, 0, 0}, {0x5828, 0x0e, 0, 0}, {0x5829, 0x08, 0, 0},
+	{0x582a, 0x06, 0, 0}, {0x582b, 0x04, 0, 0}, {0x582c, 0x05, 0, 0},
+	{0x582d, 0x07, 0, 0}, {0x582e, 0x0b, 0, 0}, {0x582f, 0x12, 0, 0},
+	{0x5830, 0x18, 0, 0}, {0x5831, 0x10, 0, 0}, {0x5832, 0x0c, 0, 0},
+	{0x5833, 0x0a, 0, 0}, {0x5834, 0x0b, 0, 0}, {0x5835, 0x0e, 0, 0},
+	{0x5836, 0x15, 0, 0}, {0x5837, 0x19, 0, 0}, {0x5838, 0x32, 0, 0},
+	{0x5839, 0x1f, 0, 0}, {0x583a, 0x18, 0, 0}, {0x583b, 0x16, 0, 0},
+	{0x583c, 0x17, 0, 0}, {0x583d, 0x1e, 0, 0}, {0x583e, 0x26, 0, 0},
+	{0x583f, 0x53, 0, 0}, {0x5840, 0x10, 0, 0}, {0x5841, 0x0f, 0, 0},
+	{0x5842, 0x0d, 0, 0}, {0x5843, 0x0c, 0, 0}, {0x5844, 0x0e, 0, 0},
+	{0x5845, 0x09, 0, 0}, {0x5846, 0x11, 0, 0}, {0x5847, 0x10, 0, 0},
+	{0x5848, 0x10, 0, 0}, {0x5849, 0x10, 0, 0}, {0x584a, 0x10, 0, 0},
+	{0x584b, 0x0e, 0, 0}, {0x584c, 0x10, 0, 0}, {0x584d, 0x10, 0, 0},
+	{0x584e, 0x11, 0, 0}, {0x584f, 0x10, 0, 0}, {0x5850, 0x0f, 0, 0},
+	{0x5851, 0x0c, 0, 0}, {0x5852, 0x0f, 0, 0}, {0x5853, 0x10, 0, 0},
+	{0x5854, 0x10, 0, 0}, {0x5855, 0x0f, 0, 0}, {0x5856, 0x0e, 0, 0},
+	{0x5857, 0x0b, 0, 0}, {0x5858, 0x10, 0, 0}, {0x5859, 0x0d, 0, 0},
+	{0x585a, 0x0d, 0, 0}, {0x585b, 0x0c, 0, 0}, {0x585c, 0x0c, 0, 0},
+	{0x585d, 0x0c, 0, 0}, {0x585e, 0x0b, 0, 0}, {0x585f, 0x0c, 0, 0},
+	{0x5860, 0x0c, 0, 0}, {0x5861, 0x0c, 0, 0}, {0x5862, 0x0d, 0, 0},
+	{0x5863, 0x08, 0, 0}, {0x5864, 0x11, 0, 0}, {0x5865, 0x18, 0, 0},
+	{0x5866, 0x18, 0, 0}, {0x5867, 0x19, 0, 0}, {0x5868, 0x17, 0, 0},
+	{0x5869, 0x19, 0, 0}, {0x586a, 0x16, 0, 0}, {0x586b, 0x13, 0, 0},
+	{0x586c, 0x13, 0, 0}, {0x586d, 0x12, 0, 0}, {0x586e, 0x13, 0, 0},
+	{0x586f, 0x16, 0, 0}, {0x5870, 0x14, 0, 0}, {0x5871, 0x12, 0, 0},
+	{0x5872, 0x10, 0, 0}, {0x5873, 0x11, 0, 0}, {0x5874, 0x11, 0, 0},
+	{0x5875, 0x16, 0, 0}, {0x5876, 0x14, 0, 0}, {0x5877, 0x11, 0, 0},
+	{0x5878, 0x10, 0, 0}, {0x5879, 0x0f, 0, 0}, {0x587a, 0x10, 0, 0},
+	{0x587b, 0x14, 0, 0}, {0x587c, 0x13, 0, 0}, {0x587d, 0x12, 0, 0},
+	{0x587e, 0x11, 0, 0}, {0x587f, 0x11, 0, 0}, {0x5880, 0x12, 0, 0},
+	{0x5881, 0x15, 0, 0}, {0x5882, 0x14, 0, 0}, {0x5883, 0x15, 0, 0},
+	{0x5884, 0x15, 0, 0}, {0x5885, 0x15, 0, 0}, {0x5886, 0x13, 0, 0},
+	{0x5887, 0x17, 0, 0}, {0x3710, 0x10, 0, 0}, {0x3632, 0x51, 0, 0},
+	{0x3702, 0x10, 0, 0}, {0x3703, 0xb2, 0, 0}, {0x3704, 0x18, 0, 0},
+	{0x370b, 0x40, 0, 0}, {0x370d, 0x03, 0, 0}, {0x3631, 0x01, 0, 0},
+	{0x3632, 0x52, 0, 0}, {0x3606, 0x24, 0, 0}, {0x3620, 0x96, 0, 0},
+	{0x5785, 0x07, 0, 0}, {0x3a13, 0x30, 0, 0}, {0x3600, 0x52, 0, 0},
+	{0x3604, 0x48, 0, 0}, {0x3606, 0x1b, 0, 0}, {0x370d, 0x0b, 0, 0},
+	{0x370f, 0xc0, 0, 0}, {0x3709, 0x01, 0, 0}, {0x3823, 0x00, 0, 0},
+	{0x5007, 0x00, 0, 0}, {0x5009, 0x00, 0, 0}, {0x5011, 0x00, 0, 0},
+	{0x5013, 0x00, 0, 0}, {0x519e, 0x00, 0, 0}, {0x5086, 0x00, 0, 0},
+	{0x5087, 0x00, 0, 0}, {0x5088, 0x00, 0, 0}, {0x5089, 0x00, 0, 0},
+	{0x302b, 0x00, 0, 0}, {0x3503, 0x07, 0, 0}, {0x3011, 0x08, 0, 0},
+	{0x350c, 0x02, 0, 0}, {0x350d, 0xe4, 0, 0}, {0x3621, 0xc9, 0, 0},
+	{0x370a, 0x81, 0, 0}, {0x3803, 0x08, 0, 0}, {0x3804, 0x05, 0, 0},
+	{0x3805, 0x00, 0, 0}, {0x3806, 0x02, 0, 0}, {0x3807, 0xd0, 0, 0},
+	{0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0},
+	{0x380b, 0xd0, 0, 0}, {0x380c, 0x08, 0, 0}, {0x380d, 0x72, 0, 0},
+	{0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0xc0, 0, 0},
+	{0x3818, 0xc9, 0, 0}, {0x381c, 0x10, 0, 0}, {0x381d, 0xa0, 0, 0},
+	{0x381e, 0x05, 0, 0}, {0x381f, 0xb0, 0, 0}, {0x3820, 0x00, 0, 0},
+	{0x3821, 0x00, 0, 0}, {0x3824, 0x11, 0, 0}, {0x3a08, 0x1b, 0, 0},
+	{0x3a09, 0xc0, 0, 0}, {0x3a0a, 0x17, 0, 0}, {0x3a0b, 0x20, 0, 0},
+	{0x3a0d, 0x02, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x401c, 0x04, 0, 0},
+	{0x5682, 0x05, 0, 0}, {0x5683, 0x00, 0, 0}, {0x5686, 0x02, 0, 0},
+	{0x5687, 0xcc, 0, 0}, {0x5001, 0x7f, 0, 0}, {0x589b, 0x06, 0, 0},
+	{0x589a, 0xc5, 0, 0}, {0x3503, 0x00, 0, 0}, {0x3010, 0x10, 0, 0},
+	{0x460c, 0x20, 0, 0}, {0x460b, 0x37, 0, 0}, {0x471c, 0xd0, 0, 0},
+	{0x471d, 0x05, 0, 0}, {0x3815, 0x01, 0, 0}, {0x3818, 0x00, 0x08, 0},
+	{0x501f, 0x00, 0, 0}, {0x4300, 0x30, 0, 0}, {0x3002, 0x1c, 0, 0},
+	{0x3819, 0x80, 0, 0}, {0x5002, 0xe0, 0, 0}, {0x3010, 0x30, 0, 0},
+	{0x3a08, 0x06, 0, 0}, {0x3a09, 0x60, 0, 0}, {0x3a0a, 0x05, 0, 0},
+	{0x3a0b, 0x50, 0, 0}, {0x3a0d, 0x08, 0, 0}, {0x3a0e, 0x07, 0, 0},
+};
+
+static struct ov5642_mode_info
+ov5642_mode_info_data[ov5642_num_framerates][ov5642_num_modes] = {
+	{
+		{ov5642_mode_QCIF_176_144, 176, 144,
+		ov5642_setting_15fps_QCIF_176_144,
+		ARRAY_SIZE(ov5642_setting_15fps_QCIF_176_144)},
+		{ov5642_mode_QVGA_320_240,   320,  240,
+		ov5642_setting_15fps_QVGA_320_240,
+		ARRAY_SIZE(ov5642_setting_15fps_QVGA_320_240)},
+		{ov5642_mode_VGA_640_480,    640,  480,
+		ov5642_setting_15fps_VGA_640_480,
+		ARRAY_SIZE(ov5642_setting_15fps_VGA_640_480)},
+		{ov5642_mode_NTSC_720_480,   720,  480,
+		ov5642_setting_15fps_NTSC_720_480,
+		ARRAY_SIZE(ov5642_setting_15fps_NTSC_720_480)},
+		{ov5642_mode_PAL_720_576,   720,  576,
+		ov5642_setting_15fps_PAL_720_576,
+		ARRAY_SIZE(ov5642_setting_15fps_PAL_720_576)},
+		{ov5642_mode_XGA_1024_768, 1024, 768,
+		ov5642_setting_15fps_XGA_1024_768,
+		ARRAY_SIZE(ov5642_setting_15fps_XGA_1024_768)},
+		{ov5642_mode_720P_1280_720,  1280, 720,
+		ov5642_setting_15fps_720P_1280_720,
+		ARRAY_SIZE(ov5642_setting_15fps_720P_1280_720)},
+		{ov5642_mode_1080P_1920_1080, 1920, 1080,
+		ov5642_setting_15fps_1080P_1920_1080,
+		ARRAY_SIZE(ov5642_setting_15fps_1080P_1920_1080)},
+		{ov5642_mode_QSXGA_2592_1944, 2592, 1944,
+		ov5642_setting_15fps_QSXGA_2592_1944,
+		ARRAY_SIZE(ov5642_setting_15fps_QSXGA_2592_1944)},
+	},
+	{
+		{ov5642_mode_QCIF_176_144, 176, 144,
+		ov5642_setting_30fps_QCIF_176_144,
+		ARRAY_SIZE(ov5642_setting_30fps_QCIF_176_144)},
+		{ov5642_mode_QVGA_320_240,   320,  240,
+		ov5642_setting_30fps_QVGA_320_240,
+		ARRAY_SIZE(ov5642_setting_30fps_QVGA_320_240)},
+		{ov5642_mode_VGA_640_480,    640,  480,
+		ov5642_setting_30fps_VGA_640_480,
+		ARRAY_SIZE(ov5642_setting_30fps_VGA_640_480)},
+		{ov5642_mode_NTSC_720_480,   720, 480,
+		ov5642_setting_30fps_NTSC_720_480,
+		ARRAY_SIZE(ov5642_setting_30fps_NTSC_720_480)},
+		{ov5642_mode_PAL_720_576,    720, 576,
+		ov5642_setting_30fps_PAL_720_576,
+		ARRAY_SIZE(ov5642_setting_30fps_PAL_720_576)},
+		{ov5642_mode_XGA_1024_768, 1024, 768,
+		ov5642_setting_30fps_XGA_1024_768,
+		ARRAY_SIZE(ov5642_setting_30fps_XGA_1024_768)},
+		{ov5642_mode_720P_1280_720,  1280, 720,
+		ov5642_setting_30fps_720P_1280_720,
+		ARRAY_SIZE(ov5642_setting_30fps_720P_1280_720)},
+		{ov5642_mode_1080P_1920_1080, 0, 0, NULL, 0},
+		{ov5642_mode_QSXGA_2592_1944, 0, 0, NULL, 0},
+	},
+};
+
+static int ov5642_write_reg(struct ov5642_dev *sensor, u16 reg, u8 val)
+{
+	u8 au8Buf[3] = {0};
+	int ret;
+
+	au8Buf[0] = reg >> 8;
+	au8Buf[1] = reg & 0xff;
+	au8Buf[2] = val;
+
+	ret = i2c_master_send(sensor->i2c_client, au8Buf, 3);
+	if (ret < 0) {
+		dev_err(sensor->dev, "%s:write reg error:reg=%x,val=%x\n",
+			__func__, reg, val);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ov5642_read_reg(struct ov5642_dev *sensor, u16 reg, u8 *val)
+{
+	u8 au8RegBuf[2] = {0};
+	u8 u8RdVal = 0;
+
+	au8RegBuf[0] = reg >> 8;
+	au8RegBuf[1] = reg & 0xff;
+
+	if (2 != i2c_master_send(sensor->i2c_client, au8RegBuf, 2)) {
+		dev_err(sensor->dev, "%s:write reg error:reg=%x\n",
+			__func__, reg);
+		return -EIO;
+	}
+
+	if (1 != i2c_master_recv(sensor->i2c_client, &u8RdVal, 1)) {
+		dev_err(sensor->dev, "%s:read reg error:reg=%x,val=%x\n",
+			__func__, reg, u8RdVal);
+		return -EIO;
+	}
+
+	*val = u8RdVal;
+	return 0;
+}
+
+#define OV5642_READ_REG(s, r, v) {				\
+		ret = ov5642_read_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+#define OV5642_WRITE_REG(s, r, v) {				\
+		ret = ov5642_write_reg((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5642_read_reg16(struct ov5642_dev *sensor, u16 reg, u16 *val)
+{
+	u8 hi, lo;
+	int ret;
+
+	OV5642_READ_REG(sensor, reg, &hi);
+	OV5642_READ_REG(sensor, reg+1, &lo);
+
+	*val = ((u16)hi << 8) | (u16)lo;
+	return 0;
+}
+
+#define OV5642_READ_REG16(s, r, v) {				\
+		ret = ov5642_read_reg16((s), (r), (v));		\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5642_mod_reg(struct ov5642_dev *sensor, u16 reg,
+			  u8 mask, u8 val)
+{
+	u8 readval;
+	int ret;
+
+	OV5642_READ_REG(sensor, reg, &readval);
+
+	readval &= ~mask;
+	val &= mask;
+	val |= readval;
+
+	OV5642_WRITE_REG(sensor, reg, val);
+	return 0;
+}
+
+#define OV5642_MOD_REG(s, r, m, v) {				\
+		ret = ov5642_mod_reg((s), (r), (m), (v));	\
+		if (ret)					\
+			return ret;				\
+	}
+
+static int ov5642_load_regs(struct ov5642_dev *sensor,
+			    struct reg_value *regs,
+			    int size)
+{
+	register u32 delay_ms = 0;
+	register u16 reg_addr = 0;
+	register u8 mask = 0;
+	register u8 val = 0;
+	int i, ret = 0;
+
+	for (i = 0; i < size; ++i, ++regs) {
+		delay_ms = regs->delay_ms;
+		reg_addr = regs->reg_addr;
+		val = regs->val;
+		mask = regs->mask;
+
+		if (mask) {
+			OV5642_MOD_REG(sensor, reg_addr, mask, val);
+		} else {
+			OV5642_WRITE_REG(sensor, reg_addr, val);
+		}
+		if (delay_ms)
+			usleep_range(1000*delay_ms, 1000*delay_ms+100);
+	}
+
+	return 0;
+}
+
+static int ov5642_dump_format(struct ov5642_dev *sensor)
+{
+	u8 scaling, l_pix, r_pix, pix_fmt;
+	u16 hs, vs, hw, vh, dvp_w, dvp_h;
+	int ret;
+
+	OV5642_READ_REG16(sensor, 0x3800, &hs);
+	OV5642_READ_REG16(sensor, 0x3802, &vs);
+	OV5642_READ_REG16(sensor, 0x3804, &hw);
+	OV5642_READ_REG16(sensor, 0x3806, &vh);
+
+	OV5642_READ_REG(sensor, 0x5001, &scaling);
+	OV5642_READ_REG16(sensor, 0x3808, &dvp_w);
+	OV5642_READ_REG16(sensor, 0x380a, &dvp_h);
+
+	OV5642_READ_REG(sensor, 0x4711, &l_pix);
+	OV5642_READ_REG(sensor, 0x4712, &r_pix);
+	OV5642_READ_REG(sensor, 0x4300, &pix_fmt);
+
+	dev_dbg(sensor->dev, "Image Window:\n");
+	dev_dbg(sensor->dev, "\thoriz: %u@%u\n", hw, hs);
+	dev_dbg(sensor->dev, "\tvert : %u@%u\n", vh, vs);
+	dev_dbg(sensor->dev, "DVP Scaling:\n");
+	dev_dbg(sensor->dev, "\thoriz %s, vert %s, %ux%u\n",
+		(scaling & (1 << 4)) ? "enabled" : "disabled",
+		(scaling & (1 << 5)) ? "enabled" : "disabled",
+		dvp_w, dvp_h);
+	dev_dbg(sensor->dev, "DVP Padding:\n");
+	dev_dbg(sensor->dev, "\tleft %u pixels, right %u pixels\n",
+		l_pix, r_pix);
+	dev_dbg(sensor->dev, "Pixel Format: %02x\n", pix_fmt);
+
+	return 0;
+}
+
+static int ov5642_init_mode(struct ov5642_dev *sensor,
+			    enum ov5642_frame_rate frame_rate,
+			    enum ov5642_mode mode);
+static int ov5642_write_snapshot_para(struct ov5642_dev *sensor,
+				      enum ov5642_frame_rate frame_rate,
+				      enum ov5642_mode mode);
+
+static enum ov5642_mode
+ov5642_find_nearest_mode(struct ov5642_dev *sensor,
+			 int width, int height)
+{
+	int i;
+
+	for (i = ov5642_num_modes - 1; i >= 0; i--) {
+		if (ov5642_mode_info_data[0][i].width <= width &&
+		    ov5642_mode_info_data[0][i].height <= height)
+			break;
+	}
+
+	if (i < 0)
+		i = 0;
+
+	return (enum ov5642_mode)i;
+}
+
+static int ov5642_change_mode(struct ov5642_dev *sensor,
+			      enum ov5642_frame_rate new_frame_rate,
+			      enum ov5642_frame_rate old_frame_rate,
+			      enum ov5642_mode new_mode,
+			      enum ov5642_mode orig_mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+	int ret = 0;
+
+	if (new_mode >= ov5642_num_modes || new_mode < ov5642_mode_MIN) {
+		dev_err(sensor->dev, "Wrong ov5642 mode detected!\n");
+		return -EINVAL;
+	}
+
+	if (new_frame_rate == old_frame_rate) {
+		if (new_mode == orig_mode)
+			return 0;
+		else if (new_mode == ov5642_mode_VGA_640_480 &&
+			 orig_mode == ov5642_mode_QSXGA_2592_1944) {
+			mode_data = ov5642_setting_QSXGA_2_VGA;
+			mode_size = ARRAY_SIZE(ov5642_setting_QSXGA_2_VGA);
+			sensor->fmt.width = 640;
+			sensor->fmt.height = 480;
+		} else if (new_mode == ov5642_mode_QVGA_320_240 &&
+			   orig_mode == ov5642_mode_VGA_640_480) {
+			mode_data = ov5642_setting_VGA_2_QVGA;
+			mode_size = ARRAY_SIZE(ov5642_setting_VGA_2_QVGA);
+			sensor->fmt.width = 320;
+			sensor->fmt.height = 240;
+		} else
+			goto load_full;
+
+		ret = ov5642_load_regs(sensor, mode_data, mode_size);
+	} else {
+load_full:
+		ret = ov5642_write_snapshot_para(sensor, new_frame_rate,
+						    new_mode);
+	}
+
+	if (ret)
+		return ret;
+
+	/* restore controls */
+	ov5642_restore_ctrls(sensor);
+
+	if (ret >= 0) {
+		sensor->current_mode = new_mode;
+		sensor->current_fr = new_frame_rate;
+
+		ov5642_dump_format(sensor);
+	}
+
+	return ret;
+}
+
+static int ov5642_restore_mode(struct ov5642_dev *sensor)
+{
+	int ret = 0;
+
+	/* first we need to set some initial register values */
+	ret = ov5642_load_regs(sensor, ov5642_initial_setting,
+				  ARRAY_SIZE(ov5642_initial_setting));
+	if (ret < 0)
+		return ret;
+
+	/* now restore the last capture mode */
+	return ov5642_change_mode(sensor,
+				  sensor->current_fr,
+				  sensor->current_fr,
+				  sensor->current_mode,
+				  ov5642_mode_VGA_640_480);
+}
+
+static int ov5642_init_mode(struct ov5642_dev *sensor,
+			    enum ov5642_frame_rate frame_rate,
+			    enum ov5642_mode mode)
+{
+	struct reg_value *mode_data = NULL;
+	int mode_size = 0;
+
+	if (mode >= ov5642_num_modes || mode < ov5642_mode_MIN) {
+		dev_err(sensor->dev, "Wrong ov5642 mode detected!\n");
+		return -EINVAL;
+	}
+
+	mode_data = ov5642_mode_info_data[frame_rate][mode].init_data_ptr;
+	mode_size = ov5642_mode_info_data[frame_rate][mode].init_data_size;
+
+	sensor->fmt.width = ov5642_mode_info_data[frame_rate][mode].width;
+	sensor->fmt.height = ov5642_mode_info_data[frame_rate][mode].height;
+
+	if (sensor->fmt.width == 0 || sensor->fmt.height == 0 ||
+	    mode_data == NULL || mode_size == 0)
+		return -EINVAL;
+
+	return ov5642_load_regs(sensor, mode_data, mode_size);
+}
+
+static int ov5642_write_snapshot_para(struct ov5642_dev *sensor,
+				      enum ov5642_frame_rate frame_rate,
+				      enum ov5642_mode mode)
+{
+	bool m_60Hz = false;
+	u16 capture_frame_rate = 50;
+	u16 g_preview_frame_rate = 225;
+	u8 ret_l, ret_m, ret_h, gain, lines_10ms;
+	u16 ulcapture_exposure, capture_maxlines;
+	u16 icapture_gain, preview_maxlines;
+	u32 ulcapture_exposure_gain, g_preview_exposure;
+	int ret;
+
+	ov5642_set_agc(sensor, 0);
+
+	ret_h = ret_m = ret_l = 0;
+	g_preview_exposure = 0;
+	OV5642_READ_REG(sensor, 0x3500, &ret_h);
+	OV5642_READ_REG(sensor, 0x3501, &ret_m);
+	OV5642_READ_REG(sensor, 0x3502, &ret_l);
+	g_preview_exposure = (ret_h << 12) + (ret_m << 4) + (ret_l >> 4);
+
+	OV5642_READ_REG16(sensor, 0x380e, &preview_maxlines);
+	/*Read back AGC Gain for preview*/
+	gain = 0;
+	OV5642_READ_REG(sensor, 0x350b, &gain);
+
+	ret = ov5642_init_mode(sensor, frame_rate, mode);
+	if (ret < 0)
+		return ret;
+
+	OV5642_READ_REG16(sensor, 0x380e, &capture_maxlines);
+	if (m_60Hz)
+		lines_10ms = capture_frame_rate * (u32)capture_maxlines/12000;
+	else
+		lines_10ms = capture_frame_rate * (u32)capture_maxlines/10000;
+
+	if (preview_maxlines == 0)
+		preview_maxlines = 1;
+
+	ulcapture_exposure = (g_preview_exposure * capture_frame_rate *
+			      (u32)capture_maxlines) /
+		(preview_maxlines * g_preview_frame_rate);
+	icapture_gain = (gain & 0x0f) + 16;
+	if (gain & 0x10)
+		icapture_gain = icapture_gain << 1;
+
+	if (gain & 0x20)
+		icapture_gain = icapture_gain << 1;
+
+	if (gain & 0x40)
+		icapture_gain = icapture_gain << 1;
+
+	if (gain & 0x80)
+		icapture_gain = icapture_gain << 1;
+
+	ulcapture_exposure_gain = 2 * ulcapture_exposure * icapture_gain;
+
+	if (ulcapture_exposure_gain < (u32)capture_maxlines*16) {
+		ulcapture_exposure = ulcapture_exposure_gain/16;
+		if (ulcapture_exposure > lines_10ms) {
+			ulcapture_exposure /= lines_10ms;
+			ulcapture_exposure *= lines_10ms;
+		}
+	} else
+		ulcapture_exposure = (u32)capture_maxlines;
+
+	if (ulcapture_exposure == 0)
+		ulcapture_exposure = 1;
+
+	icapture_gain = (ulcapture_exposure_gain*2/ulcapture_exposure + 1)/2;
+	gain = 0;
+	if (icapture_gain > 31) {
+		gain |= 0x10;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 31) {
+		gain |= 0x20;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 31) {
+		gain |= 0x40;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 31) {
+		gain |= 0x80;
+		icapture_gain = icapture_gain >> 1;
+	}
+	if (icapture_gain > 16)
+		gain |= ((icapture_gain - 16) & 0x0f);
+
+	if (gain == 0x10)
+		gain = 0x11;
+
+	ov5642_set_gain(sensor, gain);
+	ov5642_set_exposure(sensor, ulcapture_exposure);
+
+	return 0;
+}
+
+static int ov5642_regulators_on(struct ov5642_dev *sensor)
+{
+	int ret;
+
+	if (sensor->io_regulator) {
+		ret = regulator_enable(sensor->io_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "io reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->core_regulator) {
+		ret = regulator_enable(sensor->core_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "core reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->gpo_regulator) {
+		ret = regulator_enable(sensor->gpo_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "gpo reg enable failed\n");
+			return ret;
+		}
+	}
+	if (sensor->analog_regulator) {
+		ret = regulator_enable(sensor->analog_regulator);
+		if (ret) {
+			v4l2_err(&sensor->sd, "analog reg enable failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void ov5642_regulators_off(struct ov5642_dev *sensor)
+{
+	if (sensor->analog_regulator)
+		regulator_disable(sensor->analog_regulator);
+	if (sensor->core_regulator)
+		regulator_disable(sensor->core_regulator);
+	if (sensor->io_regulator)
+		regulator_disable(sensor->io_regulator);
+	if (sensor->gpo_regulator)
+		regulator_disable(sensor->gpo_regulator);
+}
+
+/* --------------- Subdev Operations --------------- */
+
+static int ov5642_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	int ret;
+
+	if (on && !sensor->on) {
+		if (sensor->xclk)
+			clk_prepare_enable(sensor->xclk);
+
+		ret = ov5642_regulators_on(sensor);
+		if (ret)
+			return ret;
+
+		ov5642_power(sensor, true);
+		ret = ov5642_restore_mode(sensor);
+		if (ret)
+			return ret;
+	} else if (!on && sensor->on) {
+		ov5642_power(sensor, false);
+
+		ov5642_regulators_off(sensor);
+
+		if (sensor->xclk)
+			clk_disable_unprepare(sensor->xclk);
+	}
+
+	sensor->on = on;
+
+	return 0;
+}
+
+static int ov5642_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	struct v4l2_captureparm *cparm = &a->parm.capture;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	memset(a, 0, sizeof(*a));
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	cparm->capability = sensor->streamcap.capability;
+	cparm->timeperframe = sensor->streamcap.timeperframe;
+	cparm->capturemode = sensor->streamcap.capturemode;
+
+	return 0;
+}
+
+static int ov5642_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+	enum ov5642_frame_rate new_frame_rate;
+	u32 tgt_fps;
+	int ret;
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	/* Check that the new frame rate is allowed. */
+	if ((timeperframe->numerator == 0) ||
+	    (timeperframe->denominator == 0)) {
+		timeperframe->denominator = DEFAULT_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps > MAX_FPS) {
+		timeperframe->denominator = MAX_FPS;
+		timeperframe->numerator = 1;
+	} else if (tgt_fps < MIN_FPS) {
+		timeperframe->denominator = MIN_FPS;
+		timeperframe->numerator = 1;
+	}
+
+	/* Actual frame rate we use */
+	tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+	if (tgt_fps == 15)
+		new_frame_rate = ov5642_15_fps;
+	else if (tgt_fps == 30)
+		new_frame_rate = ov5642_30_fps;
+	else {
+		dev_err(sensor->dev, "frame rate is not supported!\n");
+		return -EINVAL;
+	}
+
+	ret = ov5642_change_mode(
+		sensor, new_frame_rate, sensor->current_fr,
+		sensor->current_mode, sensor->current_mode);
+	if (ret < 0)
+		return ret;
+
+	sensor->streamcap.timeperframe = *timeperframe;
+
+	return 0;
+}
+
+static int ov5642_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	if (format->pad)
+		return -EINVAL;
+
+	format->format = sensor->fmt;
+
+	return 0;
+}
+
+static int ov5642_try_fmt_internal(struct v4l2_subdev *sd,
+				   struct v4l2_mbus_framefmt *fmt,
+				   enum ov5642_mode *new_mode)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	enum ov5642_mode mode;
+
+	mode = ov5642_find_nearest_mode(sensor, fmt->width, fmt->height);
+
+	fmt->width = ov5642_mode_info_data[0][mode].width;
+	fmt->height = ov5642_mode_info_data[0][mode].height;
+	fmt->code = sensor->fmt.code;
+
+	if (new_mode)
+		*new_mode = mode;
+	return 0;
+}
+
+static int ov5642_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *format)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	enum ov5642_mode new_mode;
+	int ret;
+
+	if (format->pad)
+		return -EINVAL;
+
+	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+		ret = ov5642_try_fmt_internal(sd, &format->format, NULL);
+		if (ret)
+			return ret;
+		cfg->try_fmt = format->format;
+		return 0;
+	}
+
+	ret = ov5642_try_fmt_internal(sd, &format->format, &new_mode);
+	if (ret)
+		return ret;
+
+	ret = ov5642_change_mode(sensor,
+				 sensor->current_fr,
+				 sensor->current_fr,
+				 new_mode, sensor->current_mode);
+	if (ret >= 0)
+		sensor->fmt = format->format;
+
+	return ret;
+}
+
+
+/*
+ * Sensor Controls.
+ */
+
+static int ov5642_set_hue(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 0, 1 << 0);
+		OV5642_MOD_REG(sensor, 0x558a, 1 << 6, 1 << 6);
+		OV5642_WRITE_REG(sensor, 0x5581, value & 0xff);
+		OV5642_WRITE_REG(sensor, 0x5582, (value & 0x100) >> 8);
+	} else
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 0, 0);
+	return 0;
+}
+
+static int ov5642_set_contrast(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 2, 1 << 2);
+		OV5642_WRITE_REG(sensor, 0x5589, value & 0xff);
+	} else
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 2, 0);
+	return 0;
+}
+
+static int ov5642_set_saturation(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (value) {
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 1, 1 << 1);
+		OV5642_WRITE_REG(sensor, 0x5583, value & 0xff);
+		OV5642_WRITE_REG(sensor, 0x5584, value & 0xff);
+	} else
+		OV5642_MOD_REG(sensor, 0x5580, 1 << 1, 0);
+	return 0;
+}
+
+static int ov5642_set_awb(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	sensor->awb_on = value ? true : false;
+	OV5642_MOD_REG(sensor, 0x3406, 1 << 0, sensor->awb_on ? 0 : 1);
+	return 0;
+}
+
+static int ov5642_set_red_balance(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x3401, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x3400, (value & 0xf00) >> 8);
+	return 0;
+}
+
+#if 0
+static int ov5642_set_green_balance(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x3403, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x3402, (value & 0xf00) >> 8);
+	return 0;
+}
+#endif
+
+static int ov5642_set_blue_balance(struct ov5642_dev *sensor, int value)
+{
+	int ret = 0;
+
+	if (sensor->awb_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x3405, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x3404, (value & 0xf00) >> 8);
+	return 0;
+}
+
+static int ov5642_set_exposure(struct ov5642_dev *sensor, int value)
+{
+	u16 max_exp = 0;
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5642_READ_REG16(sensor, 0x350c, &max_exp);
+	if (value < max_exp) {
+		u32 exp = value << 4;
+
+		OV5642_WRITE_REG(sensor, 0x3502, exp & 0xff);
+		OV5642_WRITE_REG(sensor, 0x3501, (exp >> 8) & 0xff);
+		OV5642_WRITE_REG(sensor, 0x3500, (exp >> 16) & 0x0f);
+	}
+
+	return 0;
+}
+
+static int ov5642_set_agc(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	/* this enables/disables both AEC and AGC */
+	sensor->agc_on = value ? true : false;
+	OV5642_MOD_REG(sensor, 0x3503, 0x7, sensor->agc_on ? 0 : 0x7);
+	return 0;
+}
+
+static int ov5642_set_gain(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	if (sensor->agc_on)
+		return -EINVAL;
+
+	OV5642_WRITE_REG(sensor, 0x350b, value & 0xff);
+	OV5642_WRITE_REG(sensor, 0x350a, (value & 0x100) >> 8);
+	return 0;
+}
+
+#if 0
+static int ov5642_set_test_pattern(struct ov5642_dev *sensor, int value)
+{
+	int ret;
+
+	OV5642_MOD_REG(sensor, 0x503d, 0xa4, value ? 0xa4 : 0);
+	return 0;
+}
+#endif
+
+static struct ov5642_control ov5642_ctrls[] = {
+	{
+		.set = ov5642_set_agc,
+		.ctrl = {
+			.id = V4L2_CID_AUTOGAIN,
+			.name = "Auto Gain/Exposure Control",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5642_set_exposure,
+		.ctrl = {
+			.id = V4L2_CID_EXPOSURE,
+			.name = "Exposure",
+			.minimum = 0,
+			.maximum = 65535,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_gain,
+		.ctrl = {
+			.id = V4L2_CID_GAIN,
+			.name = "Gain",
+			.minimum = 0,
+			.maximum = 511,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_hue,
+		.ctrl = {
+			.id = V4L2_CID_HUE,
+			.name = "Hue",
+			.minimum = 0,
+			.maximum = 359,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_contrast,
+		.ctrl = {
+			.id = V4L2_CID_CONTRAST,
+			.name = "Contrast",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 0,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_saturation,
+		.ctrl = {
+			.id = V4L2_CID_SATURATION,
+			.name = "Saturation",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 64,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_awb,
+		.ctrl = {
+			.id = V4L2_CID_AUTO_WHITE_BALANCE,
+			.name = "Auto White Balance",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 1,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+		},
+	}, {
+		.set = ov5642_set_red_balance,
+		.ctrl = {
+			.id = V4L2_CID_RED_BALANCE,
+			.name = "Red Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 1024,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	}, {
+		.set = ov5642_set_blue_balance,
+		.ctrl = {
+			.id = V4L2_CID_BLUE_BALANCE,
+			.name = "Blue Balance",
+			.minimum = 0,
+			.maximum = 4095,
+			.step = 1,
+			.default_value = 1024,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+		},
+	},
+};
+#define OV5642_NUM_CONTROLS ARRAY_SIZE(ov5642_ctrls)
+
+static struct ov5642_control *ov5642_get_ctrl(int id, int *index)
+{
+	struct ov5642_control *ret = NULL;
+	int i;
+
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		if (id == ov5642_ctrls[i].ctrl.id) {
+			ret = &ov5642_ctrls[i];
+			break;
+		}
+	}
+
+	if (ret && index)
+		*index = i;
+	return ret;
+}
+
+static int ov5642_restore_ctrls(struct ov5642_dev *sensor)
+{
+	struct ov5642_control *c;
+	int i;
+
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		c = &ov5642_ctrls[i];
+		c->set(sensor, sensor->ctrl_cache[i]);
+	}
+
+	return 0;
+}
+
+static int ov5642_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov5642_dev *sensor = ctrl_to_ov5642_dev(ctrl);
+	struct ov5642_control *c;
+	int ret = 0;
+	int i;
+
+	c = ov5642_get_ctrl(ctrl->id, &i);
+	if (!c)
+		return -EINVAL;
+
+	ret = c->set(sensor, ctrl->val);
+	/* update cached value if no error */
+	if (!ret)
+		sensor->ctrl_cache[i] = ctrl->val;
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5642_ctrl_ops = {
+	.s_ctrl = ov5642_s_ctrl,
+};
+
+static int ov5642_init_controls(struct ov5642_dev *sensor)
+{
+	struct ov5642_control *c;
+	int i;
+
+	v4l2_ctrl_handler_init(&sensor->ctrl_hdl, OV5642_NUM_CONTROLS);
+
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		c = &ov5642_ctrls[i];
+
+		v4l2_ctrl_new_std(&sensor->ctrl_hdl, &ov5642_ctrl_ops,
+				  c->ctrl.id, c->ctrl.minimum, c->ctrl.maximum,
+				  c->ctrl.step, c->ctrl.default_value);
+	}
+
+	sensor->sd.ctrl_handler = &sensor->ctrl_hdl;
+	if (sensor->ctrl_hdl.error) {
+		int err = sensor->ctrl_hdl.error;
+
+		v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+		v4l2_err(&sensor->sd, "%s: error %d\n", __func__, err);
+		return err;
+	}
+	v4l2_ctrl_handler_setup(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static int ov5642_enum_frame_size(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->pad)
+		return -EINVAL;
+	if (fse->index >= ov5642_num_modes)
+		return -EINVAL;
+
+	fse->min_width = fse->max_width =
+		ov5642_mode_info_data[0][fse->index].width;
+	fse->min_height = fse->max_height =
+		ov5642_mode_info_data[0][fse->index].height;
+
+	return 0;
+}
+
+static int ov5642_enum_frame_interval(
+	struct v4l2_subdev *sd,
+	struct v4l2_subdev_pad_config *cfg,
+	struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+	enum ov5642_mode mode;
+
+	if (fie->pad)
+		return -EINVAL;
+	if (fie->index < 0 || fie->index >= ov5642_num_framerates)
+		return -EINVAL;
+
+	if (fie->width == 0 || fie->height == 0)
+		return -EINVAL;
+
+	mode = ov5642_find_nearest_mode(sensor, fie->width, fie->height);
+
+	if (ov5642_mode_info_data[fie->index][mode].init_data_ptr == NULL)
+		return -EINVAL;
+
+	fie->interval.numerator = 1;
+	fie->interval.denominator = ov5642_framerates[fie->index];
+
+	dev_dbg(sensor->dev, "%dx%d: [%d] = %d fps\n",
+		fie->width, fie->height, fie->index, fie->interval.denominator);
+	return 0;
+}
+
+static int ov5642_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	*status = !sensor->on ? V4L2_IN_ST_NO_POWER : 0;
+
+	return 0;
+}
+
+static int ov5642_s_routing(struct v4l2_subdev *sd, u32 input,
+			    u32 output, u32 config)
+{
+	return (input != 0) ? -EINVAL : 0;
+}
+
+static int ov5642_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	if (code->pad)
+		return -EINVAL;
+	if (code->index != 0)
+		return -EINVAL;
+
+	code->code = sensor->fmt.code;
+
+	return 0;
+}
+
+static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
+				struct v4l2_mbus_config *cfg)
+{
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	cfg->type = V4L2_MBUS_PARALLEL;
+	cfg->flags = sensor->ep.bus.parallel.flags;
+
+	return 0;
+}
+
+static int ov5642_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	return 0;
+}
+
+static struct v4l2_subdev_core_ops ov5642_core_ops = {
+	.s_power = ov5642_s_power,
+	.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,
+};
+
+static struct v4l2_subdev_video_ops ov5642_video_ops = {
+	.s_parm = ov5642_s_parm,
+	.g_parm = ov5642_g_parm,
+	.g_input_status = ov5642_g_input_status,
+	.s_routing = ov5642_s_routing,
+	.g_mbus_config  = ov5642_g_mbus_config,
+	.s_stream = ov5642_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5642_pad_ops = {
+	.enum_mbus_code = ov5642_enum_mbus_code,
+	.get_fmt = ov5642_get_fmt,
+	.set_fmt = ov5642_set_fmt,
+	.enum_frame_size = ov5642_enum_frame_size,
+	.enum_frame_interval = ov5642_enum_frame_interval,
+};
+
+static struct v4l2_subdev_ops ov5642_subdev_ops = {
+	.core = &ov5642_core_ops,
+	.video = &ov5642_video_ops,
+	.pad = &ov5642_pad_ops,
+};
+
+static void ov5642_power(struct ov5642_dev *sensor, bool enable)
+{
+	gpiod_set_value(sensor->pwdn_gpio, enable ? 0 : 1);
+}
+
+#if 0
+/* we never need to reset the chip, but keep around */
+static void ov5642_reset(struct ov5642_dev *sensor)
+{
+	gpiod_set_value(sensor->reset_gpio, 1);
+	usleep_range(1000, 2000);
+	gpiod_set_value(sensor->reset_gpio, 0);
+}
+#endif
+
+static void ov5642_get_regulators(struct ov5642_dev *sensor)
+{
+	sensor->io_regulator = devm_regulator_get(sensor->dev, "DOVDD");
+	if (!IS_ERR(sensor->io_regulator)) {
+		regulator_set_voltage(sensor->io_regulator,
+				      OV5642_VOLTAGE_DIGITAL_IO,
+				      OV5642_VOLTAGE_DIGITAL_IO);
+	} else {
+		dev_dbg(sensor->dev, "%s: no io voltage reg found\n",
+			__func__);
+		sensor->io_regulator = NULL;
+	}
+
+	sensor->core_regulator = devm_regulator_get(sensor->dev, "DVDD");
+	if (!IS_ERR(sensor->core_regulator)) {
+		regulator_set_voltage(sensor->core_regulator,
+				      OV5642_VOLTAGE_DIGITAL_CORE,
+				      OV5642_VOLTAGE_DIGITAL_CORE);
+	} else {
+		sensor->core_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no core voltage reg found\n",
+			__func__);
+	}
+
+	sensor->analog_regulator = devm_regulator_get(sensor->dev, "AVDD");
+	if (!IS_ERR(sensor->analog_regulator)) {
+		regulator_set_voltage(sensor->analog_regulator,
+				      OV5642_VOLTAGE_ANALOG,
+				      OV5642_VOLTAGE_ANALOG);
+	} else {
+		sensor->analog_regulator = NULL;
+		dev_dbg(sensor->dev, "%s: no analog voltage reg found\n",
+			__func__);
+	}
+}
+
+/*!
+ * ov5642 I2C probe function
+ *
+ * @param adapter            struct i2c_adapter *
+ * @return  Error code indicating success or failure
+ */
+static int ov5642_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *endpoint;
+	struct ov5642_dev *sensor;
+	int i, xclk, ret;
+
+	sensor = devm_kzalloc(dev, sizeof(struct ov5642_dev),
+			      GFP_KERNEL);
+	if (!sensor)
+		return -ENOMEM;
+
+	sensor->i2c_client = client;
+	sensor->dev = dev;
+	sensor->fmt.code = MEDIA_BUS_FMT_YUYV8_2X8;
+	sensor->fmt.width = 640;
+	sensor->fmt.height = 480;
+	sensor->fmt.field = V4L2_FIELD_NONE;
+	sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY |
+					   V4L2_CAP_TIMEPERFRAME;
+	sensor->streamcap.capturemode = 0;
+	sensor->streamcap.timeperframe.denominator = DEFAULT_FPS;
+	sensor->streamcap.timeperframe.numerator = 1;
+
+	sensor->current_mode = ov5642_mode_VGA_640_480;
+	sensor->current_fr = ov5642_30_fps;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint) {
+		dev_err(dev, "endpoint node not found\n");
+		return -EINVAL;
+	}
+
+	v4l2_of_parse_endpoint(endpoint, &sensor->ep);
+	if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL) {
+		dev_err(dev, "invalid bus type, must be parallel\n");
+		return -EINVAL;
+	}
+	of_node_put(endpoint);
+
+	/* get system clock (xclk) frequency */
+	ret = of_property_read_u32(dev->of_node, "xclk", &xclk);
+	if (!ret) {
+		if (xclk < OV5642_XCLK_MIN || xclk > OV5642_XCLK_MAX) {
+			dev_err(dev, "invalid xclk frequency\n");
+			return -EINVAL;
+		}
+		sensor->xclk_freq = xclk;
+	}
+
+	/* get system clock (xclk) */
+	sensor->xclk = devm_clk_get(dev, "xclk");
+	if (!IS_ERR(sensor->xclk)) {
+		if (!sensor->xclk_freq) {
+			dev_err(dev,
+				"xclk requires xclk frequency!\n");
+			return -EINVAL;
+		}
+		clk_set_rate(sensor->xclk, sensor->xclk_freq);
+	} else
+		sensor->xclk = NULL;
+
+	/* request power down pin */
+	sensor->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_HIGH);
+	if (IS_ERR(sensor->pwdn_gpio)) {
+		dev_err(dev, "request for power down gpio failed\n");
+		return PTR_ERR(sensor->pwdn_gpio);
+	}
+
+	/* request reset pin */
+	sensor->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(sensor->reset_gpio)) {
+		dev_err(dev, "request for reset gpio failed\n");
+		return PTR_ERR(sensor->reset_gpio);
+	}
+
+	/*
+	 * No idea what this "gp" gpio to the sensor is used for,
+	 * but optionally acquire it and set it high.
+	 */
+	sensor->gp_gpio = devm_gpiod_get_optional(dev, "gp", GPIOD_OUT_HIGH);
+	if (!IS_ERR_OR_NULL(sensor->gp_gpio))
+		gpiod_set_value(sensor->gp_gpio, 1);
+
+	/* initialize the cached controls to their defaults */
+	for (i = 0; i < OV5642_NUM_CONTROLS; i++) {
+		struct ov5642_control *c = &ov5642_ctrls[i];
+
+		sensor->ctrl_cache[i] = c->ctrl.default_value;
+	}
+	sensor->awb_on = sensor->agc_on = true;
+
+	v4l2_i2c_subdev_init(&sensor->sd, client, &ov5642_subdev_ops);
+
+	ov5642_get_regulators(sensor);
+
+	ret = ov5642_s_power(&sensor->sd, 1);
+	if (ret)
+		goto reg_off;
+	ret = ov5642_init_controls(sensor);
+	if (ret)
+		goto reg_off;
+	ret = ov5642_s_power(&sensor->sd, 0);
+	if (ret)
+		goto free_ctrls;
+
+	ret = v4l2_async_register_subdev(&sensor->sd);
+	if (ret)
+		goto free_ctrls;
+
+	return 0;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+reg_off:
+	ov5642_regulators_off(sensor);
+	return ret;
+}
+
+/*!
+ * ov5642 I2C detach function
+ *
+ * @param client            struct i2c_client *
+ * @return  Error code indicating success or failure
+ */
+static int ov5642_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov5642_dev *sensor = to_ov5642_dev(sd);
+
+	ov5642_regulators_off(sensor);
+
+	v4l2_async_unregister_subdev(&sensor->sd);
+	v4l2_device_unregister_subdev(sd);
+	v4l2_ctrl_handler_free(&sensor->ctrl_hdl);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov5642_id[] = {
+	{ "ov5642", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ov5642_id);
+
+static const struct of_device_id ov5642_dt_ids[] = {
+	{ .compatible = "ovti,ov5642" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ov5642_dt_ids);
+
+static struct i2c_driver ov5642_driver = {
+	.driver = {
+		.name	= "ov5642",
+		.owner	= THIS_MODULE,
+		.of_match_table	= ov5642_dt_ids,
+	},
+	.id_table	= ov5642_id,
+	.probe		= ov5642_probe,
+	.remove		= ov5642_remove,
+};
+
+module_i2c_driver(ov5642_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("OV5642 Camera Subdev Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
-- 
1.9.1


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

* [PATCH 24/28] media: Add i.MX5/6 mem2mem driver
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (22 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 23/28] media: imx: Add support for Parallel OV5642 Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 25/28] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
                     ` (3 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Adds a V4L2 mem2mem driver for i.MX5/6 SoC. Uses the IPU IC image
converter (post-processor task) to perform scaling, rotation, and
color space conversion.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 Documentation/devicetree/bindings/media/imx.txt |   25 +-
 drivers/staging/media/imx/Kconfig               |   12 +
 drivers/staging/media/imx/Makefile              |    1 +
 drivers/staging/media/imx/m2m/Makefile          |    1 +
 drivers/staging/media/imx/m2m/imx-m2m.c         | 1049 +++++++++++++++++++++++
 5 files changed, 1087 insertions(+), 1 deletion(-)
 create mode 100644 drivers/staging/media/imx/m2m/Makefile
 create mode 100644 drivers/staging/media/imx/m2m/imx-m2m.c

diff --git a/Documentation/devicetree/bindings/media/imx.txt b/Documentation/devicetree/bindings/media/imx.txt
index 5a89b51..67d3817 100644
--- a/Documentation/devicetree/bindings/media/imx.txt
+++ b/Documentation/devicetree/bindings/media/imx.txt
@@ -1,4 +1,27 @@
-Freescale i.MX Video Capture
+Freescale i.MX Video Capture, Mem2Mem
+
+Video Mem2Mem node
+------------------
+
+This is the imx video mem2mem device node. The mem2mem node is an IPU
+client and uses the register-level primitives of the IPU, so it does
+not require reg or interrupt properties. Only a compatible property
+and the ipu phandle is required.
+
+Required properties:
+- compatible	: "fsl,imx-video-mem2mem";
+- ipu           : the ipu phandle;
+
+Example:
+
+/ {
+	ipum2m0: ipum2m@ipu1 {
+		compatible = "fsl,imx-video-mem2mem";
+		ipu = <&ipu1>;
+		status = "okay";
+	};
+};
+
 
 Video Capture node
 ------------------
diff --git a/drivers/staging/media/imx/Kconfig b/drivers/staging/media/imx/Kconfig
index 65e1645..3305daa 100644
--- a/drivers/staging/media/imx/Kconfig
+++ b/drivers/staging/media/imx/Kconfig
@@ -21,3 +21,15 @@ config VIDEO_IMX_CAMERA
 if VIDEO_IMX_CAMERA
 source "drivers/staging/media/imx/capture/Kconfig"
 endif
+
+config VIDEO_IMX_M2M
+	tristate "i.MX5/6 Mem2Mem driver"
+	depends on VIDEO_IMX && VIDEO_DEV
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	default y
+	---help---
+	  Use the IPU IC Post-processor on the i.MX5/6 SoC for mem2mem
+	  processing of buffers. Operations include scaling, rotation,
+	  and color space conversion. The driver implements tiling to
+	  support scaling up to 4096x4096.
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index 7c97629..b9e31d8 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_VIDEO_IMX_CAMERA) += capture/
+obj-$(CONFIG_VIDEO_IMX_M2M) += m2m/
diff --git a/drivers/staging/media/imx/m2m/Makefile b/drivers/staging/media/imx/m2m/Makefile
new file mode 100644
index 0000000..287d258
--- /dev/null
+++ b/drivers/staging/media/imx/m2m/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_IMX_M2M) += imx-m2m.o
diff --git a/drivers/staging/media/imx/m2m/imx-m2m.c b/drivers/staging/media/imx/m2m/imx-m2m.c
new file mode 100644
index 0000000..66b49ff
--- /dev/null
+++ b/drivers/staging/media/imx/m2m/imx-m2m.c
@@ -0,0 +1,1049 @@
+/*
+ * This is a mem2mem driver for the Freescale i.MX5/6 SOC. It carries out
+ * color-space conversion, downsizing, resizing, and rotation transformations
+ * on input buffers using the IPU Image Converter's Post-Processing task.
+ *
+ * Based on mem2mem_testdev.c by Pawel Osciak.
+ *
+ * Copyright (c) 2012-2013 Mentor Graphics Inc.
+ * Steve Longerbeam <steve_longerbeam@mentor.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-dma-contig.h>
+#include <video/imx-ipu-v3.h>
+#include <media/imx.h>
+
+MODULE_DESCRIPTION("i.MX5/6 Post-Processing mem2mem device");
+MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+static int instrument;
+module_param(instrument, int, 0);
+MODULE_PARM_DESC(instrument, "1 = enable conversion time measurement");
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE BIT(0)
+#define MEM2MEM_OUTPUT  BIT(1)
+
+#define MEM2MEM_NAME		"imx-m2m"
+
+/* Per queue */
+#define MEM2MEM_DEF_NUM_BUFS	VIDEO_MAX_FRAME
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT	SZ_256M
+
+#define dprintk(dev, fmt, arg...) \
+	v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+struct m2mx_ctx;
+
+struct m2mx_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	*vfd;
+	struct device           *ipu_dev;
+	struct ipu_soc          *ipu;
+
+	struct mutex		dev_mutex;
+
+	struct v4l2_m2m_dev	*m2m_dev;
+	struct vb2_alloc_ctx    *alloc_ctx;
+};
+
+struct m2mx_ctx {
+	struct v4l2_fh          fh;
+	struct m2mx_dev	*dev;
+
+	struct ipu_ic           *ic;
+	struct image_converter_ctx *ic_ctx;
+
+	/* The rotation controls */
+	struct v4l2_ctrl_handler ctrl_hdlr;
+	int                     rotation; /* degrees */
+	bool                    hflip;
+	bool                    vflip;
+
+	/* derived from rotation, hflip, vflip controls */
+	enum ipu_rotate_mode    rot_mode;
+
+	/* has the IC image converter been preapred */
+	bool                    image_converter_ready;
+
+	/* Abort requested by m2m */
+	bool			aborting;
+
+	/* Source and destination image data */
+	struct ipu_image  image[2];
+
+	/* for instrumenting */
+	struct timespec   start;
+};
+
+#define fh_to_ctx(p) container_of(p, struct m2mx_ctx, fh)
+
+enum {
+	V4L2_M2M_SRC = 0,
+	V4L2_M2M_DST = 1,
+};
+
+static const struct v4l2_ctrl_config m2mx_std_ctrl[] = {
+	{
+		.id	= V4L2_CID_HFLIP,
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.name	= "Horizontal Flip",
+		.def	= 0,
+		.min	= 0,
+		.max	= 1,
+		.step	= 1,
+	}, {
+		.id	= V4L2_CID_VFLIP,
+		.type	= V4L2_CTRL_TYPE_BOOLEAN,
+		.name	= "Vertical Flip",
+		.def	= 0,
+		.min	= 0,
+		.max	= 1,
+		.step	= 1,
+	}, {
+		.id	= V4L2_CID_ROTATE,
+		.type	= V4L2_CTRL_TYPE_INTEGER,
+		.name	= "Rotation",
+		.def	= 0,
+		.min	= 0,
+		.max	= 270,
+		.step	= 90,
+	},
+};
+
+#define M2MX_NUM_STD_CONTROLS ARRAY_SIZE(m2mx_std_ctrl)
+
+static struct ipu_image *get_image_data(struct m2mx_ctx *ctx,
+					enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		return &ctx->image[V4L2_M2M_SRC];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &ctx->image[V4L2_M2M_DST];
+	default:
+		break;
+	}
+	return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+static void m2mx_job_abort(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+
+	/* Will cancel the transaction in the next interrupt handler */
+	ipu_image_convert_abort(ctx->ic_ctx);
+	ctx->aborting = true;
+}
+
+static void m2mx_lock(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+	struct m2mx_dev *dev = ctx->dev;
+
+	mutex_lock(&dev->dev_mutex);
+}
+
+static void m2mx_unlock(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+	struct m2mx_dev *dev = ctx->dev;
+
+	mutex_unlock(&dev->dev_mutex);
+}
+
+static void m2mx_device_run(void *priv)
+{
+	struct m2mx_ctx *ctx = priv;
+	struct m2mx_dev *dev = ctx->dev;
+	struct vb2_v4l2_buffer *src_buf, *dst_buf;
+	dma_addr_t s_phys, d_phys;
+
+	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+	s_phys = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+	d_phys = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+	if (!s_phys || !d_phys) {
+		v4l2_err(&dev->v4l2_dev,
+			 "Acquiring kernel pointers to buffers failed\n");
+		return;
+	}
+
+	if (instrument)
+		getnstimeofday(&ctx->start);
+
+	ipu_image_convert_run(ctx->ic_ctx, s_phys, d_phys);
+}
+
+static void m2mx_convert_complete(void *data,
+				   struct image_converter_run *run,
+				   int err)
+{
+	struct m2mx_ctx *ctx = data;
+	struct m2mx_dev *dev = ctx->dev;
+	struct vb2_v4l2_buffer *src_vb, *dst_vb;
+
+	if (!ctx->aborting) {
+		src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+
+		if (instrument) {
+			struct timespec ts, diff;
+			unsigned long interval;
+
+			getnstimeofday(&ts);
+			diff = timespec_sub(ts, ctx->start);
+			interval = diff.tv_sec * 1000 * 1000 +
+				diff.tv_nsec / 1000;
+			v4l2_info(&ctx->dev->v4l2_dev,
+				  "buf%d completed in %lu usec\n",
+				  dst_vb->vb2_buf.index, interval);
+		}
+	}
+
+	v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *fh,
+			   struct v4l2_capability *cap)
+{
+	strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+	cap->bus_info[0] = 0;
+	cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+	const char *desc;
+	u32 fourcc;
+	int ret;
+
+	ret = ipu_image_convert_enum_format(f->index, &desc, &fourcc);
+	if (ret)
+		return ret;
+
+	strncpy(f->description, desc, sizeof(f->description) - 1);
+	f->pixelformat = fourcc;
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+				   struct v4l2_fmtdesc *f)
+{
+	return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
+				   struct v4l2_fmtdesc *f)
+{
+	return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int m2mx_g_fmt(struct m2mx_ctx *ctx, struct v4l2_format *f)
+{
+	struct vb2_queue *vq;
+	struct ipu_image *image;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	image = get_image_data(ctx, f->type);
+	if (!image)
+		return -EINVAL;
+
+	f->fmt.pix = image->pix;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_g_fmt(ctx, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_g_fmt(ctx, f);
+}
+
+static int m2mx_try_fmt(struct m2mx_ctx *ctx, struct v4l2_format *f)
+{
+	struct ipu_image test_in, test_out;
+	struct ipu_image *pin, *pout;
+	int ret;
+
+	pin = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	pout = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		test_out.pix = f->fmt.pix;
+		test_in = *pin;
+	} else {
+		test_in.pix = f->fmt.pix;
+		test_out = *pout;
+	}
+
+	ret = ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
+	if (ret)
+		return ret;
+
+	f->fmt.pix = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
+		test_out.pix : test_in.pix;
+
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_try_fmt(ctx, f);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *fh,
+				  struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_try_fmt(ctx, f);
+}
+
+static int m2mx_s_fmt(struct m2mx_ctx *ctx, struct v4l2_format *f)
+{
+	struct m2mx_dev *dev = ctx->dev;
+	struct ipu_image test_in, test_out;
+	struct ipu_image *pin, *pout;
+	struct vb2_queue *vq;
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	if (vb2_is_busy(vq)) {
+		v4l2_err(&dev->v4l2_dev, "%s: queue busy\n", __func__);
+		return -EBUSY;
+	}
+
+	pin = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	pout = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		test_out.pix = f->fmt.pix;
+		test_in = *pin;
+	} else {
+		test_in.pix = f->fmt.pix;
+		test_out = *pout;
+	}
+
+	ret = ipu_image_convert_adjust(&test_in, &test_out, ctx->rot_mode);
+	if (ret)
+		return ret;
+
+	f->fmt.pix = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
+		test_out.pix : test_in.pix;
+	*pin = test_in;
+	*pout = test_out;
+
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_s_fmt(ctx, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
+				struct v4l2_format *f)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return m2mx_s_fmt(ctx, f);
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+			  struct v4l2_requestbuffers *reqbufs)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+			   struct v4l2_buffer *buf)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_querybuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
+}
+
+static int vidioc_expbuf(struct file *file, void *fh,
+			 struct v4l2_exportbuffer *eb)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_expbuf(file, ctx->fh.m2m_ctx, eb);
+}
+
+static int vidioc_streamon(struct file *file, void *fh,
+			   enum v4l2_buf_type type)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *fh,
+			    enum v4l2_buf_type type)
+{
+	struct m2mx_ctx *ctx = fh_to_ctx(fh);
+
+	return v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, type);
+}
+
+static int m2mx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct m2mx_ctx *ctx = container_of(ctrl->handler,
+					     struct m2mx_ctx, ctrl_hdlr);
+	struct m2mx_dev *dev = ctx->dev;
+	enum ipu_rotate_mode rot_mode;
+	struct ipu_image *in, *out;
+	struct vb2_queue *vq;
+	bool hflip, vflip;
+	int rotation;
+	int ret;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	if (!vq)
+		return -EINVAL;
+
+	/* can't change rotation mid-streaming */
+	if (vb2_is_streaming(vq)) {
+		v4l2_err(&dev->v4l2_dev, "%s: not allowed while streaming\n",
+			 __func__);
+		return -EBUSY;
+	}
+
+	in = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	out = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	rotation = ctx->rotation;
+	hflip = ctx->hflip;
+	vflip = ctx->vflip;
+
+	switch (ctrl->id) {
+	case V4L2_CID_HFLIP:
+		hflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_VFLIP:
+		vflip = (ctrl->val == 1);
+		break;
+	case V4L2_CID_ROTATE:
+		rotation = ctrl->val;
+		break;
+	default:
+		v4l2_err(&dev->v4l2_dev, "Invalid control\n");
+		return -EINVAL;
+	}
+
+	ret = ipu_degrees_to_rot_mode(&rot_mode, rotation, hflip, vflip);
+	if (ret)
+		return ret;
+
+	/*
+	 * make sure this rotation will work with current src/dest
+	 * rectangles before setting
+	 */
+	ret = ipu_image_convert_verify(in, out, rot_mode);
+	if (ret)
+		return ret;
+
+	ctx->rotation = rotation;
+	ctx->hflip = hflip;
+	ctx->vflip = vflip;
+	ctx->rot_mode = rot_mode;
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops m2mx_ctrl_ops = {
+	.s_ctrl = m2mx_s_ctrl,
+};
+
+static int m2mx_init_controls(struct m2mx_ctx *ctx)
+{
+	struct v4l2_ctrl_handler *hdlr = &ctx->ctrl_hdlr;
+	const struct v4l2_ctrl_config *c;
+	int i, ret;
+
+	v4l2_ctrl_handler_init(hdlr, M2MX_NUM_STD_CONTROLS);
+
+	for (i = 0; i < M2MX_NUM_STD_CONTROLS; i++) {
+		c = &m2mx_std_ctrl[i];
+		v4l2_ctrl_new_std(hdlr, &m2mx_ctrl_ops,
+				  c->id, c->min, c->max, c->step, c->def);
+	}
+
+	if (hdlr->error) {
+		ret = hdlr->error;
+		goto free_ctrls;
+	}
+
+	ctx->fh.ctrl_handler = hdlr;
+	ret = v4l2_ctrl_handler_setup(hdlr);
+	if (ret)
+		goto free_ctrls;
+
+	return 0;
+
+free_ctrls:
+	v4l2_ctrl_handler_free(hdlr);
+	return ret;
+}
+
+static const struct v4l2_ioctl_ops m2mx_ioctl_ops = {
+	.vidioc_querycap	= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt_vid_cap,
+
+	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+	.vidioc_g_fmt_vid_out	= vidioc_g_fmt_vid_out,
+	.vidioc_try_fmt_vid_out	= vidioc_try_fmt_vid_out,
+	.vidioc_s_fmt_vid_out	= vidioc_s_fmt_vid_out,
+
+	.vidioc_reqbufs		= vidioc_reqbufs,
+	.vidioc_querybuf	= vidioc_querybuf,
+
+	.vidioc_qbuf		= vidioc_qbuf,
+	.vidioc_dqbuf		= vidioc_dqbuf,
+	.vidioc_expbuf		= vidioc_expbuf,
+
+	.vidioc_streamon	= vidioc_streamon,
+	.vidioc_streamoff	= vidioc_streamoff,
+};
+
+/*
+ * Queue operations
+ */
+
+static int m2mx_queue_setup(struct vb2_queue *vq,
+			     unsigned int *nbuffers, unsigned int *nplanes,
+			     unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(vq);
+	struct ipu_image *image;
+	unsigned int count = *nbuffers;
+
+	image = get_image_data(ctx, vq->type);
+	if (!image)
+		return -EINVAL;
+
+	while (image->pix.sizeimage * count > MEM2MEM_VID_MEM_LIMIT)
+		count--;
+
+	*nplanes = 1;
+	*nbuffers = count;
+	sizes[0] = image->pix.sizeimage;
+
+	alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+	return 0;
+}
+
+static int m2mx_buf_prepare(struct vb2_buffer *vb)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct m2mx_dev *dev = ctx->dev;
+	struct ipu_image *image;
+
+	image = get_image_data(ctx, vb->vb2_queue->type);
+	if (!image)
+		return -EINVAL;
+
+	if (vb2_plane_size(vb, 0) < image->pix.sizeimage) {
+		v4l2_err(&dev->v4l2_dev,
+			 "%s: data will not fit into plane (%lu < %lu)\n",
+			 __func__, vb2_plane_size(vb, 0),
+			 (long)image->pix.sizeimage);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, image->pix.sizeimage);
+
+	return 0;
+}
+
+static void m2mx_buf_queue(struct vb2_buffer *vb)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb));
+}
+
+static void m2mx_wait_prepare(struct vb2_queue *q)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+
+	m2mx_unlock(ctx);
+}
+
+static void m2mx_wait_finish(struct vb2_queue *q)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+
+	m2mx_lock(ctx);
+}
+
+static int m2mx_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+	struct ipu_image *pin, *pout;
+
+	/*
+	 * mem2mem requires streamon at both sides, capture and output,
+	 * and we only want to prepare the ipu-ic image converter once.
+	 */
+	if (ctx->image_converter_ready)
+		return 0;
+
+	pin = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	pout = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+	ctx->ic_ctx = ipu_image_convert_prepare(ctx->ic, pin, pout,
+						ctx->rot_mode,
+						m2mx_convert_complete, ctx);
+	if (IS_ERR(ctx->ic_ctx))
+		return PTR_ERR(ctx->ic_ctx);
+
+	ctx->image_converter_ready = true;
+	return 0;
+}
+
+static void m2mx_stop_streaming(struct vb2_queue *q)
+{
+	struct m2mx_ctx *ctx = vb2_get_drv_priv(q);
+
+	if (ctx->image_converter_ready) {
+		ipu_image_convert_unprepare(ctx->ic_ctx);
+		ctx->image_converter_ready = false;
+	}
+}
+
+static struct vb2_ops m2mx_qops = {
+	.queue_setup	 = m2mx_queue_setup,
+	.buf_prepare	 = m2mx_buf_prepare,
+	.buf_queue	 = m2mx_buf_queue,
+	.wait_prepare	 = m2mx_wait_prepare,
+	.wait_finish	 = m2mx_wait_finish,
+	.start_streaming = m2mx_start_streaming,
+	.stop_streaming  = m2mx_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct m2mx_ctx *ctx = priv;
+	int ret;
+
+	memset(src_vq, 0, sizeof(*src_vq));
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	src_vq->ops = &m2mx_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	memset(dst_vq, 0, sizeof(*dst_vq));
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	dst_vq->ops = &m2mx_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+	return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int m2mx_open(struct file *file)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct ipu_image *in, *out;
+	struct m2mx_ctx *ctx;
+	int ret = 0;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+	ctx->dev = dev;
+
+	ctx->ic = ipu_ic_get(dev->ipu, IC_TASK_POST_PROCESSOR);
+	if (IS_ERR(ctx->ic)) {
+		v4l2_err(&dev->v4l2_dev, "could not get IC PP\n");
+		ret = PTR_ERR(ctx->ic);
+		goto error_free;
+	}
+
+	v4l2_fh_init(&ctx->fh, dev->vfd);
+	file->private_data = &ctx->fh;
+	v4l2_fh_add(&ctx->fh);
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		ret = PTR_ERR(ctx->fh.m2m_ctx);
+		goto error_fh;
+	}
+
+	/*
+	 * set some defaults for output and capture image formats.
+	 * default for both is 640x480 RGB565.
+	 */
+	in = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	out = get_image_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	in->pix.width = out->pix.width = 640;
+	in->pix.height = out->pix.height = 480;
+	in->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+	out->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+	in->pix.sizeimage =
+		(in->pix.width * in->pix.height * 16) >> 3;
+	out->pix.sizeimage =
+		(out->pix.width * out->pix.height * 16) >> 3;
+
+	ret = m2mx_init_controls(ctx);
+	if (ret)
+		goto error_m2m_ctx;
+
+	mutex_unlock(&dev->dev_mutex);
+	return 0;
+
+error_m2m_ctx:
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+error_fh:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	ipu_ic_put(ctx->ic);
+error_free:
+	kfree(ctx);
+unlock:
+	mutex_unlock(&dev->dev_mutex);
+	return ret;
+}
+
+static int m2mx_release(struct file *file)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct m2mx_ctx *ctx = fh_to_ctx(file->private_data);
+
+	mutex_lock(&dev->dev_mutex);
+
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+	v4l2_ctrl_handler_free(&ctx->ctrl_hdlr);
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	ipu_ic_put(ctx->ic);
+	kfree(ctx);
+
+	mutex_unlock(&dev->dev_mutex);
+	return 0;
+}
+
+static unsigned int m2mx_poll(struct file *file,
+			       struct poll_table_struct *wait)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct m2mx_ctx *ctx = fh_to_ctx(file->private_data);
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+
+	ret = v4l2_m2m_poll(file, ctx->fh.m2m_ctx, wait);
+
+	mutex_unlock(&dev->dev_mutex);
+	return ret;
+}
+
+static int m2mx_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct m2mx_dev *dev = video_drvdata(file);
+	struct m2mx_ctx *ctx = fh_to_ctx(file->private_data);
+	int ret;
+
+	if (mutex_lock_interruptible(&dev->dev_mutex))
+		return -ERESTARTSYS;
+
+	ret = v4l2_m2m_mmap(file, ctx->fh.m2m_ctx, vma);
+
+	mutex_unlock(&dev->dev_mutex);
+	return ret;
+}
+
+static const struct v4l2_file_operations m2mx_fops = {
+	.owner		= THIS_MODULE,
+	.open		= m2mx_open,
+	.release	= m2mx_release,
+	.poll		= m2mx_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= m2mx_mmap,
+};
+
+static struct video_device m2mx_videodev = {
+	.name		= MEM2MEM_NAME,
+	.fops		= &m2mx_fops,
+	.ioctl_ops	= &m2mx_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release,
+	.vfl_dir	= VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+	.device_run	= m2mx_device_run,
+	.job_abort	= m2mx_job_abort,
+	.lock		= m2mx_lock,
+	.unlock		= m2mx_unlock,
+};
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static struct ipu_soc *m2mx_get_ipu(struct m2mx_dev *dev,
+				     struct device_node *node)
+{
+	struct device_node *ipu_node;
+	struct device *ipu_dev;
+	struct ipu_soc *ipu;
+
+	ipu_node = of_parse_phandle(node, "ipu", 0);
+	if (!ipu_node) {
+		v4l2_err(&dev->v4l2_dev, "missing ipu phandle!\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	ipu_dev = bus_find_device(&platform_bus_type, NULL,
+				  ipu_node, of_dev_node_match);
+	of_node_put(ipu_node);
+
+	if (!ipu_dev) {
+		v4l2_err(&dev->v4l2_dev, "failed to find ipu device!\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	device_lock(ipu_dev);
+
+	if (!ipu_dev->driver || !try_module_get(ipu_dev->driver->owner)) {
+		ipu = ERR_PTR(-EPROBE_DEFER);
+		v4l2_warn(&dev->v4l2_dev, "IPU driver not loaded\n");
+		device_unlock(ipu_dev);
+		goto dev_put;
+	}
+
+	dev->ipu_dev = ipu_dev;
+	ipu = dev_get_drvdata(ipu_dev);
+
+	device_unlock(ipu_dev);
+	return ipu;
+dev_put:
+	put_device(ipu_dev);
+	return ipu;
+}
+
+static void m2mx_put_ipu(struct m2mx_dev *dev)
+{
+	if (!IS_ERR_OR_NULL(dev->ipu_dev)) {
+		module_put(dev->ipu_dev->driver->owner);
+		put_device(dev->ipu_dev);
+	}
+}
+
+static int m2mx_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct m2mx_dev *dev;
+	struct video_device *vfd;
+	int ret;
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+	if (ret)
+		return ret;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+	/* get our IPU */
+	dev->ipu = m2mx_get_ipu(dev, node);
+	if (IS_ERR(dev->ipu)) {
+		v4l2_err(&dev->v4l2_dev, "could not get ipu\n");
+		ret = PTR_ERR(dev->ipu);
+		goto unreg_dev;
+	}
+
+	mutex_init(&dev->dev_mutex);
+
+	vfd = video_device_alloc();
+	if (!vfd) {
+		v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+		ret = -ENOMEM;
+		goto unreg_dev;
+	}
+
+	*vfd = m2mx_videodev;
+	vfd->v4l2_dev = &dev->v4l2_dev;
+	vfd->lock = &dev->dev_mutex;
+
+	dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR(dev->m2m_dev)) {
+		v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(dev->m2m_dev);
+		video_device_release(vfd);
+		goto unreg_dev;
+	}
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+	if (ret) {
+		v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+		video_device_release(vfd);
+		goto rel_m2m;
+	}
+
+	video_set_drvdata(vfd, dev);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", m2mx_videodev.name);
+	dev->vfd = vfd;
+	v4l2_info(&dev->v4l2_dev,
+		  "Device registered as /dev/video%d, on ipu%d\n",
+		  vfd->num, ipu_get_num(dev->ipu));
+
+	platform_set_drvdata(pdev, dev);
+
+	dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+	if (IS_ERR(dev->alloc_ctx)) {
+		v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n");
+		ret = PTR_ERR(dev->alloc_ctx);
+		goto unreg_vdev;
+	}
+
+	return 0;
+
+unreg_vdev:
+	video_unregister_device(dev->vfd);
+rel_m2m:
+	v4l2_m2m_release(dev->m2m_dev);
+unreg_dev:
+	v4l2_device_unregister(&dev->v4l2_dev);
+	return ret;
+}
+
+static int m2mx_remove(struct platform_device *pdev)
+{
+	struct m2mx_dev *dev =
+		(struct m2mx_dev *)platform_get_drvdata(pdev);
+
+	v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME "\n");
+	m2mx_put_ipu(dev);
+	v4l2_m2m_release(dev->m2m_dev);
+	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+	video_unregister_device(dev->vfd);
+
+	v4l2_device_unregister(&dev->v4l2_dev);
+
+	return 0;
+}
+
+static const struct of_device_id m2mx_dt_ids[] = {
+	{ .compatible = "fsl,imx-video-mem2mem" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, m2mx_dt_ids);
+
+static struct platform_driver m2mx_pdrv = {
+	.probe		= m2mx_probe,
+	.remove		= m2mx_remove,
+	.driver		= {
+		.name	= MEM2MEM_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table	= m2mx_dt_ids,
+	},
+};
+
+module_platform_driver(m2mx_pdrv);
-- 
1.9.1


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

* [PATCH 25/28] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (23 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 24/28] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:06   ` [PATCH 26/28] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
                     ` (2 subsequent siblings)
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Add to the MIPI CSI2 receiver node: compatible string, interrupt sources,
clocks.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6qdl.dtsi | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index ed613eb..50499eb 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -1119,7 +1119,14 @@
 			};
 
 			mipi_csi: mipi@021dc000 {
+				compatible = "fsl,imx-mipi-csi2";
 				reg = <0x021dc000 0x4000>;
+				interrupts = <0 100 0x04>, <0 101 0x04>;
+				clocks = <&clks IMX6QDL_CLK_HSI_TX>,
+					 <&clks IMX6QDL_CLK_VIDEO_27M>,
+					 <&clks IMX6QDL_CLK_EIM_SEL>;
+				clock-names = "dphy_clk", "cfg_clk", "pix_clk";
+				status = "disabled";
 			};
 
 			mipi_dsi: mipi@021e0000 {
-- 
1.9.1


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

* [PATCH 26/28] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (24 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 25/28] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
@ 2016-07-06 23:06   ` Steve Longerbeam
  2016-07-06 23:11   ` [PATCH 27/28] ARM: dts: imx6qdl: add mem2mem devices Steve Longerbeam
  2016-07-07 13:31   ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Tim Harvey
  27 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:06 UTC (permalink / raw)
  To: linux-media; +Cc: Philipp Zabel, Steve Longerbeam

From: Philipp Zabel <p.zabel@pengutronix.de>

This patch adds the device tree graph connecting the input multiplexers
to the IPU CSIs and the MIPI-CSI2 gasket on i.MX6.

On i.MX6Q/D two two-input multiplexers in front of IPU1 CSI0 and IPU2 CSI1
allow to select between CSI0/1 parallel input pads and the MIPI CSI-2 virtual
channels 0/3.

On i.MX6DL/S two five-input multiplexers in front of IPU1 CSI0 and IPU1 CSI1
allow to select between CSI0/1 parallel input pads and any of the four MIPI
CSI-2 virtual channels.

Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6dl.dtsi  | 183 +++++++++++++++++++++++++++++++++++++++++
 arch/arm/boot/dts/imx6q.dtsi   | 120 +++++++++++++++++++++++++++
 arch/arm/boot/dts/imx6qdl.dtsi |   6 ++
 3 files changed, 309 insertions(+)

diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi
index 9a4c22c..41ef065 100644
--- a/arch/arm/boot/dts/imx6dl.dtsi
+++ b/arch/arm/boot/dts/imx6dl.dtsi
@@ -109,6 +109,118 @@
 		compatible = "fsl,imx-gpu-subsystem";
 		cores = <&gpu_2d>, <&gpu_3d>;
 	};
+
+	ipu1_csi0_mux: ipu1_csi0_mux@34 {
+		compatible = "imx-video-mux";
+		reg = <0x34 0x07>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu1_csi0_mux_from_mipi_vc0: endpoint {
+				remote-endpoint = <&mipi_vc0_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu1_csi0_mux_from_mipi_vc1: endpoint {
+				remote-endpoint = <&mipi_vc1_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu1_csi0_mux_from_mipi_vc2: endpoint {
+				remote-endpoint = <&mipi_vc2_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@3 {
+			reg = <3>;
+
+			ipu1_csi0_mux_from_mipi_vc3: endpoint {
+				remote-endpoint = <&mipi_vc3_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@4 {
+			reg = <4>;
+
+			ipu1_csi0_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@5 {
+			reg = <5>;
+
+			ipu1_csi0_mux_to_ipu1_csi0: endpoint {
+				remote-endpoint = <&ipu1_csi0_from_ipu1_csi0_mux>;
+			};
+		};
+	};
+
+	ipu1_csi1_mux: ipu1_csi1_mux@34 {
+		compatible = "imx-video-mux";
+		reg = <0x34 0x38>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu1_csi1_mux_from_mipi_vc0: endpoint {
+				remote-endpoint = <&mipi_vc0_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu1_csi1_mux_from_mipi_vc1: endpoint {
+				remote-endpoint = <&mipi_vc1_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu1_csi1_mux_from_mipi_vc2: endpoint {
+				remote-endpoint = <&mipi_vc2_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@3 {
+			reg = <3>;
+
+			ipu1_csi1_mux_from_mipi_vc3: endpoint {
+				remote-endpoint = <&mipi_vc3_to_ipu1_csi1_mux>;
+			};
+		};
+
+		port@4 {
+			reg = <4>;
+
+			ipu1_csi1_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@5 {
+			reg = <5>;
+
+			ipu1_csi1_mux_to_ipu1_csi1: endpoint {
+				remote-endpoint = <&ipu1_csi1_from_ipu1_csi1_mux>;
+			};
+		};
+	};
 };
 
 &gpt {
@@ -131,3 +243,74 @@
 &vpu {
 	compatible = "fsl,imx6dl-vpu", "cnm,coda960";
 };
+
+&ipu1_csi1 {
+	ipu1_csi1_from_ipu1_csi1_mux: endpoint {
+		remote-endpoint = <&ipu1_csi1_mux_to_ipu1_csi1>;
+	};
+};
+
+&mipi_csi {
+	port@0 {
+		reg = <0>;
+
+		mipi_csi_from_mipi_sensor: endpoint {
+		};
+	};
+
+	port@1 {
+		reg = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc0_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc0>;
+		};
+
+		mipi_vc0_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc0>;
+		};
+	};
+
+	port@2 {
+		reg = <2>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc1_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc1>;
+		};
+
+		mipi_vc1_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc1>;
+		};
+	};
+
+	port@3 {
+		reg = <3>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc2_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc2>;
+		};
+
+		mipi_vc2_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc2>;
+		};
+	};
+
+	port@4 {
+		reg = <4>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mipi_vc3_to_ipu1_csi0_mux: endpoint@0 {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc3>;
+		};
+
+		mipi_vc3_to_ipu1_csi1_mux: endpoint@1 {
+			remote-endpoint = <&ipu1_csi1_mux_from_mipi_vc3>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index c30c836..9b9fc91 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -143,10 +143,18 @@
 
 			ipu2_csi0: port@0 {
 				reg = <0>;
+
+				ipu2_csi0_from_mipi_vc2: endpoint {
+					remote-endpoint = <&mipi_vc2_to_ipu2_csi0>;
+				};
 			};
 
 			ipu2_csi1: port@1 {
 				reg = <1>;
+
+				ipu2_csi1_from_ipu2_csi1_mux: endpoint {
+					remote-endpoint = <&ipu2_csi1_mux_to_ipu2_csi1>;
+				};
 			};
 
 			ipu2_di0: port@2 {
@@ -207,6 +215,71 @@
 		compatible = "fsl,imx-gpu-subsystem";
 		cores = <&gpu_2d>, <&gpu_3d>, <&gpu_vg>;
 	};
+
+
+	ipu1_csi0_mux: ipu1_csi0_mux@4 {
+		compatible = "imx-video-mux";
+		reg = <0x04 0x80000>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu1_csi0_mux_from_mipi_vc0: endpoint {
+				remote-endpoint = <&mipi_vc0_to_ipu1_csi0_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu1_csi0_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu1_csi0_mux_to_ipu1_csi0: endpoint {
+				remote-endpoint = <&ipu1_csi0_from_ipu1_csi0_mux>;
+			};
+		};
+	};
+
+	ipu2_csi1_mux: ipu2_csi1_mux@4 {
+		compatible = "imx-video-mux";
+		reg = <0x04 0x100000>;
+		gpr = <&gpr>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			reg = <0>;
+
+			ipu2_csi1_mux_from_mipi_vc3: endpoint {
+				remote-endpoint = <&mipi_vc3_to_ipu2_csi1_mux>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			ipu2_csi1_mux_from_parallel_sensor: endpoint {
+			};
+		};
+
+		port@2 {
+			reg = <2>;
+
+			ipu2_csi1_mux_to_ipu2_csi1: endpoint {
+				remote-endpoint = <&ipu2_csi1_from_ipu2_csi1_mux>;
+			};
+		};
+	};
 };
 
 &hdmi {
@@ -229,6 +302,12 @@
 	};
 };
 
+&ipu1_csi1 {
+	ipu1_csi1_from_mipi_vc1: endpoint {
+		remote-endpoint = <&mipi_vc1_to_ipu1_csi1>;
+	};
+};
+
 &ldb {
 	clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>, <&clks IMX6QDL_CLK_LDB_DI1_SEL>,
 		 <&clks IMX6QDL_CLK_IPU1_DI0_SEL>, <&clks IMX6QDL_CLK_IPU1_DI1_SEL>,
@@ -275,6 +354,47 @@
 	};
 };
 
+&mipi_csi {
+	port@0 {
+		reg = <0>;
+
+		mipi_csi_from_mipi_sensor: endpoint {
+		};
+	};
+
+	port@1 {
+		reg = <1>;
+
+		mipi_vc0_to_ipu1_csi0_mux: endpoint {
+			remote-endpoint = <&ipu1_csi0_mux_from_mipi_vc0>;
+		};
+	};
+
+	port@2 {
+		reg = <2>;
+
+		mipi_vc1_to_ipu1_csi1: endpoint {
+			remote-endpoint = <&ipu1_csi1_from_mipi_vc1>;
+		};
+	};
+
+	port@3 {
+		reg = <3>;
+
+		mipi_vc2_to_ipu2_csi0: endpoint {
+			remote-endpoint = <&ipu2_csi0_from_mipi_vc2>;
+		};
+	};
+
+	port@4 {
+		reg = <4>;
+
+		mipi_vc3_to_ipu2_csi1_mux: endpoint {
+			remote-endpoint = <&ipu2_csi1_mux_from_mipi_vc3>;
+		};
+	};
+};
+
 &mipi_dsi {
 	ports {
 		port@2 {
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 50499eb..838d1d5 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -1121,6 +1121,8 @@
 			mipi_csi: mipi@021dc000 {
 				compatible = "fsl,imx-mipi-csi2";
 				reg = <0x021dc000 0x4000>;
+				#address-cells = <1>;
+				#size-cells = <0>;
 				interrupts = <0 100 0x04>, <0 101 0x04>;
 				clocks = <&clks IMX6QDL_CLK_HSI_TX>,
 					 <&clks IMX6QDL_CLK_VIDEO_27M>,
@@ -1226,6 +1228,10 @@
 
 			ipu1_csi0: port@0 {
 				reg = <0>;
+
+				ipu1_csi0_from_ipu1_csi0_mux: endpoint {
+					remote-endpoint = <&ipu1_csi0_mux_to_ipu1_csi0>;
+				};
 			};
 
 			ipu1_csi1: port@1 {
-- 
1.9.1


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

* [PATCH 27/28] ARM: dts: imx6qdl: add mem2mem devices
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (25 preceding siblings ...)
  2016-07-06 23:06   ` [PATCH 26/28] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
@ 2016-07-06 23:11   ` Steve Longerbeam
  2016-07-06 23:11     ` [PATCH 28/28] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers Steve Longerbeam
  2016-07-07 13:31   ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Tim Harvey
  27 siblings, 1 reply; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:11 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Enables ipu-mem2mem devices on imx6qdl.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/boot/dts/imx6q.dtsi   | 7 +++++++
 arch/arm/boot/dts/imx6qdl.dtsi | 6 ++++++
 2 files changed, 13 insertions(+)

diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 9b9fc91..b81d262 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -204,6 +204,13 @@
 				};
 			};
 		};
+
+
+		ipum2m1: ipum2m@ipu2 {
+			compatible = "fsl,imx-video-mem2mem";
+			ipu = <&ipu2>;
+			status = "okay";
+		};
 	};
 
 	display-subsystem {
diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
index 838d1d5..ec0be0e 100644
--- a/arch/arm/boot/dts/imx6qdl.dtsi
+++ b/arch/arm/boot/dts/imx6qdl.dtsi
@@ -1288,5 +1288,11 @@
 				};
 			};
 		};
+
+		ipum2m0: ipum2m@ipu1 {
+			compatible = "fsl,imx-video-mem2mem";
+			ipu = <&ipu1>;
+			status = "okay";
+		};
 	};
 };
-- 
1.9.1


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

* [PATCH 28/28] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
  2016-07-06 23:11   ` [PATCH 27/28] ARM: dts: imx6qdl: add mem2mem devices Steve Longerbeam
@ 2016-07-06 23:11     ` Steve Longerbeam
  0 siblings, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-06 23:11 UTC (permalink / raw)
  To: linux-media; +Cc: Steve Longerbeam

Enable imx v4l2 staging drivers. For video capture on
the SabreAuto, the ADV7180 video decoder also requires the
i2c-mux-gpio and the max7310 port expander.

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
---
 arch/arm/configs/imx_v6_v7_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
index 21339ce..8b1590a 100644
--- a/arch/arm/configs/imx_v6_v7_defconfig
+++ b/arch/arm/configs/imx_v6_v7_defconfig
@@ -327,6 +327,8 @@ CONFIG_FSL_EDMA=y
 CONFIG_IMX_SDMA=y
 CONFIG_MXS_DMA=y
 CONFIG_STAGING=y
+CONFIG_STAGING_MEDIA=y
+CONFIG_VIDEO_IMX=y
 # CONFIG_IOMMU_SUPPORT is not set
 CONFIG_IIO=y
 CONFIG_VF610_ADC=y
-- 
1.9.1


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

* Re: [PATCH 00/28] i.MX5/6 Video Capture, v2
  2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
                     ` (26 preceding siblings ...)
  2016-07-06 23:11   ` [PATCH 27/28] ARM: dts: imx6qdl: add mem2mem devices Steve Longerbeam
@ 2016-07-07 13:31   ` Tim Harvey
  27 siblings, 0 replies; 105+ messages in thread
From: Tim Harvey @ 2016-07-07 13:31 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-media, Steve Longerbeam

On Wed, Jul 6, 2016 at 4:06 PM, Steve Longerbeam <slongerbeam@gmail.com> wrote:
> Philipp Zabel (2):
>   media: imx: Add video switch
>   ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their
>     connections
>
> Steve Longerbeam (25):
>   gpu: ipu-v3: Add Video Deinterlacer unit
>   gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset()
>   gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize()
>   gpu: ipu-v3: Add ipu_get_num()
>   gpu: ipu-v3: Add IDMA channel linking support
>   gpu: ipu-v3: Add ipu_set_vdi_src_mux()
>   gpu: ipu-v3: Add VDI input IDMAC channels
>   gpu: ipu-v3: Add ipu_csi_set_src()
>   gpu: ipu-v3: Add ipu_ic_set_src()
>   gpu: ipu-v3: set correct full sensor frame for PAL/NTSC
>   gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats
>   gpu: ipu-v3: Fix IRT usage
>   gpu: ipu-ic: Add complete image conversion support with tiling
>   gpu: ipu-ic: allow multiple handles to ic
>   gpu: ipu-v3: rename CSI client device
>   gpio: pca953x: Add optional reset gpio control
>   clocksource/drivers/imx: add input capture support
>   media: Add i.MX5/6 camera interface driver
>   media: imx: Add MIPI CSI-2 Receiver driver
>   media: imx: Add support for MIPI CSI-2 OV5640
>   media: imx: Add support for Parallel OV5642
>   media: Add i.MX5/6 mem2mem driver
>   ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node
>   ARM: dts: imx6qdl: add mem2mem devices
>   ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers
>
> Suresh Dhandapani (1):
>   gpu: ipu-v3: Fix CSI0 blur in NTSC format
>
>  Documentation/devicetree/bindings/media/imx.txt    |  449 ++
>  Documentation/video4linux/imx_camera.txt           |  243 ++
>  arch/arm/boot/dts/imx6dl.dtsi                      |  183 +
>  arch/arm/boot/dts/imx6q.dtsi                       |  127 +
>  arch/arm/boot/dts/imx6qdl.dtsi                     |   19 +
>  arch/arm/configs/imx_v6_v7_defconfig               |    2 +
>  drivers/clocksource/timer-imx-gpt.c                |  463 ++-
>  drivers/gpio/gpio-pca953x.c                        |   18 +
>  drivers/gpu/ipu-v3/Makefile                        |    2 +-
>  drivers/gpu/ipu-v3/ipu-common.c                    |  155 +-
>  drivers/gpu/ipu-v3/ipu-cpmem.c                     |   13 +
>  drivers/gpu/ipu-v3/ipu-csi.c                       |   36 +-
>  drivers/gpu/ipu-v3/ipu-ic.c                        | 1769 +++++++-
>  drivers/gpu/ipu-v3/ipu-prv.h                       |    7 +
>  drivers/gpu/ipu-v3/ipu-vdi.c                       |  266 ++
>  drivers/staging/media/Kconfig                      |    2 +
>  drivers/staging/media/Makefile                     |    1 +
>  drivers/staging/media/imx/Kconfig                  |   35 +
>  drivers/staging/media/imx/Makefile                 |    2 +
>  drivers/staging/media/imx/capture/Kconfig          |   35 +
>  drivers/staging/media/imx/capture/Makefile         |    9 +
>  drivers/staging/media/imx/capture/imx-camif.c      | 2326 +++++++++++
>  drivers/staging/media/imx/capture/imx-camif.h      |  270 ++
>  drivers/staging/media/imx/capture/imx-csi.c        |  195 +
>  drivers/staging/media/imx/capture/imx-ic-prpenc.c  |  661 +++
>  drivers/staging/media/imx/capture/imx-of.c         |  354 ++
>  drivers/staging/media/imx/capture/imx-of.h         |   18 +
>  drivers/staging/media/imx/capture/imx-smfc.c       |  506 +++
>  drivers/staging/media/imx/capture/imx-vdic.c       |  995 +++++
>  .../staging/media/imx/capture/imx-video-switch.c   |  347 ++
>  drivers/staging/media/imx/capture/mipi-csi2.c      |  373 ++
>  drivers/staging/media/imx/capture/ov5640-mipi.c    | 2303 +++++++++++
>  drivers/staging/media/imx/capture/ov5642.c         | 4309 ++++++++++++++++++++
>  drivers/staging/media/imx/m2m/Makefile             |    1 +
>  drivers/staging/media/imx/m2m/imx-m2m.c            | 1049 +++++
>  include/linux/mxc_icap.h                           |   20 +
>  include/media/imx.h                                |   15 +
>  include/uapi/Kbuild                                |    1 +
>  include/uapi/linux/v4l2-controls.h                 |    4 +
>  include/uapi/media/Kbuild                          |    2 +
>  include/uapi/media/imx.h                           |   22 +
>  include/video/imx-ipu-v3.h                         |   96 +-
>  42 files changed, 17596 insertions(+), 107 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/media/imx.txt
>  create mode 100644 Documentation/video4linux/imx_camera.txt
>  create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c
>  create mode 100644 drivers/staging/media/imx/Kconfig
>  create mode 100644 drivers/staging/media/imx/Makefile
>  create mode 100644 drivers/staging/media/imx/capture/Kconfig
>  create mode 100644 drivers/staging/media/imx/capture/Makefile
>  create mode 100644 drivers/staging/media/imx/capture/imx-camif.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-camif.h
>  create mode 100644 drivers/staging/media/imx/capture/imx-csi.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-ic-prpenc.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-of.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-of.h
>  create mode 100644 drivers/staging/media/imx/capture/imx-smfc.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-vdic.c
>  create mode 100644 drivers/staging/media/imx/capture/imx-video-switch.c
>  create mode 100644 drivers/staging/media/imx/capture/mipi-csi2.c
>  create mode 100644 drivers/staging/media/imx/capture/ov5640-mipi.c
>  create mode 100644 drivers/staging/media/imx/capture/ov5642.c
>  create mode 100644 drivers/staging/media/imx/m2m/Makefile
>  create mode 100644 drivers/staging/media/imx/m2m/imx-m2m.c
>  create mode 100644 include/linux/mxc_icap.h
>  create mode 100644 include/media/imx.h
>  create mode 100644 include/uapi/media/Kbuild
>  create mode 100644 include/uapi/media/imx.h
>

Steve,

I think at this point it would be best to send 'just' your gpu: ipu*
patches above in their own series 'to' Philipp (as he's the maintainer
for the drivers/files in those dirs) with a cc to linux-media and with
an explanation in the cover-letter that explains these are primitives
necessary for capture drivers to come in a separate series. There are
15 patches there which you have posted before and they have never
really been commented on (because I think the entire series they are
always a part of is large and goes across several sub-systems). I
think if you treat those as separate patches and we focus on getting
those in, the rest will be easier as follow-on patches.

Also, if you use the format '[PATCH v2 00/28]' it tends to filter
these way better in mail-readers so that they get the proper attention
they deserve. Because there was no 'v2' in the prefix of these patches
they got all mixed up with your most recent series and are difficult
to review.

Regards,

Tim

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

* Re: [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-06 23:06   ` [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
@ 2016-07-13 18:58     ` Mauro Carvalho Chehab
  2016-07-13 19:06       ` Mauro Carvalho Chehab
  2016-07-13 22:24       ` Steve Longerbeam
  0 siblings, 2 replies; 105+ messages in thread
From: Mauro Carvalho Chehab @ 2016-07-13 18:58 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-media, Steve Longerbeam

Em Wed,  6 Jul 2016 16:06:44 -0700
Steve Longerbeam <slongerbeam@gmail.com> escreveu:

> This patch implements complete image conversion support to ipu-ic,
> with tiling to support scaling to and from images up to 4096x4096.
> Image rotation is also supported.
> 
> The internal API is subsystem agnostic (no V4L2 dependency except
> for the use of V4L2 fourcc pixel formats).
> 
> Callers prepare for image conversion by calling
> ipu_image_convert_prepare(), which initializes the parameters of
> the conversion. The caller passes in the ipu_ic task to use for
> the conversion, the input and output image formats, a rotation mode,
> and a completion callback and completion context pointer:
> 
> struct image_converter_ctx *
> ipu_image_convert_prepare(struct ipu_ic *ic,
>                           struct ipu_image *in, struct ipu_image *out,
>                           enum ipu_rotate_mode rot_mode,
>                           image_converter_cb_t complete,
>                           void *complete_context);
> 
> The caller is given a new conversion context that must be passed to
> the further APIs:
> 
> struct image_converter_run *
> ipu_image_convert_run(struct image_converter_ctx *ctx,
>                       dma_addr_t in_phys, dma_addr_t out_phys);
> 
> This queues a new image conversion request to a run queue, and
> starts the conversion immediately if the run queue is empty. Only
> the physaddr's of the input and output image buffers are needed,
> since the conversion context was created previously with
> ipu_image_convert_prepare(). Returns a new run object pointer. When
> the conversion completes, the run pointer is returned to the
> completion callback.
> 
> void image_convert_abort(struct image_converter_ctx *ctx);
> 
> This will abort any active or pending conversions for this context.
> Any currently active or pending runs belonging to this context are
> returned via the completion callback with an error status.
> 
> void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
> 
> Unprepares the conversion context. Any active or pending runs will
> be aborted by calling image_convert_abort().
> ---
>  drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
>  include/video/imx-ipu-v3.h  |   57 +-
>  2 files changed, 1736 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> index 5329bfe..f6a1125 100644
> --- a/drivers/gpu/ipu-v3/ipu-ic.c
> +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> @@ -17,6 +17,8 @@
>  #include <linux/bitrev.h>
>  #include <linux/io.h>
>  #include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/dma-mapping.h>
>  #include "ipu-prv.h"
>  
>  /* IC Register Offsets */
> @@ -82,6 +84,40 @@
>  #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
>  #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
>  
> +/*
> + * The IC Resizer has a restriction that the output frame from the
> + * resizer must be 1024 or less in both width (pixels) and height
> + * (lines).
> + *
> + * The image conversion support attempts to split up a conversion when
> + * the desired output (converted) frame resolution exceeds the IC resizer
> + * limit of 1024 in either dimension.
> + *
> + * If either dimension of the output frame exceeds the limit, the
> + * dimension is split into 1, 2, or 4 equal stripes, for a maximum
> + * of 4*4 or 16 tiles. A conversion is then carried out for each
> + * tile (but taking care to pass the full frame stride length to
> + * the DMA channel's parameter memory!). IDMA double-buffering is used
> + * to convert each tile back-to-back when possible (see note below
> + * when double_buffering boolean is set).
> + *
> + * Note that the input frame must be split up into the same number
> + * of tiles as the output frame.
> + */
> +#define MAX_STRIPES_W    4
> +#define MAX_STRIPES_H    4
> +#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
> +
> +#define MIN_W     128
> +#define MIN_H     128
> +#define MAX_W     4096
> +#define MAX_H     4096
> +
> +enum image_convert_type {
> +	IMAGE_CONVERT_IN = 0,
> +	IMAGE_CONVERT_OUT,
> +};
> +
>  struct ic_task_regoffs {
>  	u32 rsc;
>  	u32 tpmem_csc[2];
> @@ -96,6 +132,16 @@ struct ic_task_bitfields {
>  	u32 ic_cmb_galpha_bit;
>  };
>  
> +struct ic_task_channels {
> +	int in;
> +	int out;
> +	int rot_in;
> +	int rot_out;
> +	int vdi_in_p;
> +	int vdi_in;
> +	int vdi_in_n;
> +};
> +
>  static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
>  	[IC_TASK_ENCODER] = {
>  		.rsc = IC_PRP_ENC_RSC,
> @@ -138,12 +184,159 @@ static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
>  	},
>  };
>  
> +static const struct ic_task_channels ic_task_ch[IC_NUM_TASKS] = {
> +	[IC_TASK_ENCODER] = {
> +		.out = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
> +		.rot_in = IPUV3_CHANNEL_MEM_ROT_ENC,
> +		.rot_out = IPUV3_CHANNEL_ROT_ENC_MEM,
> +	},
> +	[IC_TASK_VIEWFINDER] = {
> +		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
> +		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
> +		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
> +		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
> +		.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_P,
> +		.vdi_in = IPUV3_CHANNEL_MEM_VDI,
> +		.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_N,
> +	},
> +	[IC_TASK_POST_PROCESSOR] = {
> +		.in = IPUV3_CHANNEL_MEM_IC_PP,
> +		.out = IPUV3_CHANNEL_IC_PP_MEM,
> +		.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
> +		.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
> +	},
> +};
> +
> +struct ipu_ic_dma_buf {
> +	void          *virt;
> +	dma_addr_t    phys;
> +	unsigned long len;
> +};
> +
> +/* dimensions of one tile */
> +struct ipu_ic_tile {
> +	unsigned int width;
> +	unsigned int height;
> +	/* size and strides are in bytes */
> +	unsigned int size;
> +	unsigned int stride;
> +	unsigned int rot_stride;
> +};
> +
> +struct ipu_ic_tile_off {
> +	/* start Y or packed offset of this tile */
> +	u32     offset;
> +	/* offset from start to tile in U plane, for planar formats */
> +	u32     u_off;
> +	/* offset from start to tile in V plane, for planar formats */
> +	u32     v_off;
> +};
> +
> +struct ipu_ic_pixfmt {
> +	char	*name;
> +	u32	fourcc;        /* V4L2 fourcc */
> +	int     bpp;           /* total bpp */
> +	int     y_depth;       /* depth of Y plane for planar formats */
> +	int     uv_width_dec;  /* decimation in width for U/V planes */
> +	int     uv_height_dec; /* decimation in height for U/V planes */
> +	bool    uv_swapped;    /* U and V planes are swapped */
> +	bool    uv_packed;     /* partial planar (U and V in same plane) */
> +};
> +
> +struct ipu_ic_image {
> +	struct ipu_image base;
> +	enum image_convert_type type;
> +
> +	const struct ipu_ic_pixfmt *fmt;
> +	unsigned int stride;
> +
> +	/* # of rows (horizontal stripes) if dest height is > 1024 */
> +	unsigned int num_rows;
> +	/* # of columns (vertical stripes) if dest width is > 1024 */
> +	unsigned int num_cols;
> +
> +	struct ipu_ic_tile tile;
> +	struct ipu_ic_tile_off tile_off[MAX_TILES];
> +};
> +
> +struct image_converter_ctx;
> +struct image_converter;
>  struct ipu_ic_priv;
> +struct ipu_ic;
> +
> +struct image_converter_run {
> +	struct image_converter_ctx *ctx;
> +
> +	dma_addr_t in_phys;
> +	dma_addr_t out_phys;
> +
> +	int status;
> +
> +	struct list_head list;
> +};
> +
> +struct image_converter_ctx {
> +	struct image_converter *cvt;
> +
> +	image_converter_cb_t complete;
> +	void *complete_context;
> +
> +	/* Source/destination image data and rotation mode */
> +	struct ipu_ic_image in;
> +	struct ipu_ic_image out;
> +	enum ipu_rotate_mode rot_mode;
> +
> +	/* intermediate buffer for rotation */
> +	struct ipu_ic_dma_buf rot_intermediate[2];
> +
> +	/* current buffer number for double buffering */
> +	int cur_buf_num;
> +
> +	bool aborting;
> +	struct completion aborted;
> +
> +	/* can we use double-buffering for this conversion operation? */
> +	bool double_buffering;
> +	/* num_rows * num_cols */
> +	unsigned int num_tiles;
> +	/* next tile to process */
> +	unsigned int next_tile;
> +	/* where to place converted tile in dest image */
> +	unsigned int out_tile_map[MAX_TILES];
> +
> +	struct list_head list;
> +};
> +
> +struct image_converter {
> +	struct ipu_ic *ic;
> +
> +	struct ipuv3_channel *in_chan;
> +	struct ipuv3_channel *out_chan;
> +	struct ipuv3_channel *rotation_in_chan;
> +	struct ipuv3_channel *rotation_out_chan;
> +
> +	/* the IPU end-of-frame irqs */
> +	int out_eof_irq;
> +	int rot_out_eof_irq;
> +
> +	spinlock_t irqlock;
> +
> +	/* list of convert contexts */
> +	struct list_head ctx_list;
> +	/* queue of conversion runs */
> +	struct list_head pending_q;
> +	/* queue of completed runs */
> +	struct list_head done_q;
> +
> +	/* the current conversion run */
> +	struct image_converter_run *current_run;
> +};
>  
>  struct ipu_ic {
>  	enum ipu_ic_task task;
>  	const struct ic_task_regoffs *reg;
>  	const struct ic_task_bitfields *bit;
> +	const struct ic_task_channels *ch;
>  
>  	enum ipu_color_space in_cs, g_in_cs;
>  	enum ipu_color_space out_cs;
> @@ -151,6 +344,8 @@ struct ipu_ic {
>  	bool rotation;
>  	bool in_use;
>  
> +	struct image_converter cvt;
> +
>  	struct ipu_ic_priv *priv;
>  };
>  
> @@ -619,7 +814,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
>  	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
>  	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
>  
> -	if (rot >= IPU_ROTATE_90_RIGHT)
> +	if (ipu_rot_mode_is_irt(rot))
>  		ic->rotation = true;
>  
>  unlock:
> @@ -661,6 +856,1480 @@ static void ipu_irt_disable(struct ipu_ic *ic)
>  		priv->irt_use_count = 0;
>  }
>  
> +/*
> + * Complete image conversion support follows
> + */
> +
> +static const struct ipu_ic_pixfmt ipu_ic_formats[] = {
> +	{
> +		.name	= "RGB565",
> +		.fourcc	= V4L2_PIX_FMT_RGB565,
> +		.bpp    = 16,
> +	}, {
> +		.name	= "RGB24",
> +		.fourcc	= V4L2_PIX_FMT_RGB24,
> +		.bpp    = 24,
> +	}, {
> +		.name	= "BGR24",
> +		.fourcc	= V4L2_PIX_FMT_BGR24,
> +		.bpp    = 24,
> +	}, {
> +		.name	= "RGB32",
> +		.fourcc	= V4L2_PIX_FMT_RGB32,
> +		.bpp    = 32,
> +	}, {
> +		.name	= "BGR32",
> +		.fourcc	= V4L2_PIX_FMT_BGR32,
> +		.bpp    = 32,
> +	}, {
> +		.name	= "4:2:2 packed, YUYV",
> +		.fourcc	= V4L2_PIX_FMT_YUYV,
> +		.bpp    = 16,
> +		.uv_width_dec = 2,
> +		.uv_height_dec = 1,
> +	}, {
> +		.name	= "4:2:2 packed, UYVY",
> +		.fourcc	= V4L2_PIX_FMT_UYVY,
> +		.bpp    = 16,
> +		.uv_width_dec = 2,
> +		.uv_height_dec = 1,
> +	}, {
> +		.name	= "4:2:0 planar, YUV",
> +		.fourcc	= V4L2_PIX_FMT_YUV420,
> +		.bpp    = 12,
> +		.y_depth = 8,
> +		.uv_width_dec = 2,
> +		.uv_height_dec = 2,
> +	}, {
> +		.name	= "4:2:0 planar, YVU",
> +		.fourcc	= V4L2_PIX_FMT_YVU420,
> +		.bpp    = 12,
> +		.y_depth = 8,
> +		.uv_width_dec = 2,
> +		.uv_height_dec = 2,
> +		.uv_swapped = true,
> +	}, {
> +		.name   = "4:2:0 partial planar, NV12",
> +		.fourcc = V4L2_PIX_FMT_NV12,
> +		.bpp    = 12,
> +		.y_depth = 8,
> +		.uv_width_dec = 2,
> +		.uv_height_dec = 2,
> +		.uv_packed = true,
> +	}, {
> +		.name   = "4:2:2 planar, YUV",
> +		.fourcc = V4L2_PIX_FMT_YUV422P,
> +		.bpp    = 16,
> +		.y_depth = 8,
> +		.uv_width_dec = 2,
> +		.uv_height_dec = 1,
> +	}, {
> +		.name   = "4:2:2 partial planar, NV16",
> +		.fourcc = V4L2_PIX_FMT_NV16,
> +		.bpp    = 16,
> +		.y_depth = 8,
> +		.uv_width_dec = 2,
> +		.uv_height_dec = 1,
> +		.uv_packed = true,
> +	},
> +};
> +
> +static const struct ipu_ic_pixfmt *ipu_ic_get_format(u32 fourcc)
> +{
> +	const struct ipu_ic_pixfmt *ret = NULL;
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ipu_ic_formats); i++) {
> +		if (ipu_ic_formats[i].fourcc == fourcc) {
> +			ret = &ipu_ic_formats[i];
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void ipu_ic_dump_format(struct image_converter_ctx *ctx,
> +			       struct ipu_ic_image *ic_image)
> +{
> +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> +
> +	dev_dbg(priv->ipu->dev,
> +		"ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
> +		ctx,
> +		ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
> +		ic_image->base.pix.width, ic_image->base.pix.height,
> +		ic_image->num_cols, ic_image->num_rows,
> +		ic_image->tile.width, ic_image->tile.height,
> +		ic_image->fmt->fourcc & 0xff,
> +		(ic_image->fmt->fourcc >> 8) & 0xff,
> +		(ic_image->fmt->fourcc >> 16) & 0xff,
> +		(ic_image->fmt->fourcc >> 24) & 0xff);
> +}
> +
> +int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc)
> +{
> +	const struct ipu_ic_pixfmt *fmt;
> +
> +	if (index >= (int)ARRAY_SIZE(ipu_ic_formats))
> +		return -EINVAL;
> +
> +	/* Format found */
> +	fmt = &ipu_ic_formats[index];
> +	*desc = fmt->name;
> +	*fourcc = fmt->fourcc;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
> +
> +static void ipu_ic_free_dma_buf(struct ipu_ic_priv *priv,
> +				struct ipu_ic_dma_buf *buf)
> +{
> +	if (buf->virt)
> +		dma_free_coherent(priv->ipu->dev,
> +				  buf->len, buf->virt, buf->phys);
> +	buf->virt = NULL;
> +	buf->phys = 0;
> +}
> +
> +static int ipu_ic_alloc_dma_buf(struct ipu_ic_priv *priv,
> +				struct ipu_ic_dma_buf *buf,
> +				int size)
> +{
> +	unsigned long newlen = PAGE_ALIGN(size);
> +
> +	if (buf->virt) {
> +		if (buf->len == newlen)
> +			return 0;
> +		ipu_ic_free_dma_buf(priv, buf);
> +	}
> +
> +	buf->len = newlen;
> +	buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
> +				       GFP_DMA | GFP_KERNEL);
> +	if (!buf->virt) {
> +		dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline int ipu_ic_num_stripes(int dim)
> +{
> +	if (dim <= 1024)
> +		return 1;
> +	else if (dim <= 2048)
> +		return 2;
> +	else
> +		return 4;
> +}
> +
> +static void ipu_ic_calc_tile_dimensions(struct image_converter_ctx *ctx,
> +					struct ipu_ic_image *image)
> +{
> +	struct ipu_ic_tile *tile = &image->tile;
> +
> +	tile->height = image->base.pix.height / image->num_rows;
> +	tile->width = image->base.pix.width / image->num_cols;
> +	tile->size = ((tile->height * image->fmt->bpp) >> 3) * tile->width;
> +
> +	if (image->fmt->y_depth) {
> +		tile->stride = (image->fmt->y_depth * tile->width) >> 3;
> +		tile->rot_stride = (image->fmt->y_depth * tile->height) >> 3;
> +	} else {
> +		tile->stride = (image->fmt->bpp * tile->width) >> 3;
> +		tile->rot_stride = (image->fmt->bpp * tile->height) >> 3;
> +	}
> +}
> +
> +/*
> + * Use the rotation transformation to find the tile coordinates
> + * (row, col) of a tile in the destination frame that corresponds
> + * to the given tile coordinates of a source frame. The destination
> + * coordinate is then converted to a tile index.
> + */
> +static int ipu_ic_transform_tile_index(struct image_converter_ctx *ctx,
> +				       int src_row, int src_col)
> +{

We don't do image rotation in software inside the Kernel! This is
something that should be done either by some hardware block or
in userspace.

> +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> +	struct ipu_ic_image *s_image = &ctx->in;
> +	struct ipu_ic_image *d_image = &ctx->out;
> +	int cos, sin, dst_row, dst_col;
> +
> +	/* with no rotation it's a 1:1 mapping */
> +	if (ctx->rot_mode == IPU_ROTATE_NONE)
> +		return src_row * s_image->num_cols + src_col;
> +
> +	if (ctx->rot_mode & IPU_ROT_BIT_90) {
> +		cos = 0;
> +		sin = 1;
> +	} else {
> +		cos = 1;
> +		sin = 0;
> +	}
> +
> +	/*
> +	 * before doing the transform, first we have to translate
> +	 * source row,col for an origin in the center of s_image
> +	 */
> +	src_row *= 2;
> +	src_col *= 2;
> +	src_row -= s_image->num_rows - 1;
> +	src_col -= s_image->num_cols - 1;
> +
> +	/* do the rotation transform */
> +	dst_col = src_col * cos - src_row * sin;
> +	dst_row = src_col * sin + src_row * cos;
> +
> +	/* apply flip */
> +	if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
> +		dst_col = -dst_col;
> +	if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
> +		dst_row = -dst_row;
> +
> +	dev_dbg(priv->ipu->dev, "ctx %p: [%d,%d] --> [%d,%d]\n",
> +		ctx, src_col, src_row, dst_col, dst_row);
> +
> +	/*
> +	 * finally translate dest row,col using an origin in upper
> +	 * left of d_image
> +	 */
> +	dst_row += d_image->num_rows - 1;
> +	dst_col += d_image->num_cols - 1;
> +	dst_row /= 2;
> +	dst_col /= 2;
> +
> +	return dst_row * d_image->num_cols + dst_col;
> +}
> +
> +/*
> + * Fill the out_tile_map[] with transformed destination tile indeces.
> + */
> +static void ipu_ic_calc_out_tile_map(struct image_converter_ctx *ctx)
> +{
> +	struct ipu_ic_image *s_image = &ctx->in;
> +	unsigned int row, col, tile = 0;
> +
> +	for (row = 0; row < s_image->num_rows; row++) {
> +		for (col = 0; col < s_image->num_cols; col++) {
> +			ctx->out_tile_map[tile] =
> +				ipu_ic_transform_tile_index(ctx, row, col);
> +			tile++;
> +		}
> +	}
> +}
> +
> +static void ipu_ic_calc_tile_offsets_planar(struct image_converter_ctx *ctx,
> +					    struct ipu_ic_image *image)
> +{

We also don't do image conversions inside the Kernel. Same applies
to other similar codes on this patch.

> +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> +	const struct ipu_ic_pixfmt *fmt = image->fmt;
> +	unsigned int row, col, tile = 0;
> +	u32 H, w, h, y_depth, y_stride, uv_stride;
> +	u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
> +	u32 y_row_off, y_col_off, y_off;
> +	u32 y_size, uv_size;
> +
> +	/* setup some convenience vars */
> +	H = image->base.pix.height;
> +	w = image->tile.width;
> +	h = image->tile.height;
> +
> +	y_depth = fmt->y_depth;
> +	y_stride = image->stride;
> +	uv_stride = y_stride / fmt->uv_width_dec;
> +	if (fmt->uv_packed)
> +		uv_stride *= 2;
> +
> +	y_size = H * y_stride;
> +	uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
> +
> +	for (row = 0; row < image->num_rows; row++) {
> +		y_row_off = row * h * y_stride;
> +		uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
> +
> +		for (col = 0; col < image->num_cols; col++) {
> +			y_col_off = (col * w * y_depth) >> 3;
> +			uv_col_off = y_col_off / fmt->uv_width_dec;
> +			if (fmt->uv_packed)
> +				uv_col_off *= 2;
> +
> +			y_off = y_row_off + y_col_off;
> +			uv_off = uv_row_off + uv_col_off;
> +
> +			u_off = y_size - y_off + uv_off;
> +			v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
> +			if (fmt->uv_swapped) {
> +				tmp = u_off;
> +				u_off = v_off;
> +				v_off = tmp;
> +			}
> +
> +			image->tile_off[tile].offset = y_off;
> +			image->tile_off[tile].u_off = u_off;
> +			image->tile_off[tile++].v_off = v_off;
> +
> +			dev_dbg(priv->ipu->dev,
> +				"ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
> +				ctx, image->type == IMAGE_CONVERT_IN ?
> +				"Input" : "Output", row, col,
> +				y_off, u_off, v_off);
> +		}
> +	}
> +}
> +
> +static void ipu_ic_calc_tile_offsets_packed(struct image_converter_ctx *ctx,
> +					    struct ipu_ic_image *image)
> +{
> +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> +	const struct ipu_ic_pixfmt *fmt = image->fmt;
> +	unsigned int row, col, tile = 0;
> +	u32 w, h, bpp, stride;
> +	u32 row_off, col_off;
> +
> +	/* setup some convenience vars */
> +	w = image->tile.width;
> +	h = image->tile.height;
> +	stride = image->stride;
> +	bpp = fmt->bpp;
> +
> +	for (row = 0; row < image->num_rows; row++) {
> +		row_off = row * h * stride;
> +
> +		for (col = 0; col < image->num_cols; col++) {
> +			col_off = (col * w * bpp) >> 3;
> +
> +			image->tile_off[tile].offset = row_off + col_off;
> +			image->tile_off[tile].u_off = 0;
> +			image->tile_off[tile++].v_off = 0;
> +
> +			dev_dbg(priv->ipu->dev,
> +				"ctx %p: %s@[%d,%d]: phys %08x\n", ctx,
> +				image->type == IMAGE_CONVERT_IN ?
> +				"Input" : "Output", row, col,
> +				row_off + col_off);
> +		}
> +	}
> +}
> +
> +static void ipu_ic_calc_tile_offsets(struct image_converter_ctx *ctx,
> +				     struct ipu_ic_image *image)
> +{
> +	memset(image->tile_off, 0, sizeof(image->tile_off));
> +
> +	if (image->fmt->y_depth)
> +		ipu_ic_calc_tile_offsets_planar(ctx, image);
> +	else
> +		ipu_ic_calc_tile_offsets_packed(ctx, image);
> +}
> +
> +/*
> + * return the number of runs in given queue (pending_q or done_q)
> + * for this context. hold irqlock when calling.
> + */
> +static int ipu_ic_get_run_count(struct image_converter_ctx *ctx,
> +				struct list_head *q)
> +{
> +	struct image_converter_run *run;
> +	int count = 0;
> +
> +	list_for_each_entry(run, q, list) {
> +		if (run->ctx == ctx)
> +			count++;
> +	}
> +
> +	return count;
> +}
> +
> +/* hold irqlock when calling */
> +static void ipu_ic_convert_stop(struct image_converter_run *run)
> +{
> +	struct image_converter_ctx *ctx = run->ctx;
> +	struct image_converter *cvt = ctx->cvt;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +
> +	dev_dbg(priv->ipu->dev, "%s: stopping ctx %p run %p\n",
> +		__func__, ctx, run);
> +
> +	/* disable IC tasks and the channels */
> +	ipu_ic_task_disable(cvt->ic);
> +	ipu_idmac_disable_channel(cvt->in_chan);
> +	ipu_idmac_disable_channel(cvt->out_chan);
> +
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		ipu_idmac_disable_channel(cvt->rotation_in_chan);
> +		ipu_idmac_disable_channel(cvt->rotation_out_chan);
> +		ipu_idmac_unlink(cvt->out_chan, cvt->rotation_in_chan);
> +	}
> +
> +	ipu_ic_disable(cvt->ic);
> +}
> +
> +/* hold irqlock when calling */
> +static void init_idmac_channel(struct image_converter_ctx *ctx,
> +			       struct ipuv3_channel *channel,
> +			       struct ipu_ic_image *image,
> +			       enum ipu_rotate_mode rot_mode,
> +			       bool rot_swap_width_height)
> +{
> +	struct image_converter *cvt = ctx->cvt;
> +	unsigned int burst_size;
> +	u32 width, height, stride;
> +	dma_addr_t addr0, addr1 = 0;
> +	struct ipu_image tile_image;
> +	unsigned int tile_idx[2];
> +
> +	if (image->type == IMAGE_CONVERT_OUT) {
> +		tile_idx[0] = ctx->out_tile_map[0];
> +		tile_idx[1] = ctx->out_tile_map[1];
> +	} else {
> +		tile_idx[0] = 0;
> +		tile_idx[1] = 1;
> +	}
> +
> +	if (rot_swap_width_height) {
> +		width = image->tile.height;
> +		height = image->tile.width;
> +		stride = image->tile.rot_stride;
> +		addr0 = ctx->rot_intermediate[0].phys;
> +		if (ctx->double_buffering)
> +			addr1 = ctx->rot_intermediate[1].phys;
> +	} else {
> +		width = image->tile.width;
> +		height = image->tile.height;
> +		stride = image->stride;
> +		addr0 = image->base.phys0 +
> +			image->tile_off[tile_idx[0]].offset;
> +		if (ctx->double_buffering)
> +			addr1 = image->base.phys0 +
> +				image->tile_off[tile_idx[1]].offset;
> +	}
> +
> +	ipu_cpmem_zero(channel);
> +
> +	memset(&tile_image, 0, sizeof(tile_image));
> +	tile_image.pix.width = tile_image.rect.width = width;
> +	tile_image.pix.height = tile_image.rect.height = height;
> +	tile_image.pix.bytesperline = stride;
> +	tile_image.pix.pixelformat =  image->fmt->fourcc;
> +	tile_image.phys0 = addr0;
> +	tile_image.phys1 = addr1;
> +	ipu_cpmem_set_image(channel, &tile_image);
> +
> +	if (image->fmt->y_depth && !rot_swap_width_height)
> +		ipu_cpmem_set_uv_offset(channel,
> +					image->tile_off[tile_idx[0]].u_off,
> +					image->tile_off[tile_idx[0]].v_off);
> +
> +	if (rot_mode)
> +		ipu_cpmem_set_rotation(channel, rot_mode);
> +
> +	if (channel == cvt->rotation_in_chan ||
> +	    channel == cvt->rotation_out_chan) {
> +		burst_size = 8;
> +		ipu_cpmem_set_block_mode(channel);
> +	} else
> +		burst_size = (width % 16) ? 8 : 16;
> +
> +	ipu_cpmem_set_burstsize(channel, burst_size);
> +
> +	ipu_ic_task_idma_init(cvt->ic, channel, width, height,
> +			      burst_size, rot_mode);
> +
> +	ipu_cpmem_set_axi_id(channel, 1);
> +
> +	ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
> +}
> +
> +/* hold irqlock when calling */
> +static int ipu_ic_convert_start(struct image_converter_run *run)
> +{
> +	struct image_converter_ctx *ctx = run->ctx;
> +	struct image_converter *cvt = ctx->cvt;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	struct ipu_ic_image *s_image = &ctx->in;
> +	struct ipu_ic_image *d_image = &ctx->out;
> +	enum ipu_color_space src_cs, dest_cs;
> +	unsigned int dest_width, dest_height;
> +	int ret;
> +
> +	dev_dbg(priv->ipu->dev, "%s: starting ctx %p run %p\n",
> +		__func__, ctx, run);
> +
> +	src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
> +	dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
> +
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		/* swap width/height for resizer */
> +		dest_width = d_image->tile.height;
> +		dest_height = d_image->tile.width;
> +	} else {
> +		dest_width = d_image->tile.width;
> +		dest_height = d_image->tile.height;
> +	}
> +
> +	/* setup the IC resizer and CSC */
> +	ret = ipu_ic_task_init(cvt->ic,
> +			       s_image->tile.width,
> +			       s_image->tile.height,
> +			       dest_width,
> +			       dest_height,
> +			       src_cs, dest_cs);
> +	if (ret) {
> +		dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* init the source MEM-->IC PP IDMAC channel */
> +	init_idmac_channel(ctx, cvt->in_chan, s_image,
> +			   IPU_ROTATE_NONE, false);
> +
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		/* init the IC PP-->MEM IDMAC channel */
> +		init_idmac_channel(ctx, cvt->out_chan, d_image,
> +				   IPU_ROTATE_NONE, true);
> +
> +		/* init the MEM-->IC PP ROT IDMAC channel */
> +		init_idmac_channel(ctx, cvt->rotation_in_chan, d_image,
> +				   ctx->rot_mode, true);
> +
> +		/* init the destination IC PP ROT-->MEM IDMAC channel */
> +		init_idmac_channel(ctx, cvt->rotation_out_chan, d_image,
> +				   IPU_ROTATE_NONE, false);
> +
> +		/* now link IC PP-->MEM to MEM-->IC PP ROT */
> +		ipu_idmac_link(cvt->out_chan, cvt->rotation_in_chan);
> +	} else {
> +		/* init the destination IC PP-->MEM IDMAC channel */
> +		init_idmac_channel(ctx, cvt->out_chan, d_image,
> +				   ctx->rot_mode, false);
> +	}
> +
> +	/* enable the IC */
> +	ipu_ic_enable(cvt->ic);
> +
> +	/* set buffers ready */
> +	ipu_idmac_select_buffer(cvt->in_chan, 0);
> +	ipu_idmac_select_buffer(cvt->out_chan, 0);
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode))
> +		ipu_idmac_select_buffer(cvt->rotation_out_chan, 0);
> +	if (ctx->double_buffering) {
> +		ipu_idmac_select_buffer(cvt->in_chan, 1);
> +		ipu_idmac_select_buffer(cvt->out_chan, 1);
> +		if (ipu_rot_mode_is_irt(ctx->rot_mode))
> +			ipu_idmac_select_buffer(cvt->rotation_out_chan, 1);
> +	}
> +
> +	/* enable the channels! */
> +	ipu_idmac_enable_channel(cvt->in_chan);
> +	ipu_idmac_enable_channel(cvt->out_chan);
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		ipu_idmac_enable_channel(cvt->rotation_in_chan);
> +		ipu_idmac_enable_channel(cvt->rotation_out_chan);
> +	}
> +
> +	ipu_ic_task_enable(cvt->ic);
> +
> +	ipu_cpmem_dump(cvt->in_chan);
> +	ipu_cpmem_dump(cvt->out_chan);
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		ipu_cpmem_dump(cvt->rotation_in_chan);
> +		ipu_cpmem_dump(cvt->rotation_out_chan);
> +	}
> +
> +	ipu_dump(priv->ipu);
> +
> +	return 0;
> +}
> +
> +/* hold irqlock when calling */
> +static int ipu_ic_run(struct image_converter_run *run)
> +{
> +	struct image_converter_ctx *ctx = run->ctx;
> +	struct image_converter *cvt = ctx->cvt;
> +
> +	ctx->in.base.phys0 = run->in_phys;
> +	ctx->out.base.phys0 = run->out_phys;
> +
> +	ctx->cur_buf_num = 0;
> +	ctx->next_tile = 1;
> +
> +	/* remove run from pending_q and set as current */
> +	list_del(&run->list);
> +	cvt->current_run = run;
> +
> +	return ipu_ic_convert_start(run);
> +}
> +
> +/* hold irqlock when calling */
> +static void ipu_ic_run_next(struct image_converter *cvt)
> +{
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	struct image_converter_run *run, *tmp;
> +	int ret;
> +
> +	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
> +		/* skip contexts that are aborting */
> +		if (run->ctx->aborting) {
> +			dev_dbg(priv->ipu->dev,
> +				 "%s: skipping aborting ctx %p run %p\n",
> +				 __func__, run->ctx, run);
> +			continue;
> +		}
> +
> +		ret = ipu_ic_run(run);
> +		if (!ret)
> +			break;
> +
> +		/*
> +		 * something went wrong with start, add the run
> +		 * to done q and continue to the next run in the
> +		 * pending q.
> +		 */
> +		run->status = ret;
> +		list_add_tail(&run->list, &cvt->done_q);
> +		cvt->current_run = NULL;
> +	}
> +}
> +
> +static void ipu_ic_empty_done_q(struct image_converter *cvt)
> +{
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	struct image_converter_run *run;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	while (!list_empty(&cvt->done_q)) {
> +		run = list_entry(cvt->done_q.next,
> +				 struct image_converter_run,
> +				 list);
> +
> +		list_del(&run->list);
> +
> +		dev_dbg(priv->ipu->dev,
> +			"%s: completing ctx %p run %p with %d\n",
> +			__func__, run->ctx, run, run->status);
> +
> +		/* call the completion callback and free the run */
> +		spin_unlock_irqrestore(&cvt->irqlock, flags);
> +		run->ctx->complete(run->ctx->complete_context, run,
> +				   run->status);
> +		kfree(run);
> +		spin_lock_irqsave(&cvt->irqlock, flags);
> +	}
> +
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +}
> +
> +/*
> + * the bottom half thread clears out the done_q, calling the
> + * completion handler for each.
> + */
> +static irqreturn_t ipu_ic_bh(int irq, void *dev_id)
> +{
> +	struct image_converter *cvt = dev_id;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	struct image_converter_ctx *ctx;
> +	unsigned long flags;
> +
> +	dev_dbg(priv->ipu->dev, "%s: enter\n", __func__);
> +
> +	ipu_ic_empty_done_q(cvt);
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	/*
> +	 * the done_q is cleared out, signal any contexts
> +	 * that are aborting that abort can complete.
> +	 */
> +	list_for_each_entry(ctx, &cvt->ctx_list, list) {
> +		if (ctx->aborting) {
> +			dev_dbg(priv->ipu->dev,
> +				 "%s: signaling abort for ctx %p\n",
> +				 __func__, ctx);
> +			complete(&ctx->aborted);
> +		}
> +	}
> +
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +
> +	dev_dbg(priv->ipu->dev, "%s: exit\n", __func__);
> +	return IRQ_HANDLED;
> +}
> +
> +/* hold irqlock when calling */
> +static irqreturn_t ipu_ic_doirq(struct image_converter_run *run)
> +{
> +	struct image_converter_ctx *ctx = run->ctx;
> +	struct image_converter *cvt = ctx->cvt;
> +	struct ipu_ic_tile_off *src_off, *dst_off;
> +	struct ipu_ic_image *s_image = &ctx->in;
> +	struct ipu_ic_image *d_image = &ctx->out;
> +	struct ipuv3_channel *outch;
> +	unsigned int dst_idx;
> +
> +	outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
> +		cvt->rotation_out_chan : cvt->out_chan;
> +
> +	/*
> +	 * It is difficult to stop the channel DMA before the channels
> +	 * enter the paused state. Without double-buffering the channels
> +	 * are always in a paused state when the EOF irq occurs, so it
> +	 * is safe to stop the channels now. For double-buffering we
> +	 * just ignore the abort until the operation completes, when it
> +	 * is safe to shut down.
> +	 */
> +	if (ctx->aborting && !ctx->double_buffering) {
> +		ipu_ic_convert_stop(run);
> +		run->status = -EIO;
> +		goto done;
> +	}
> +
> +	if (ctx->next_tile == ctx->num_tiles) {
> +		/*
> +		 * the conversion is complete
> +		 */
> +		ipu_ic_convert_stop(run);
> +		run->status = 0;
> +		goto done;
> +	}
> +
> +	/*
> +	 * not done, place the next tile buffers.
> +	 */
> +	if (!ctx->double_buffering) {
> +
> +		src_off = &s_image->tile_off[ctx->next_tile];
> +		dst_idx = ctx->out_tile_map[ctx->next_tile];
> +		dst_off = &d_image->tile_off[dst_idx];
> +
> +		ipu_cpmem_set_buffer(cvt->in_chan, 0,
> +				     s_image->base.phys0 + src_off->offset);
> +		ipu_cpmem_set_buffer(outch, 0,
> +				     d_image->base.phys0 + dst_off->offset);
> +		if (s_image->fmt->y_depth)
> +			ipu_cpmem_set_uv_offset(cvt->in_chan,
> +						src_off->u_off,
> +						src_off->v_off);
> +		if (d_image->fmt->y_depth)
> +			ipu_cpmem_set_uv_offset(outch,
> +						dst_off->u_off,
> +						dst_off->v_off);
> +
> +		ipu_idmac_select_buffer(cvt->in_chan, 0);
> +		ipu_idmac_select_buffer(outch, 0);
> +
> +	} else if (ctx->next_tile < ctx->num_tiles - 1) {
> +
> +		src_off = &s_image->tile_off[ctx->next_tile + 1];
> +		dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
> +		dst_off = &d_image->tile_off[dst_idx];
> +
> +		ipu_cpmem_set_buffer(cvt->in_chan, ctx->cur_buf_num,
> +				     s_image->base.phys0 + src_off->offset);
> +		ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
> +				     d_image->base.phys0 + dst_off->offset);
> +
> +		ipu_idmac_select_buffer(cvt->in_chan, ctx->cur_buf_num);
> +		ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
> +
> +		ctx->cur_buf_num ^= 1;
> +	}
> +
> +	ctx->next_tile++;
> +	return IRQ_HANDLED;
> +done:
> +	list_add_tail(&run->list, &cvt->done_q);
> +	cvt->current_run = NULL;
> +	ipu_ic_run_next(cvt);
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t ipu_ic_norotate_irq(int irq, void *data)
> +{
> +	struct image_converter *cvt = data;
> +	struct image_converter_ctx *ctx;
> +	struct image_converter_run *run;
> +	unsigned long flags;
> +	irqreturn_t ret;
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	/* get current run and its context */
> +	run = cvt->current_run;
> +	if (!run) {
> +		ret = IRQ_NONE;
> +		goto out;
> +	}
> +
> +	ctx = run->ctx;
> +
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		/* this is a rotation operation, just ignore */
> +		spin_unlock_irqrestore(&cvt->irqlock, flags);
> +		return IRQ_HANDLED;
> +	}
> +
> +	ret = ipu_ic_doirq(run);
> +out:
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +	return ret;
> +}
> +
> +static irqreturn_t ipu_ic_rotate_irq(int irq, void *data)
> +{
> +	struct image_converter *cvt = data;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	struct image_converter_ctx *ctx;
> +	struct image_converter_run *run;
> +	unsigned long flags;
> +	irqreturn_t ret;
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	/* get current run and its context */
> +	run = cvt->current_run;
> +	if (!run) {
> +		ret = IRQ_NONE;
> +		goto out;
> +	}
> +
> +	ctx = run->ctx;
> +
> +	if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		/* this was NOT a rotation operation, shouldn't happen */
> +		dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
> +		spin_unlock_irqrestore(&cvt->irqlock, flags);
> +		return IRQ_HANDLED;
> +	}
> +
> +	ret = ipu_ic_doirq(run);
> +out:
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +	return ret;
> +}
> +
> +/*
> + * try to force the completion of runs for this ctx. Called when
> + * abort wait times out in ipu_image_convert_abort().
> + */
> +static void ipu_ic_force_abort(struct image_converter_ctx *ctx)
> +{
> +	struct image_converter *cvt = ctx->cvt;
> +	struct image_converter_run *run;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	run = cvt->current_run;
> +	if (run && run->ctx == ctx) {
> +		ipu_ic_convert_stop(run);
> +		run->status = -EIO;
> +		list_add_tail(&run->list, &cvt->done_q);
> +		cvt->current_run = NULL;
> +		ipu_ic_run_next(cvt);
> +	}
> +
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +
> +	ipu_ic_empty_done_q(cvt);
> +}
> +
> +static void ipu_ic_release_ipu_resources(struct image_converter *cvt)
> +{
> +	if (cvt->out_eof_irq >= 0)
> +		free_irq(cvt->out_eof_irq, cvt);
> +	if (cvt->rot_out_eof_irq >= 0)
> +		free_irq(cvt->rot_out_eof_irq, cvt);
> +
> +	if (!IS_ERR_OR_NULL(cvt->in_chan))
> +		ipu_idmac_put(cvt->in_chan);
> +	if (!IS_ERR_OR_NULL(cvt->out_chan))
> +		ipu_idmac_put(cvt->out_chan);
> +	if (!IS_ERR_OR_NULL(cvt->rotation_in_chan))
> +		ipu_idmac_put(cvt->rotation_in_chan);
> +	if (!IS_ERR_OR_NULL(cvt->rotation_out_chan))
> +		ipu_idmac_put(cvt->rotation_out_chan);
> +
> +	cvt->in_chan = cvt->out_chan = cvt->rotation_in_chan =
> +		cvt->rotation_out_chan = NULL;
> +	cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
> +}
> +
> +static int ipu_ic_get_ipu_resources(struct image_converter *cvt)
> +{
> +	const struct ic_task_channels *chan = cvt->ic->ch;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	int ret;
> +
> +	/* get IDMAC channels */
> +	cvt->in_chan = ipu_idmac_get(priv->ipu, chan->in);
> +	cvt->out_chan = ipu_idmac_get(priv->ipu, chan->out);
> +	if (IS_ERR(cvt->in_chan) || IS_ERR(cvt->out_chan)) {
> +		dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
> +		ret = -EBUSY;
> +		goto err;
> +	}
> +
> +	cvt->rotation_in_chan = ipu_idmac_get(priv->ipu, chan->rot_in);
> +	cvt->rotation_out_chan = ipu_idmac_get(priv->ipu, chan->rot_out);
> +	if (IS_ERR(cvt->rotation_in_chan) || IS_ERR(cvt->rotation_out_chan)) {
> +		dev_err(priv->ipu->dev,
> +			"could not acquire idmac rotation channels\n");
> +		ret = -EBUSY;
> +		goto err;
> +	}
> +
> +	/* acquire the EOF interrupts */
> +	cvt->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
> +						cvt->out_chan,
> +						IPU_IRQ_EOF);
> +
> +	ret = request_threaded_irq(cvt->out_eof_irq,
> +				   ipu_ic_norotate_irq, ipu_ic_bh,
> +				   0, "ipu-ic", cvt);
> +	if (ret < 0) {
> +		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
> +			 cvt->out_eof_irq);
> +		cvt->out_eof_irq = -1;
> +		goto err;
> +	}
> +
> +	cvt->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
> +						     cvt->rotation_out_chan,
> +						     IPU_IRQ_EOF);
> +
> +	ret = request_threaded_irq(cvt->rot_out_eof_irq,
> +				   ipu_ic_rotate_irq, ipu_ic_bh,
> +				   0, "ipu-ic", cvt);
> +	if (ret < 0) {
> +		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
> +			cvt->rot_out_eof_irq);
> +		cvt->rot_out_eof_irq = -1;
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	ipu_ic_release_ipu_resources(cvt);
> +	return ret;
> +}
> +
> +static int ipu_ic_fill_image(struct image_converter_ctx *ctx,
> +			     struct ipu_ic_image *ic_image,
> +			     struct ipu_image *image,
> +			     enum image_convert_type type)
> +{
> +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> +
> +	ic_image->base = *image;
> +	ic_image->type = type;
> +
> +	ic_image->fmt = ipu_ic_get_format(image->pix.pixelformat);
> +	if (!ic_image->fmt) {
> +		dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
> +			type == IMAGE_CONVERT_OUT ? "Output" : "Input");
> +		return -EINVAL;
> +	}
> +
> +	if (ic_image->fmt->y_depth)
> +		ic_image->stride = (ic_image->fmt->y_depth *
> +				    ic_image->base.pix.width) >> 3;
> +	else
> +		ic_image->stride  = ic_image->base.pix.bytesperline;
> +
> +	ipu_ic_calc_tile_dimensions(ctx, ic_image);
> +	ipu_ic_calc_tile_offsets(ctx, ic_image);
> +
> +	return 0;
> +}
> +
> +/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
> +static unsigned int clamp_align(unsigned int x, unsigned int min,
> +				unsigned int max, unsigned int align)
> +{
> +	/* Bits that must be zero to be aligned */
> +	unsigned int mask = ~((1 << align) - 1);
> +
> +	/* Clamp to aligned min and max */
> +	x = clamp(x, (min + ~mask) & mask, max & mask);
> +
> +	/* Round to nearest aligned value */
> +	if (align)
> +		x = (x + (1 << (align - 1))) & mask;
> +
> +	return x;
> +}
> +
> +/*
> + * We have to adjust the tile width such that the tile physaddrs and
> + * U and V plane offsets are multiples of 8 bytes as required by
> + * the IPU DMA Controller. For the planar formats, this corresponds
> + * to a pixel alignment of 16 (but use a more formal equation since
> + * the variables are available). For all the packed formats, 8 is
> + * good enough.
> + */
> +static inline u32 tile_width_align(const struct ipu_ic_pixfmt *fmt)
> +{
> +	return fmt->y_depth ? (64 * fmt->uv_width_dec) / fmt->y_depth : 8;
> +}
> +
> +/*
> + * For tile height alignment, we have to ensure that the output tile
> + * heights are multiples of 8 lines if the IRT is required by the
> + * given rotation mode (the IRT performs rotations on 8x8 blocks
> + * at a time). If the IRT is not used, or for input image tiles,
> + * 2 lines are good enough.
> + */
> +static inline u32 tile_height_align(enum image_convert_type type,
> +				    enum ipu_rotate_mode rot_mode)
> +{
> +	return (type == IMAGE_CONVERT_OUT &&
> +		ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
> +}
> +
> +/* Adjusts input/output images to IPU restrictions */
> +int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
> +			     enum ipu_rotate_mode rot_mode)
> +{
> +	const struct ipu_ic_pixfmt *infmt, *outfmt;
> +	unsigned int num_in_rows, num_in_cols;
> +	unsigned int num_out_rows, num_out_cols;
> +	u32 w_align, h_align;
> +
> +	infmt = ipu_ic_get_format(in->pix.pixelformat);
> +	outfmt = ipu_ic_get_format(out->pix.pixelformat);
> +
> +	/* set some defaults if needed */
> +	if (!infmt) {
> +		in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
> +		infmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
> +	}
> +	if (!outfmt) {
> +		out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
> +		outfmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
> +	}
> +
> +	if (!in->pix.width || !in->pix.height) {
> +		in->pix.width = 640;
> +		in->pix.height = 480;
> +	}
> +	if (!out->pix.width || !out->pix.height) {
> +		out->pix.width = 640;
> +		out->pix.height = 480;
> +	}
> +
> +	/* image converter does not handle fields */
> +	in->pix.field = out->pix.field = V4L2_FIELD_NONE;
> +
> +	/* resizer cannot downsize more than 4:1 */
> +	if (ipu_rot_mode_is_irt(rot_mode)) {
> +		out->pix.height = max_t(__u32, out->pix.height,
> +					in->pix.width / 4);
> +		out->pix.width = max_t(__u32, out->pix.width,
> +				       in->pix.height / 4);
> +	} else {
> +		out->pix.width = max_t(__u32, out->pix.width,
> +				       in->pix.width / 4);
> +		out->pix.height = max_t(__u32, out->pix.height,
> +					in->pix.height / 4);
> +	}
> +
> +	/* get tiling rows/cols from output format */
> +	num_out_rows = ipu_ic_num_stripes(out->pix.height);
> +	num_out_cols = ipu_ic_num_stripes(out->pix.width);
> +	if (ipu_rot_mode_is_irt(rot_mode)) {
> +		num_in_rows = num_out_cols;
> +		num_in_cols = num_out_rows;
> +	} else {
> +		num_in_rows = num_out_rows;
> +		num_in_cols = num_out_cols;
> +	}
> +
> +	/* align input width/height */
> +	w_align = ilog2(tile_width_align(infmt) * num_in_cols);
> +	h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
> +			num_in_rows);
> +	in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
> +	in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
> +
> +	/* align output width/height */
> +	w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
> +	h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
> +			num_out_rows);
> +	out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
> +	out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
> +
> +	/* set input/output strides and image sizes */
> +	in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
> +	in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
> +	out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
> +	out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
> +
> +/*
> + * this is used by ipu_image_convert_prepare() to verify set input and
> + * output images are valid before starting the conversion. Clients can
> + * also call it before calling ipu_image_convert_prepare().
> + */
> +int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
> +			     enum ipu_rotate_mode rot_mode)
> +{
> +	struct ipu_image testin, testout;
> +	int ret;
> +
> +	testin = *in;
> +	testout = *out;
> +
> +	ret = ipu_image_convert_adjust(&testin, &testout, rot_mode);
> +	if (ret)
> +		return ret;
> +
> +	if (testin.pix.width != in->pix.width ||
> +	    testin.pix.height != in->pix.height ||
> +	    testout.pix.width != out->pix.width ||
> +	    testout.pix.height != out->pix.height)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
> +
> +/*
> + * Call ipu_image_convert_prepare() to prepare for the conversion of
> + * given images and rotation mode. Returns a new conversion context.
> + */
> +struct image_converter_ctx *
> +ipu_image_convert_prepare(struct ipu_ic *ic,
> +			  struct ipu_image *in, struct ipu_image *out,
> +			  enum ipu_rotate_mode rot_mode,
> +			  image_converter_cb_t complete,
> +			  void *complete_context)
> +{
> +	struct ipu_ic_priv *priv = ic->priv;
> +	struct image_converter *cvt = &ic->cvt;
> +	struct ipu_ic_image *s_image, *d_image;
> +	struct image_converter_ctx *ctx;
> +	unsigned long flags;
> +	bool get_res;
> +	int ret;
> +
> +	if (!ic || !in || !out || !complete)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* verify the in/out images before continuing */
> +	ret = ipu_image_convert_verify(in, out, rot_mode);
> +	if (ret) {
> +		dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
> +			__func__);
> +		return ERR_PTR(ret);
> +	}
> +
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return ERR_PTR(-ENOMEM);
> +
> +	dev_dbg(priv->ipu->dev, "%s: ctx %p\n", __func__, ctx);
> +
> +	ctx->cvt = cvt;
> +	init_completion(&ctx->aborted);
> +
> +	s_image = &ctx->in;
> +	d_image = &ctx->out;
> +
> +	/* set tiling and rotation */
> +	d_image->num_rows = ipu_ic_num_stripes(out->pix.height);
> +	d_image->num_cols = ipu_ic_num_stripes(out->pix.width);
> +	if (ipu_rot_mode_is_irt(rot_mode)) {
> +		s_image->num_rows = d_image->num_cols;
> +		s_image->num_cols = d_image->num_rows;
> +	} else {
> +		s_image->num_rows = d_image->num_rows;
> +		s_image->num_cols = d_image->num_cols;
> +	}
> +
> +	ctx->num_tiles = d_image->num_cols * d_image->num_rows;
> +	ctx->rot_mode = rot_mode;
> +
> +	ret = ipu_ic_fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
> +	if (ret)
> +		goto out_free;
> +	ret = ipu_ic_fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
> +	if (ret)
> +		goto out_free;
> +
> +	ipu_ic_calc_out_tile_map(ctx);
> +
> +	ipu_ic_dump_format(ctx, s_image);
> +	ipu_ic_dump_format(ctx, d_image);
> +
> +	ctx->complete = complete;
> +	ctx->complete_context = complete_context;
> +
> +	/*
> +	 * Can we use double-buffering for this operation? If there is
> +	 * only one tile (the whole image can be converted in a single
> +	 * operation) there's no point in using double-buffering. Also,
> +	 * the IPU's IDMAC channels allow only a single U and V plane
> +	 * offset shared between both buffers, but these offsets change
> +	 * for every tile, and therefore would have to be updated for
> +	 * each buffer which is not possible. So double-buffering is
> +	 * impossible when either the source or destination images are
> +	 * a planar format (YUV420, YUV422P, etc.).
> +	 */
> +	ctx->double_buffering = (ctx->num_tiles > 1 &&
> +				 !s_image->fmt->y_depth &&
> +				 !d_image->fmt->y_depth);
> +
> +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> +		ret = ipu_ic_alloc_dma_buf(priv, &ctx->rot_intermediate[0],
> +					   d_image->tile.size);
> +		if (ret)
> +			goto out_free;
> +		if (ctx->double_buffering) {
> +			ret = ipu_ic_alloc_dma_buf(priv,
> +						   &ctx->rot_intermediate[1],
> +						   d_image->tile.size);
> +			if (ret)
> +				goto out_free_dmabuf0;
> +		}
> +	}
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	get_res = list_empty(&cvt->ctx_list);
> +
> +	list_add_tail(&ctx->list, &cvt->ctx_list);
> +
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +
> +	if (get_res) {
> +		ret = ipu_ic_get_ipu_resources(cvt);
> +		if (ret)
> +			goto out_free_dmabuf1;
> +	}
> +
> +	return ctx;
> +
> +out_free_dmabuf1:
> +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +	list_del(&ctx->list);
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +out_free_dmabuf0:
> +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
> +out_free:
> +	kfree(ctx);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
> +
> +/*
> + * Carry out a single image conversion. Only the physaddr's of the input
> + * and output image buffers are needed. The conversion context must have
> + * been created previously with ipu_image_convert_prepare(). Returns the
> + * new run object.
> + */
> +struct image_converter_run *
> +ipu_image_convert_run(struct image_converter_ctx *ctx,
> +		      dma_addr_t in_phys, dma_addr_t out_phys)
> +{
> +	struct image_converter *cvt = ctx->cvt;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	struct image_converter_run *run;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	run = kzalloc(sizeof(*run), GFP_KERNEL);
> +	if (!run)
> +		return ERR_PTR(-ENOMEM);
> +
> +	run->ctx = ctx;
> +	run->in_phys = in_phys;
> +	run->out_phys = out_phys;
> +
> +	dev_dbg(priv->ipu->dev, "%s: ctx %p run %p\n", __func__,
> +		ctx, run);
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	if (ctx->aborting) {
> +		ret = -EIO;
> +		goto unlock;
> +	}
> +
> +	list_add_tail(&run->list, &cvt->pending_q);
> +
> +	if (!cvt->current_run) {
> +		ret = ipu_ic_run(run);
> +		if (ret)
> +			cvt->current_run = NULL;
> +	}
> +unlock:
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +
> +	if (ret) {
> +		kfree(run);
> +		run = ERR_PTR(ret);
> +	}
> +
> +	return run;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_run);
> +
> +/* Abort any active or pending conversions for this context */
> +void ipu_image_convert_abort(struct image_converter_ctx *ctx)
> +{
> +	struct image_converter *cvt = ctx->cvt;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	struct image_converter_run *run, *active_run, *tmp;
> +	unsigned long flags;
> +	int run_count, ret;
> +	bool need_abort;
> +
> +	reinit_completion(&ctx->aborted);
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	/* move all remaining pending runs in this context to done_q */
> +	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
> +		if (run->ctx != ctx)
> +			continue;
> +		run->status = -EIO;
> +		list_move_tail(&run->list, &cvt->done_q);
> +	}
> +
> +	run_count = ipu_ic_get_run_count(ctx, &cvt->done_q);
> +	active_run = (cvt->current_run && cvt->current_run->ctx == ctx) ?
> +		cvt->current_run : NULL;
> +
> +	need_abort = (run_count || active_run);
> +
> +	ctx->aborting = need_abort;
> +
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +
> +	if (!need_abort) {
> +		dev_dbg(priv->ipu->dev, "%s: no abort needed for ctx %p\n",
> +			__func__, ctx);
> +		return;
> +	}
> +
> +	dev_dbg(priv->ipu->dev,
> +		 "%s: wait for completion: %d runs, active run %p\n",
> +		 __func__, run_count, active_run);
> +
> +	ret = wait_for_completion_timeout(&ctx->aborted,
> +					  msecs_to_jiffies(10000));
> +	if (ret == 0) {
> +		dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
> +		ipu_ic_force_abort(ctx);
> +	}
> +
> +	ctx->aborting = false;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
> +
> +/* Unprepare image conversion context */
> +void ipu_image_convert_unprepare(struct image_converter_ctx *ctx)
> +{
> +	struct image_converter *cvt = ctx->cvt;
> +	struct ipu_ic_priv *priv = cvt->ic->priv;
> +	unsigned long flags;
> +	bool put_res;
> +
> +	/* make sure no runs are hanging around */
> +	ipu_image_convert_abort(ctx);
> +
> +	dev_dbg(priv->ipu->dev, "%s: removing ctx %p\n", __func__, ctx);
> +
> +	spin_lock_irqsave(&cvt->irqlock, flags);
> +
> +	list_del(&ctx->list);
> +
> +	put_res = list_empty(&cvt->ctx_list);
> +
> +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> +
> +	if (put_res)
> +		ipu_ic_release_ipu_resources(cvt);
> +
> +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
> +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
> +
> +	kfree(ctx);
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
> +
> +/*
> + * "Canned" asynchronous single image conversion. On successful return
> + * caller must call ipu_image_convert_unprepare() after conversion completes.
> + * Returns the new conversion context.
> + */
> +struct image_converter_ctx *
> +ipu_image_convert(struct ipu_ic *ic,
> +		  struct ipu_image *in, struct ipu_image *out,
> +		  enum ipu_rotate_mode rot_mode,
> +		  image_converter_cb_t complete,
> +		  void *complete_context)
> +{
> +	struct image_converter_ctx *ctx;
> +	struct image_converter_run *run;
> +
> +	ctx = ipu_image_convert_prepare(ic, in, out, rot_mode,
> +					complete, complete_context);
> +	if (IS_ERR(ctx))
> +		return ctx;
> +
> +	run = ipu_image_convert_run(ctx, in->phys0, out->phys0);
> +	if (IS_ERR(run)) {
> +		ipu_image_convert_unprepare(ctx);
> +		return ERR_PTR(PTR_ERR(run));
> +	}
> +
> +	return ctx;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert);
> +
> +/* "Canned" synchronous single image conversion */
> +static void image_convert_sync_complete(void *data,
> +					struct image_converter_run *run,
> +					int err)
> +{
> +	struct completion *comp = data;
> +
> +	complete(comp);
> +}
> +
> +int ipu_image_convert_sync(struct ipu_ic *ic,
> +			   struct ipu_image *in, struct ipu_image *out,
> +			   enum ipu_rotate_mode rot_mode)
> +{
> +	struct image_converter_ctx *ctx;
> +	struct completion comp;
> +	int ret;
> +
> +	init_completion(&comp);
> +
> +	ctx = ipu_image_convert(ic, in, out, rot_mode,
> +				image_convert_sync_complete, &comp);
> +	if (IS_ERR(ctx))
> +		return PTR_ERR(ctx);
> +
> +	ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
> +	ret = (ret == 0) ? -ETIMEDOUT : 0;
> +
> +	ipu_image_convert_unprepare(ctx);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
> +
>  int ipu_ic_enable(struct ipu_ic *ic)
>  {
>  	struct ipu_ic_priv *priv = ic->priv;
> @@ -759,6 +2428,7 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
>  	ipu->ic_priv = priv;
>  
>  	spin_lock_init(&priv->lock);
> +
>  	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
>  	if (!priv->base)
>  		return -ENOMEM;
> @@ -771,10 +2441,21 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
>  	priv->ipu = ipu;
>  
>  	for (i = 0; i < IC_NUM_TASKS; i++) {
> -		priv->task[i].task = i;
> -		priv->task[i].priv = priv;
> -		priv->task[i].reg = &ic_task_reg[i];
> -		priv->task[i].bit = &ic_task_bit[i];
> +		struct ipu_ic *ic = &priv->task[i];
> +		struct image_converter *cvt = &ic->cvt;
> +
> +		ic->task = i;
> +		ic->priv = priv;
> +		ic->reg = &ic_task_reg[i];
> +		ic->bit = &ic_task_bit[i];
> +		ic->ch = &ic_task_ch[i];
> +
> +		cvt->ic = ic;
> +		spin_lock_init(&cvt->irqlock);
> +		INIT_LIST_HEAD(&cvt->ctx_list);
> +		INIT_LIST_HEAD(&cvt->pending_q);
> +		INIT_LIST_HEAD(&cvt->done_q);
> +		cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
>  	}
>  
>  	return 0;
> diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
> index 8f77ddb..5938a69 100644
> --- a/include/video/imx-ipu-v3.h
> +++ b/include/video/imx-ipu-v3.h
> @@ -63,17 +63,25 @@ enum ipu_csi_dest {
>  /*
>   * Enumeration of IPU rotation modes
>   */
> +#define IPU_ROT_BIT_VFLIP (1 << 0)
> +#define IPU_ROT_BIT_HFLIP (1 << 1)
> +#define IPU_ROT_BIT_90    (1 << 2)
> +
>  enum ipu_rotate_mode {
>  	IPU_ROTATE_NONE = 0,
> -	IPU_ROTATE_VERT_FLIP,
> -	IPU_ROTATE_HORIZ_FLIP,
> -	IPU_ROTATE_180,
> -	IPU_ROTATE_90_RIGHT,
> -	IPU_ROTATE_90_RIGHT_VFLIP,
> -	IPU_ROTATE_90_RIGHT_HFLIP,
> -	IPU_ROTATE_90_LEFT,
> +	IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
> +	IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
> +	IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
> +	IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
> +	IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
> +	IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
> +	IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
> +			      IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
>  };
>  
> +/* 90-degree rotations require the IRT unit */
> +#define ipu_rot_mode_is_irt(m) ((m) >= IPU_ROTATE_90_RIGHT)
> +
>  enum ipu_color_space {
>  	IPUV3_COLORSPACE_RGB,
>  	IPUV3_COLORSPACE_YUV,
> @@ -320,6 +328,7 @@ enum ipu_ic_task {
>  };
>  
>  struct ipu_ic;
> +
>  int ipu_ic_task_init(struct ipu_ic *ic,
>  		     int in_width, int in_height,
>  		     int out_width, int out_height,
> @@ -335,6 +344,40 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
>  			  u32 width, u32 height, int burst_size,
>  			  enum ipu_rotate_mode rot);
>  int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
> +
> +struct image_converter_ctx;
> +struct image_converter_run;
> +
> +typedef void (*image_converter_cb_t)(void *ctx,
> +				     struct image_converter_run *run,
> +				     int err);
> +
> +int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc);
> +int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
> +			     enum ipu_rotate_mode rot_mode);
> +int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
> +			     enum ipu_rotate_mode rot_mode);
> +struct image_converter_ctx *
> +ipu_image_convert_prepare(struct ipu_ic *ic,
> +			  struct ipu_image *in, struct ipu_image *out,
> +			  enum ipu_rotate_mode rot_mode,
> +			  image_converter_cb_t complete,
> +			  void *complete_context);
> +void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
> +struct image_converter_run *
> +ipu_image_convert_run(struct image_converter_ctx *ctx,
> +		      dma_addr_t in_phys, dma_addr_t out_phys);
> +void ipu_image_convert_abort(struct image_converter_ctx *ctx);
> +struct image_converter_ctx *
> +ipu_image_convert(struct ipu_ic *ic,
> +		  struct ipu_image *in, struct ipu_image *out,
> +		  enum ipu_rotate_mode rot_mode,
> +		  image_converter_cb_t complete,
> +		  void *complete_context);
> +int ipu_image_convert_sync(struct ipu_ic *ic,
> +			   struct ipu_image *in, struct ipu_image *out,
> +			   enum ipu_rotate_mode rot_mode);
> +
>  int ipu_ic_enable(struct ipu_ic *ic);
>  int ipu_ic_disable(struct ipu_ic *ic);
>  struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);



Thanks,
Mauro

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

* Re: [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-13 18:58     ` Mauro Carvalho Chehab
@ 2016-07-13 19:06       ` Mauro Carvalho Chehab
  2016-07-13 22:24       ` Steve Longerbeam
  1 sibling, 0 replies; 105+ messages in thread
From: Mauro Carvalho Chehab @ 2016-07-13 19:06 UTC (permalink / raw)
  To: Steve Longerbeam; +Cc: linux-media, Steve Longerbeam

Em Wed, 13 Jul 2016 15:58:31 -0300
Mauro Carvalho Chehab <mchehab@s-opensource.com> escreveu:

> Em Wed,  6 Jul 2016 16:06:44 -0700
> Steve Longerbeam <slongerbeam@gmail.com> escreveu:
> 
> > This patch implements complete image conversion support to ipu-ic,
> > with tiling to support scaling to and from images up to 4096x4096.
> > Image rotation is also supported.
> > 
> > The internal API is subsystem agnostic (no V4L2 dependency except
> > for the use of V4L2 fourcc pixel formats).

An additional note: when you send a patch series that overrides a
previously sent one, you should add a version at the email title,
like:
	[PATCH 14/28 v2] gpu: ipu-ic: Add complete image conversion support with tiling

It also helps if you add a cover patch when sending big patch series like
this one.


> > 
> > Callers prepare for image conversion by calling
> > ipu_image_convert_prepare(), which initializes the parameters of
> > the conversion. The caller passes in the ipu_ic task to use for
> > the conversion, the input and output image formats, a rotation mode,
> > and a completion callback and completion context pointer:
> > 
> > struct image_converter_ctx *
> > ipu_image_convert_prepare(struct ipu_ic *ic,
> >                           struct ipu_image *in, struct ipu_image *out,
> >                           enum ipu_rotate_mode rot_mode,
> >                           image_converter_cb_t complete,
> >                           void *complete_context);
> > 
> > The caller is given a new conversion context that must be passed to
> > the further APIs:
> > 
> > struct image_converter_run *
> > ipu_image_convert_run(struct image_converter_ctx *ctx,
> >                       dma_addr_t in_phys, dma_addr_t out_phys);
> > 
> > This queues a new image conversion request to a run queue, and
> > starts the conversion immediately if the run queue is empty. Only
> > the physaddr's of the input and output image buffers are needed,
> > since the conversion context was created previously with
> > ipu_image_convert_prepare(). Returns a new run object pointer. When
> > the conversion completes, the run pointer is returned to the
> > completion callback.
> > 
> > void image_convert_abort(struct image_converter_ctx *ctx);
> > 
> > This will abort any active or pending conversions for this context.
> > Any currently active or pending runs belonging to this context are
> > returned via the completion callback with an error status.
> > 
> > void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
> > 
> > Unprepares the conversion context. Any active or pending runs will
> > be aborted by calling image_convert_abort().
> > ---
> >  drivers/gpu/ipu-v3/ipu-ic.c | 1691 ++++++++++++++++++++++++++++++++++++++++++-
> >  include/video/imx-ipu-v3.h  |   57 +-
> >  2 files changed, 1736 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/gpu/ipu-v3/ipu-ic.c b/drivers/gpu/ipu-v3/ipu-ic.c
> > index 5329bfe..f6a1125 100644
> > --- a/drivers/gpu/ipu-v3/ipu-ic.c
> > +++ b/drivers/gpu/ipu-v3/ipu-ic.c
> > @@ -17,6 +17,8 @@
> >  #include <linux/bitrev.h>
> >  #include <linux/io.h>
> >  #include <linux/err.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/dma-mapping.h>
> >  #include "ipu-prv.h"
> >  
> >  /* IC Register Offsets */
> > @@ -82,6 +84,40 @@
> >  #define IC_IDMAC_3_PP_WIDTH_MASK        (0x3ff << 20)
> >  #define IC_IDMAC_3_PP_WIDTH_OFFSET      20
> >  
> > +/*
> > + * The IC Resizer has a restriction that the output frame from the
> > + * resizer must be 1024 or less in both width (pixels) and height
> > + * (lines).
> > + *
> > + * The image conversion support attempts to split up a conversion when
> > + * the desired output (converted) frame resolution exceeds the IC resizer
> > + * limit of 1024 in either dimension.
> > + *
> > + * If either dimension of the output frame exceeds the limit, the
> > + * dimension is split into 1, 2, or 4 equal stripes, for a maximum
> > + * of 4*4 or 16 tiles. A conversion is then carried out for each
> > + * tile (but taking care to pass the full frame stride length to
> > + * the DMA channel's parameter memory!). IDMA double-buffering is used
> > + * to convert each tile back-to-back when possible (see note below
> > + * when double_buffering boolean is set).
> > + *
> > + * Note that the input frame must be split up into the same number
> > + * of tiles as the output frame.
> > + */
> > +#define MAX_STRIPES_W    4
> > +#define MAX_STRIPES_H    4
> > +#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
> > +
> > +#define MIN_W     128
> > +#define MIN_H     128
> > +#define MAX_W     4096
> > +#define MAX_H     4096
> > +
> > +enum image_convert_type {
> > +	IMAGE_CONVERT_IN = 0,
> > +	IMAGE_CONVERT_OUT,
> > +};
> > +
> >  struct ic_task_regoffs {
> >  	u32 rsc;
> >  	u32 tpmem_csc[2];
> > @@ -96,6 +132,16 @@ struct ic_task_bitfields {
> >  	u32 ic_cmb_galpha_bit;
> >  };
> >  
> > +struct ic_task_channels {
> > +	int in;
> > +	int out;
> > +	int rot_in;
> > +	int rot_out;
> > +	int vdi_in_p;
> > +	int vdi_in;
> > +	int vdi_in_n;
> > +};
> > +
> >  static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = {
> >  	[IC_TASK_ENCODER] = {
> >  		.rsc = IC_PRP_ENC_RSC,
> > @@ -138,12 +184,159 @@ static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = {
> >  	},
> >  };
> >  
> > +static const struct ic_task_channels ic_task_ch[IC_NUM_TASKS] = {
> > +	[IC_TASK_ENCODER] = {
> > +		.out = IPUV3_CHANNEL_IC_PRP_ENC_MEM,
> > +		.rot_in = IPUV3_CHANNEL_MEM_ROT_ENC,
> > +		.rot_out = IPUV3_CHANNEL_ROT_ENC_MEM,
> > +	},
> > +	[IC_TASK_VIEWFINDER] = {
> > +		.in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
> > +		.out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
> > +		.rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
> > +		.rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
> > +		.vdi_in_p = IPUV3_CHANNEL_MEM_VDI_P,
> > +		.vdi_in = IPUV3_CHANNEL_MEM_VDI,
> > +		.vdi_in_n = IPUV3_CHANNEL_MEM_VDI_N,
> > +	},
> > +	[IC_TASK_POST_PROCESSOR] = {
> > +		.in = IPUV3_CHANNEL_MEM_IC_PP,
> > +		.out = IPUV3_CHANNEL_IC_PP_MEM,
> > +		.rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
> > +		.rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
> > +	},
> > +};
> > +
> > +struct ipu_ic_dma_buf {
> > +	void          *virt;
> > +	dma_addr_t    phys;
> > +	unsigned long len;
> > +};
> > +
> > +/* dimensions of one tile */
> > +struct ipu_ic_tile {
> > +	unsigned int width;
> > +	unsigned int height;
> > +	/* size and strides are in bytes */
> > +	unsigned int size;
> > +	unsigned int stride;
> > +	unsigned int rot_stride;
> > +};
> > +
> > +struct ipu_ic_tile_off {
> > +	/* start Y or packed offset of this tile */
> > +	u32     offset;
> > +	/* offset from start to tile in U plane, for planar formats */
> > +	u32     u_off;
> > +	/* offset from start to tile in V plane, for planar formats */
> > +	u32     v_off;
> > +};
> > +
> > +struct ipu_ic_pixfmt {
> > +	char	*name;
> > +	u32	fourcc;        /* V4L2 fourcc */
> > +	int     bpp;           /* total bpp */
> > +	int     y_depth;       /* depth of Y plane for planar formats */
> > +	int     uv_width_dec;  /* decimation in width for U/V planes */
> > +	int     uv_height_dec; /* decimation in height for U/V planes */
> > +	bool    uv_swapped;    /* U and V planes are swapped */
> > +	bool    uv_packed;     /* partial planar (U and V in same plane) */
> > +};
> > +
> > +struct ipu_ic_image {
> > +	struct ipu_image base;
> > +	enum image_convert_type type;
> > +
> > +	const struct ipu_ic_pixfmt *fmt;
> > +	unsigned int stride;
> > +
> > +	/* # of rows (horizontal stripes) if dest height is > 1024 */
> > +	unsigned int num_rows;
> > +	/* # of columns (vertical stripes) if dest width is > 1024 */
> > +	unsigned int num_cols;
> > +
> > +	struct ipu_ic_tile tile;
> > +	struct ipu_ic_tile_off tile_off[MAX_TILES];
> > +};
> > +
> > +struct image_converter_ctx;
> > +struct image_converter;
> >  struct ipu_ic_priv;
> > +struct ipu_ic;
> > +
> > +struct image_converter_run {
> > +	struct image_converter_ctx *ctx;
> > +
> > +	dma_addr_t in_phys;
> > +	dma_addr_t out_phys;
> > +
> > +	int status;
> > +
> > +	struct list_head list;
> > +};
> > +
> > +struct image_converter_ctx {
> > +	struct image_converter *cvt;
> > +
> > +	image_converter_cb_t complete;
> > +	void *complete_context;
> > +
> > +	/* Source/destination image data and rotation mode */
> > +	struct ipu_ic_image in;
> > +	struct ipu_ic_image out;
> > +	enum ipu_rotate_mode rot_mode;
> > +
> > +	/* intermediate buffer for rotation */
> > +	struct ipu_ic_dma_buf rot_intermediate[2];
> > +
> > +	/* current buffer number for double buffering */
> > +	int cur_buf_num;
> > +
> > +	bool aborting;
> > +	struct completion aborted;
> > +
> > +	/* can we use double-buffering for this conversion operation? */
> > +	bool double_buffering;
> > +	/* num_rows * num_cols */
> > +	unsigned int num_tiles;
> > +	/* next tile to process */
> > +	unsigned int next_tile;
> > +	/* where to place converted tile in dest image */
> > +	unsigned int out_tile_map[MAX_TILES];
> > +
> > +	struct list_head list;
> > +};
> > +
> > +struct image_converter {
> > +	struct ipu_ic *ic;
> > +
> > +	struct ipuv3_channel *in_chan;
> > +	struct ipuv3_channel *out_chan;
> > +	struct ipuv3_channel *rotation_in_chan;
> > +	struct ipuv3_channel *rotation_out_chan;
> > +
> > +	/* the IPU end-of-frame irqs */
> > +	int out_eof_irq;
> > +	int rot_out_eof_irq;
> > +
> > +	spinlock_t irqlock;
> > +
> > +	/* list of convert contexts */
> > +	struct list_head ctx_list;
> > +	/* queue of conversion runs */
> > +	struct list_head pending_q;
> > +	/* queue of completed runs */
> > +	struct list_head done_q;
> > +
> > +	/* the current conversion run */
> > +	struct image_converter_run *current_run;
> > +};
> >  
> >  struct ipu_ic {
> >  	enum ipu_ic_task task;
> >  	const struct ic_task_regoffs *reg;
> >  	const struct ic_task_bitfields *bit;
> > +	const struct ic_task_channels *ch;
> >  
> >  	enum ipu_color_space in_cs, g_in_cs;
> >  	enum ipu_color_space out_cs;
> > @@ -151,6 +344,8 @@ struct ipu_ic {
> >  	bool rotation;
> >  	bool in_use;
> >  
> > +	struct image_converter cvt;
> > +
> >  	struct ipu_ic_priv *priv;
> >  };
> >  
> > @@ -619,7 +814,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
> >  	ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
> >  	ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
> >  
> > -	if (rot >= IPU_ROTATE_90_RIGHT)
> > +	if (ipu_rot_mode_is_irt(rot))
> >  		ic->rotation = true;
> >  
> >  unlock:
> > @@ -661,6 +856,1480 @@ static void ipu_irt_disable(struct ipu_ic *ic)
> >  		priv->irt_use_count = 0;
> >  }
> >  
> > +/*
> > + * Complete image conversion support follows
> > + */
> > +
> > +static const struct ipu_ic_pixfmt ipu_ic_formats[] = {
> > +	{
> > +		.name	= "RGB565",
> > +		.fourcc	= V4L2_PIX_FMT_RGB565,
> > +		.bpp    = 16,
> > +	}, {
> > +		.name	= "RGB24",
> > +		.fourcc	= V4L2_PIX_FMT_RGB24,
> > +		.bpp    = 24,
> > +	}, {
> > +		.name	= "BGR24",
> > +		.fourcc	= V4L2_PIX_FMT_BGR24,
> > +		.bpp    = 24,
> > +	}, {
> > +		.name	= "RGB32",
> > +		.fourcc	= V4L2_PIX_FMT_RGB32,
> > +		.bpp    = 32,
> > +	}, {
> > +		.name	= "BGR32",
> > +		.fourcc	= V4L2_PIX_FMT_BGR32,
> > +		.bpp    = 32,
> > +	}, {
> > +		.name	= "4:2:2 packed, YUYV",
> > +		.fourcc	= V4L2_PIX_FMT_YUYV,
> > +		.bpp    = 16,
> > +		.uv_width_dec = 2,
> > +		.uv_height_dec = 1,
> > +	}, {
> > +		.name	= "4:2:2 packed, UYVY",
> > +		.fourcc	= V4L2_PIX_FMT_UYVY,
> > +		.bpp    = 16,
> > +		.uv_width_dec = 2,
> > +		.uv_height_dec = 1,
> > +	}, {
> > +		.name	= "4:2:0 planar, YUV",
> > +		.fourcc	= V4L2_PIX_FMT_YUV420,
> > +		.bpp    = 12,
> > +		.y_depth = 8,
> > +		.uv_width_dec = 2,
> > +		.uv_height_dec = 2,
> > +	}, {
> > +		.name	= "4:2:0 planar, YVU",
> > +		.fourcc	= V4L2_PIX_FMT_YVU420,
> > +		.bpp    = 12,
> > +		.y_depth = 8,
> > +		.uv_width_dec = 2,
> > +		.uv_height_dec = 2,
> > +		.uv_swapped = true,
> > +	}, {
> > +		.name   = "4:2:0 partial planar, NV12",
> > +		.fourcc = V4L2_PIX_FMT_NV12,
> > +		.bpp    = 12,
> > +		.y_depth = 8,
> > +		.uv_width_dec = 2,
> > +		.uv_height_dec = 2,
> > +		.uv_packed = true,
> > +	}, {
> > +		.name   = "4:2:2 planar, YUV",
> > +		.fourcc = V4L2_PIX_FMT_YUV422P,
> > +		.bpp    = 16,
> > +		.y_depth = 8,
> > +		.uv_width_dec = 2,
> > +		.uv_height_dec = 1,
> > +	}, {
> > +		.name   = "4:2:2 partial planar, NV16",
> > +		.fourcc = V4L2_PIX_FMT_NV16,
> > +		.bpp    = 16,
> > +		.y_depth = 8,
> > +		.uv_width_dec = 2,
> > +		.uv_height_dec = 1,
> > +		.uv_packed = true,
> > +	},
> > +};
> > +
> > +static const struct ipu_ic_pixfmt *ipu_ic_get_format(u32 fourcc)
> > +{
> > +	const struct ipu_ic_pixfmt *ret = NULL;
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(ipu_ic_formats); i++) {
> > +		if (ipu_ic_formats[i].fourcc == fourcc) {
> > +			ret = &ipu_ic_formats[i];
> > +			break;
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static void ipu_ic_dump_format(struct image_converter_ctx *ctx,
> > +			       struct ipu_ic_image *ic_image)
> > +{
> > +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> > +
> > +	dev_dbg(priv->ipu->dev,
> > +		"ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
> > +		ctx,
> > +		ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
> > +		ic_image->base.pix.width, ic_image->base.pix.height,
> > +		ic_image->num_cols, ic_image->num_rows,
> > +		ic_image->tile.width, ic_image->tile.height,
> > +		ic_image->fmt->fourcc & 0xff,
> > +		(ic_image->fmt->fourcc >> 8) & 0xff,
> > +		(ic_image->fmt->fourcc >> 16) & 0xff,
> > +		(ic_image->fmt->fourcc >> 24) & 0xff);
> > +}
> > +
> > +int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc)
> > +{
> > +	const struct ipu_ic_pixfmt *fmt;
> > +
> > +	if (index >= (int)ARRAY_SIZE(ipu_ic_formats))
> > +		return -EINVAL;
> > +
> > +	/* Format found */
> > +	fmt = &ipu_ic_formats[index];
> > +	*desc = fmt->name;
> > +	*fourcc = fmt->fourcc;
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
> > +
> > +static void ipu_ic_free_dma_buf(struct ipu_ic_priv *priv,
> > +				struct ipu_ic_dma_buf *buf)
> > +{
> > +	if (buf->virt)
> > +		dma_free_coherent(priv->ipu->dev,
> > +				  buf->len, buf->virt, buf->phys);
> > +	buf->virt = NULL;
> > +	buf->phys = 0;
> > +}
> > +
> > +static int ipu_ic_alloc_dma_buf(struct ipu_ic_priv *priv,
> > +				struct ipu_ic_dma_buf *buf,
> > +				int size)
> > +{
> > +	unsigned long newlen = PAGE_ALIGN(size);
> > +
> > +	if (buf->virt) {
> > +		if (buf->len == newlen)
> > +			return 0;
> > +		ipu_ic_free_dma_buf(priv, buf);
> > +	}
> > +
> > +	buf->len = newlen;
> > +	buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
> > +				       GFP_DMA | GFP_KERNEL);
> > +	if (!buf->virt) {
> > +		dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static inline int ipu_ic_num_stripes(int dim)
> > +{
> > +	if (dim <= 1024)
> > +		return 1;
> > +	else if (dim <= 2048)
> > +		return 2;
> > +	else
> > +		return 4;
> > +}
> > +
> > +static void ipu_ic_calc_tile_dimensions(struct image_converter_ctx *ctx,
> > +					struct ipu_ic_image *image)
> > +{
> > +	struct ipu_ic_tile *tile = &image->tile;
> > +
> > +	tile->height = image->base.pix.height / image->num_rows;
> > +	tile->width = image->base.pix.width / image->num_cols;
> > +	tile->size = ((tile->height * image->fmt->bpp) >> 3) * tile->width;
> > +
> > +	if (image->fmt->y_depth) {
> > +		tile->stride = (image->fmt->y_depth * tile->width) >> 3;
> > +		tile->rot_stride = (image->fmt->y_depth * tile->height) >> 3;
> > +	} else {
> > +		tile->stride = (image->fmt->bpp * tile->width) >> 3;
> > +		tile->rot_stride = (image->fmt->bpp * tile->height) >> 3;
> > +	}
> > +}
> > +
> > +/*
> > + * Use the rotation transformation to find the tile coordinates
> > + * (row, col) of a tile in the destination frame that corresponds
> > + * to the given tile coordinates of a source frame. The destination
> > + * coordinate is then converted to a tile index.
> > + */
> > +static int ipu_ic_transform_tile_index(struct image_converter_ctx *ctx,
> > +				       int src_row, int src_col)
> > +{
> 
> We don't do image rotation in software inside the Kernel! This is
> something that should be done either by some hardware block or
> in userspace.
> 
> > +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> > +	struct ipu_ic_image *s_image = &ctx->in;
> > +	struct ipu_ic_image *d_image = &ctx->out;
> > +	int cos, sin, dst_row, dst_col;
> > +
> > +	/* with no rotation it's a 1:1 mapping */
> > +	if (ctx->rot_mode == IPU_ROTATE_NONE)
> > +		return src_row * s_image->num_cols + src_col;
> > +
> > +	if (ctx->rot_mode & IPU_ROT_BIT_90) {
> > +		cos = 0;
> > +		sin = 1;
> > +	} else {
> > +		cos = 1;
> > +		sin = 0;
> > +	}
> > +
> > +	/*
> > +	 * before doing the transform, first we have to translate
> > +	 * source row,col for an origin in the center of s_image
> > +	 */
> > +	src_row *= 2;
> > +	src_col *= 2;
> > +	src_row -= s_image->num_rows - 1;
> > +	src_col -= s_image->num_cols - 1;
> > +
> > +	/* do the rotation transform */
> > +	dst_col = src_col * cos - src_row * sin;
> > +	dst_row = src_col * sin + src_row * cos;
> > +
> > +	/* apply flip */
> > +	if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
> > +		dst_col = -dst_col;
> > +	if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
> > +		dst_row = -dst_row;
> > +
> > +	dev_dbg(priv->ipu->dev, "ctx %p: [%d,%d] --> [%d,%d]\n",
> > +		ctx, src_col, src_row, dst_col, dst_row);
> > +
> > +	/*
> > +	 * finally translate dest row,col using an origin in upper
> > +	 * left of d_image
> > +	 */
> > +	dst_row += d_image->num_rows - 1;
> > +	dst_col += d_image->num_cols - 1;
> > +	dst_row /= 2;
> > +	dst_col /= 2;
> > +
> > +	return dst_row * d_image->num_cols + dst_col;
> > +}
> > +
> > +/*
> > + * Fill the out_tile_map[] with transformed destination tile indeces.
> > + */
> > +static void ipu_ic_calc_out_tile_map(struct image_converter_ctx *ctx)
> > +{
> > +	struct ipu_ic_image *s_image = &ctx->in;
> > +	unsigned int row, col, tile = 0;
> > +
> > +	for (row = 0; row < s_image->num_rows; row++) {
> > +		for (col = 0; col < s_image->num_cols; col++) {
> > +			ctx->out_tile_map[tile] =
> > +				ipu_ic_transform_tile_index(ctx, row, col);
> > +			tile++;
> > +		}
> > +	}
> > +}
> > +
> > +static void ipu_ic_calc_tile_offsets_planar(struct image_converter_ctx *ctx,
> > +					    struct ipu_ic_image *image)
> > +{
> 
> We also don't do image conversions inside the Kernel. Same applies
> to other similar codes on this patch.
> 
> > +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> > +	const struct ipu_ic_pixfmt *fmt = image->fmt;
> > +	unsigned int row, col, tile = 0;
> > +	u32 H, w, h, y_depth, y_stride, uv_stride;
> > +	u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
> > +	u32 y_row_off, y_col_off, y_off;
> > +	u32 y_size, uv_size;
> > +
> > +	/* setup some convenience vars */
> > +	H = image->base.pix.height;
> > +	w = image->tile.width;
> > +	h = image->tile.height;
> > +
> > +	y_depth = fmt->y_depth;
> > +	y_stride = image->stride;
> > +	uv_stride = y_stride / fmt->uv_width_dec;
> > +	if (fmt->uv_packed)
> > +		uv_stride *= 2;
> > +
> > +	y_size = H * y_stride;
> > +	uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
> > +
> > +	for (row = 0; row < image->num_rows; row++) {
> > +		y_row_off = row * h * y_stride;
> > +		uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
> > +
> > +		for (col = 0; col < image->num_cols; col++) {
> > +			y_col_off = (col * w * y_depth) >> 3;
> > +			uv_col_off = y_col_off / fmt->uv_width_dec;
> > +			if (fmt->uv_packed)
> > +				uv_col_off *= 2;
> > +
> > +			y_off = y_row_off + y_col_off;
> > +			uv_off = uv_row_off + uv_col_off;
> > +
> > +			u_off = y_size - y_off + uv_off;
> > +			v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
> > +			if (fmt->uv_swapped) {
> > +				tmp = u_off;
> > +				u_off = v_off;
> > +				v_off = tmp;
> > +			}
> > +
> > +			image->tile_off[tile].offset = y_off;
> > +			image->tile_off[tile].u_off = u_off;
> > +			image->tile_off[tile++].v_off = v_off;
> > +
> > +			dev_dbg(priv->ipu->dev,
> > +				"ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
> > +				ctx, image->type == IMAGE_CONVERT_IN ?
> > +				"Input" : "Output", row, col,
> > +				y_off, u_off, v_off);
> > +		}
> > +	}
> > +}
> > +
> > +static void ipu_ic_calc_tile_offsets_packed(struct image_converter_ctx *ctx,
> > +					    struct ipu_ic_image *image)
> > +{
> > +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> > +	const struct ipu_ic_pixfmt *fmt = image->fmt;
> > +	unsigned int row, col, tile = 0;
> > +	u32 w, h, bpp, stride;
> > +	u32 row_off, col_off;
> > +
> > +	/* setup some convenience vars */
> > +	w = image->tile.width;
> > +	h = image->tile.height;
> > +	stride = image->stride;
> > +	bpp = fmt->bpp;
> > +
> > +	for (row = 0; row < image->num_rows; row++) {
> > +		row_off = row * h * stride;
> > +
> > +		for (col = 0; col < image->num_cols; col++) {
> > +			col_off = (col * w * bpp) >> 3;
> > +
> > +			image->tile_off[tile].offset = row_off + col_off;
> > +			image->tile_off[tile].u_off = 0;
> > +			image->tile_off[tile++].v_off = 0;
> > +
> > +			dev_dbg(priv->ipu->dev,
> > +				"ctx %p: %s@[%d,%d]: phys %08x\n", ctx,
> > +				image->type == IMAGE_CONVERT_IN ?
> > +				"Input" : "Output", row, col,
> > +				row_off + col_off);
> > +		}
> > +	}
> > +}
> > +
> > +static void ipu_ic_calc_tile_offsets(struct image_converter_ctx *ctx,
> > +				     struct ipu_ic_image *image)
> > +{
> > +	memset(image->tile_off, 0, sizeof(image->tile_off));
> > +
> > +	if (image->fmt->y_depth)
> > +		ipu_ic_calc_tile_offsets_planar(ctx, image);
> > +	else
> > +		ipu_ic_calc_tile_offsets_packed(ctx, image);
> > +}
> > +
> > +/*
> > + * return the number of runs in given queue (pending_q or done_q)
> > + * for this context. hold irqlock when calling.
> > + */
> > +static int ipu_ic_get_run_count(struct image_converter_ctx *ctx,
> > +				struct list_head *q)
> > +{
> > +	struct image_converter_run *run;
> > +	int count = 0;
> > +
> > +	list_for_each_entry(run, q, list) {
> > +		if (run->ctx == ctx)
> > +			count++;
> > +	}
> > +
> > +	return count;
> > +}
> > +
> > +/* hold irqlock when calling */
> > +static void ipu_ic_convert_stop(struct image_converter_run *run)
> > +{
> > +	struct image_converter_ctx *ctx = run->ctx;
> > +	struct image_converter *cvt = ctx->cvt;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +
> > +	dev_dbg(priv->ipu->dev, "%s: stopping ctx %p run %p\n",
> > +		__func__, ctx, run);
> > +
> > +	/* disable IC tasks and the channels */
> > +	ipu_ic_task_disable(cvt->ic);
> > +	ipu_idmac_disable_channel(cvt->in_chan);
> > +	ipu_idmac_disable_channel(cvt->out_chan);
> > +
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		ipu_idmac_disable_channel(cvt->rotation_in_chan);
> > +		ipu_idmac_disable_channel(cvt->rotation_out_chan);
> > +		ipu_idmac_unlink(cvt->out_chan, cvt->rotation_in_chan);
> > +	}
> > +
> > +	ipu_ic_disable(cvt->ic);
> > +}
> > +
> > +/* hold irqlock when calling */
> > +static void init_idmac_channel(struct image_converter_ctx *ctx,
> > +			       struct ipuv3_channel *channel,
> > +			       struct ipu_ic_image *image,
> > +			       enum ipu_rotate_mode rot_mode,
> > +			       bool rot_swap_width_height)
> > +{
> > +	struct image_converter *cvt = ctx->cvt;
> > +	unsigned int burst_size;
> > +	u32 width, height, stride;
> > +	dma_addr_t addr0, addr1 = 0;
> > +	struct ipu_image tile_image;
> > +	unsigned int tile_idx[2];
> > +
> > +	if (image->type == IMAGE_CONVERT_OUT) {
> > +		tile_idx[0] = ctx->out_tile_map[0];
> > +		tile_idx[1] = ctx->out_tile_map[1];
> > +	} else {
> > +		tile_idx[0] = 0;
> > +		tile_idx[1] = 1;
> > +	}
> > +
> > +	if (rot_swap_width_height) {
> > +		width = image->tile.height;
> > +		height = image->tile.width;
> > +		stride = image->tile.rot_stride;
> > +		addr0 = ctx->rot_intermediate[0].phys;
> > +		if (ctx->double_buffering)
> > +			addr1 = ctx->rot_intermediate[1].phys;
> > +	} else {
> > +		width = image->tile.width;
> > +		height = image->tile.height;
> > +		stride = image->stride;
> > +		addr0 = image->base.phys0 +
> > +			image->tile_off[tile_idx[0]].offset;
> > +		if (ctx->double_buffering)
> > +			addr1 = image->base.phys0 +
> > +				image->tile_off[tile_idx[1]].offset;
> > +	}
> > +
> > +	ipu_cpmem_zero(channel);
> > +
> > +	memset(&tile_image, 0, sizeof(tile_image));
> > +	tile_image.pix.width = tile_image.rect.width = width;
> > +	tile_image.pix.height = tile_image.rect.height = height;
> > +	tile_image.pix.bytesperline = stride;
> > +	tile_image.pix.pixelformat =  image->fmt->fourcc;
> > +	tile_image.phys0 = addr0;
> > +	tile_image.phys1 = addr1;
> > +	ipu_cpmem_set_image(channel, &tile_image);
> > +
> > +	if (image->fmt->y_depth && !rot_swap_width_height)
> > +		ipu_cpmem_set_uv_offset(channel,
> > +					image->tile_off[tile_idx[0]].u_off,
> > +					image->tile_off[tile_idx[0]].v_off);
> > +
> > +	if (rot_mode)
> > +		ipu_cpmem_set_rotation(channel, rot_mode);
> > +
> > +	if (channel == cvt->rotation_in_chan ||
> > +	    channel == cvt->rotation_out_chan) {
> > +		burst_size = 8;
> > +		ipu_cpmem_set_block_mode(channel);
> > +	} else
> > +		burst_size = (width % 16) ? 8 : 16;
> > +
> > +	ipu_cpmem_set_burstsize(channel, burst_size);
> > +
> > +	ipu_ic_task_idma_init(cvt->ic, channel, width, height,
> > +			      burst_size, rot_mode);
> > +
> > +	ipu_cpmem_set_axi_id(channel, 1);
> > +
> > +	ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
> > +}
> > +
> > +/* hold irqlock when calling */
> > +static int ipu_ic_convert_start(struct image_converter_run *run)
> > +{
> > +	struct image_converter_ctx *ctx = run->ctx;
> > +	struct image_converter *cvt = ctx->cvt;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	struct ipu_ic_image *s_image = &ctx->in;
> > +	struct ipu_ic_image *d_image = &ctx->out;
> > +	enum ipu_color_space src_cs, dest_cs;
> > +	unsigned int dest_width, dest_height;
> > +	int ret;
> > +
> > +	dev_dbg(priv->ipu->dev, "%s: starting ctx %p run %p\n",
> > +		__func__, ctx, run);
> > +
> > +	src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
> > +	dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
> > +
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		/* swap width/height for resizer */
> > +		dest_width = d_image->tile.height;
> > +		dest_height = d_image->tile.width;
> > +	} else {
> > +		dest_width = d_image->tile.width;
> > +		dest_height = d_image->tile.height;
> > +	}
> > +
> > +	/* setup the IC resizer and CSC */
> > +	ret = ipu_ic_task_init(cvt->ic,
> > +			       s_image->tile.width,
> > +			       s_image->tile.height,
> > +			       dest_width,
> > +			       dest_height,
> > +			       src_cs, dest_cs);
> > +	if (ret) {
> > +		dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	/* init the source MEM-->IC PP IDMAC channel */
> > +	init_idmac_channel(ctx, cvt->in_chan, s_image,
> > +			   IPU_ROTATE_NONE, false);
> > +
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		/* init the IC PP-->MEM IDMAC channel */
> > +		init_idmac_channel(ctx, cvt->out_chan, d_image,
> > +				   IPU_ROTATE_NONE, true);
> > +
> > +		/* init the MEM-->IC PP ROT IDMAC channel */
> > +		init_idmac_channel(ctx, cvt->rotation_in_chan, d_image,
> > +				   ctx->rot_mode, true);
> > +
> > +		/* init the destination IC PP ROT-->MEM IDMAC channel */
> > +		init_idmac_channel(ctx, cvt->rotation_out_chan, d_image,
> > +				   IPU_ROTATE_NONE, false);
> > +
> > +		/* now link IC PP-->MEM to MEM-->IC PP ROT */
> > +		ipu_idmac_link(cvt->out_chan, cvt->rotation_in_chan);
> > +	} else {
> > +		/* init the destination IC PP-->MEM IDMAC channel */
> > +		init_idmac_channel(ctx, cvt->out_chan, d_image,
> > +				   ctx->rot_mode, false);
> > +	}
> > +
> > +	/* enable the IC */
> > +	ipu_ic_enable(cvt->ic);
> > +
> > +	/* set buffers ready */
> > +	ipu_idmac_select_buffer(cvt->in_chan, 0);
> > +	ipu_idmac_select_buffer(cvt->out_chan, 0);
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode))
> > +		ipu_idmac_select_buffer(cvt->rotation_out_chan, 0);
> > +	if (ctx->double_buffering) {
> > +		ipu_idmac_select_buffer(cvt->in_chan, 1);
> > +		ipu_idmac_select_buffer(cvt->out_chan, 1);
> > +		if (ipu_rot_mode_is_irt(ctx->rot_mode))
> > +			ipu_idmac_select_buffer(cvt->rotation_out_chan, 1);
> > +	}
> > +
> > +	/* enable the channels! */
> > +	ipu_idmac_enable_channel(cvt->in_chan);
> > +	ipu_idmac_enable_channel(cvt->out_chan);
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		ipu_idmac_enable_channel(cvt->rotation_in_chan);
> > +		ipu_idmac_enable_channel(cvt->rotation_out_chan);
> > +	}
> > +
> > +	ipu_ic_task_enable(cvt->ic);
> > +
> > +	ipu_cpmem_dump(cvt->in_chan);
> > +	ipu_cpmem_dump(cvt->out_chan);
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		ipu_cpmem_dump(cvt->rotation_in_chan);
> > +		ipu_cpmem_dump(cvt->rotation_out_chan);
> > +	}
> > +
> > +	ipu_dump(priv->ipu);
> > +
> > +	return 0;
> > +}
> > +
> > +/* hold irqlock when calling */
> > +static int ipu_ic_run(struct image_converter_run *run)
> > +{
> > +	struct image_converter_ctx *ctx = run->ctx;
> > +	struct image_converter *cvt = ctx->cvt;
> > +
> > +	ctx->in.base.phys0 = run->in_phys;
> > +	ctx->out.base.phys0 = run->out_phys;
> > +
> > +	ctx->cur_buf_num = 0;
> > +	ctx->next_tile = 1;
> > +
> > +	/* remove run from pending_q and set as current */
> > +	list_del(&run->list);
> > +	cvt->current_run = run;
> > +
> > +	return ipu_ic_convert_start(run);
> > +}
> > +
> > +/* hold irqlock when calling */
> > +static void ipu_ic_run_next(struct image_converter *cvt)
> > +{
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	struct image_converter_run *run, *tmp;
> > +	int ret;
> > +
> > +	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
> > +		/* skip contexts that are aborting */
> > +		if (run->ctx->aborting) {
> > +			dev_dbg(priv->ipu->dev,
> > +				 "%s: skipping aborting ctx %p run %p\n",
> > +				 __func__, run->ctx, run);
> > +			continue;
> > +		}
> > +
> > +		ret = ipu_ic_run(run);
> > +		if (!ret)
> > +			break;
> > +
> > +		/*
> > +		 * something went wrong with start, add the run
> > +		 * to done q and continue to the next run in the
> > +		 * pending q.
> > +		 */
> > +		run->status = ret;
> > +		list_add_tail(&run->list, &cvt->done_q);
> > +		cvt->current_run = NULL;
> > +	}
> > +}
> > +
> > +static void ipu_ic_empty_done_q(struct image_converter *cvt)
> > +{
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	struct image_converter_run *run;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	while (!list_empty(&cvt->done_q)) {
> > +		run = list_entry(cvt->done_q.next,
> > +				 struct image_converter_run,
> > +				 list);
> > +
> > +		list_del(&run->list);
> > +
> > +		dev_dbg(priv->ipu->dev,
> > +			"%s: completing ctx %p run %p with %d\n",
> > +			__func__, run->ctx, run, run->status);
> > +
> > +		/* call the completion callback and free the run */
> > +		spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +		run->ctx->complete(run->ctx->complete_context, run,
> > +				   run->status);
> > +		kfree(run);
> > +		spin_lock_irqsave(&cvt->irqlock, flags);
> > +	}
> > +
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +}
> > +
> > +/*
> > + * the bottom half thread clears out the done_q, calling the
> > + * completion handler for each.
> > + */
> > +static irqreturn_t ipu_ic_bh(int irq, void *dev_id)
> > +{
> > +	struct image_converter *cvt = dev_id;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	struct image_converter_ctx *ctx;
> > +	unsigned long flags;
> > +
> > +	dev_dbg(priv->ipu->dev, "%s: enter\n", __func__);
> > +
> > +	ipu_ic_empty_done_q(cvt);
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	/*
> > +	 * the done_q is cleared out, signal any contexts
> > +	 * that are aborting that abort can complete.
> > +	 */
> > +	list_for_each_entry(ctx, &cvt->ctx_list, list) {
> > +		if (ctx->aborting) {
> > +			dev_dbg(priv->ipu->dev,
> > +				 "%s: signaling abort for ctx %p\n",
> > +				 __func__, ctx);
> > +			complete(&ctx->aborted);
> > +		}
> > +	}
> > +
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +
> > +	dev_dbg(priv->ipu->dev, "%s: exit\n", __func__);
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +/* hold irqlock when calling */
> > +static irqreturn_t ipu_ic_doirq(struct image_converter_run *run)
> > +{
> > +	struct image_converter_ctx *ctx = run->ctx;
> > +	struct image_converter *cvt = ctx->cvt;
> > +	struct ipu_ic_tile_off *src_off, *dst_off;
> > +	struct ipu_ic_image *s_image = &ctx->in;
> > +	struct ipu_ic_image *d_image = &ctx->out;
> > +	struct ipuv3_channel *outch;
> > +	unsigned int dst_idx;
> > +
> > +	outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
> > +		cvt->rotation_out_chan : cvt->out_chan;
> > +
> > +	/*
> > +	 * It is difficult to stop the channel DMA before the channels
> > +	 * enter the paused state. Without double-buffering the channels
> > +	 * are always in a paused state when the EOF irq occurs, so it
> > +	 * is safe to stop the channels now. For double-buffering we
> > +	 * just ignore the abort until the operation completes, when it
> > +	 * is safe to shut down.
> > +	 */
> > +	if (ctx->aborting && !ctx->double_buffering) {
> > +		ipu_ic_convert_stop(run);
> > +		run->status = -EIO;
> > +		goto done;
> > +	}
> > +
> > +	if (ctx->next_tile == ctx->num_tiles) {
> > +		/*
> > +		 * the conversion is complete
> > +		 */
> > +		ipu_ic_convert_stop(run);
> > +		run->status = 0;
> > +		goto done;
> > +	}
> > +
> > +	/*
> > +	 * not done, place the next tile buffers.
> > +	 */
> > +	if (!ctx->double_buffering) {
> > +
> > +		src_off = &s_image->tile_off[ctx->next_tile];
> > +		dst_idx = ctx->out_tile_map[ctx->next_tile];
> > +		dst_off = &d_image->tile_off[dst_idx];
> > +
> > +		ipu_cpmem_set_buffer(cvt->in_chan, 0,
> > +				     s_image->base.phys0 + src_off->offset);
> > +		ipu_cpmem_set_buffer(outch, 0,
> > +				     d_image->base.phys0 + dst_off->offset);
> > +		if (s_image->fmt->y_depth)
> > +			ipu_cpmem_set_uv_offset(cvt->in_chan,
> > +						src_off->u_off,
> > +						src_off->v_off);
> > +		if (d_image->fmt->y_depth)
> > +			ipu_cpmem_set_uv_offset(outch,
> > +						dst_off->u_off,
> > +						dst_off->v_off);
> > +
> > +		ipu_idmac_select_buffer(cvt->in_chan, 0);
> > +		ipu_idmac_select_buffer(outch, 0);
> > +
> > +	} else if (ctx->next_tile < ctx->num_tiles - 1) {
> > +
> > +		src_off = &s_image->tile_off[ctx->next_tile + 1];
> > +		dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
> > +		dst_off = &d_image->tile_off[dst_idx];
> > +
> > +		ipu_cpmem_set_buffer(cvt->in_chan, ctx->cur_buf_num,
> > +				     s_image->base.phys0 + src_off->offset);
> > +		ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
> > +				     d_image->base.phys0 + dst_off->offset);
> > +
> > +		ipu_idmac_select_buffer(cvt->in_chan, ctx->cur_buf_num);
> > +		ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
> > +
> > +		ctx->cur_buf_num ^= 1;
> > +	}
> > +
> > +	ctx->next_tile++;
> > +	return IRQ_HANDLED;
> > +done:
> > +	list_add_tail(&run->list, &cvt->done_q);
> > +	cvt->current_run = NULL;
> > +	ipu_ic_run_next(cvt);
> > +	return IRQ_WAKE_THREAD;
> > +}
> > +
> > +static irqreturn_t ipu_ic_norotate_irq(int irq, void *data)
> > +{
> > +	struct image_converter *cvt = data;
> > +	struct image_converter_ctx *ctx;
> > +	struct image_converter_run *run;
> > +	unsigned long flags;
> > +	irqreturn_t ret;
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	/* get current run and its context */
> > +	run = cvt->current_run;
> > +	if (!run) {
> > +		ret = IRQ_NONE;
> > +		goto out;
> > +	}
> > +
> > +	ctx = run->ctx;
> > +
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		/* this is a rotation operation, just ignore */
> > +		spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +		return IRQ_HANDLED;
> > +	}
> > +
> > +	ret = ipu_ic_doirq(run);
> > +out:
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +	return ret;
> > +}
> > +
> > +static irqreturn_t ipu_ic_rotate_irq(int irq, void *data)
> > +{
> > +	struct image_converter *cvt = data;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	struct image_converter_ctx *ctx;
> > +	struct image_converter_run *run;
> > +	unsigned long flags;
> > +	irqreturn_t ret;
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	/* get current run and its context */
> > +	run = cvt->current_run;
> > +	if (!run) {
> > +		ret = IRQ_NONE;
> > +		goto out;
> > +	}
> > +
> > +	ctx = run->ctx;
> > +
> > +	if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		/* this was NOT a rotation operation, shouldn't happen */
> > +		dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
> > +		spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +		return IRQ_HANDLED;
> > +	}
> > +
> > +	ret = ipu_ic_doirq(run);
> > +out:
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +	return ret;
> > +}
> > +
> > +/*
> > + * try to force the completion of runs for this ctx. Called when
> > + * abort wait times out in ipu_image_convert_abort().
> > + */
> > +static void ipu_ic_force_abort(struct image_converter_ctx *ctx)
> > +{
> > +	struct image_converter *cvt = ctx->cvt;
> > +	struct image_converter_run *run;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	run = cvt->current_run;
> > +	if (run && run->ctx == ctx) {
> > +		ipu_ic_convert_stop(run);
> > +		run->status = -EIO;
> > +		list_add_tail(&run->list, &cvt->done_q);
> > +		cvt->current_run = NULL;
> > +		ipu_ic_run_next(cvt);
> > +	}
> > +
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +
> > +	ipu_ic_empty_done_q(cvt);
> > +}
> > +
> > +static void ipu_ic_release_ipu_resources(struct image_converter *cvt)
> > +{
> > +	if (cvt->out_eof_irq >= 0)
> > +		free_irq(cvt->out_eof_irq, cvt);
> > +	if (cvt->rot_out_eof_irq >= 0)
> > +		free_irq(cvt->rot_out_eof_irq, cvt);
> > +
> > +	if (!IS_ERR_OR_NULL(cvt->in_chan))
> > +		ipu_idmac_put(cvt->in_chan);
> > +	if (!IS_ERR_OR_NULL(cvt->out_chan))
> > +		ipu_idmac_put(cvt->out_chan);
> > +	if (!IS_ERR_OR_NULL(cvt->rotation_in_chan))
> > +		ipu_idmac_put(cvt->rotation_in_chan);
> > +	if (!IS_ERR_OR_NULL(cvt->rotation_out_chan))
> > +		ipu_idmac_put(cvt->rotation_out_chan);
> > +
> > +	cvt->in_chan = cvt->out_chan = cvt->rotation_in_chan =
> > +		cvt->rotation_out_chan = NULL;
> > +	cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
> > +}
> > +
> > +static int ipu_ic_get_ipu_resources(struct image_converter *cvt)
> > +{
> > +	const struct ic_task_channels *chan = cvt->ic->ch;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	int ret;
> > +
> > +	/* get IDMAC channels */
> > +	cvt->in_chan = ipu_idmac_get(priv->ipu, chan->in);
> > +	cvt->out_chan = ipu_idmac_get(priv->ipu, chan->out);
> > +	if (IS_ERR(cvt->in_chan) || IS_ERR(cvt->out_chan)) {
> > +		dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
> > +		ret = -EBUSY;
> > +		goto err;
> > +	}
> > +
> > +	cvt->rotation_in_chan = ipu_idmac_get(priv->ipu, chan->rot_in);
> > +	cvt->rotation_out_chan = ipu_idmac_get(priv->ipu, chan->rot_out);
> > +	if (IS_ERR(cvt->rotation_in_chan) || IS_ERR(cvt->rotation_out_chan)) {
> > +		dev_err(priv->ipu->dev,
> > +			"could not acquire idmac rotation channels\n");
> > +		ret = -EBUSY;
> > +		goto err;
> > +	}
> > +
> > +	/* acquire the EOF interrupts */
> > +	cvt->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
> > +						cvt->out_chan,
> > +						IPU_IRQ_EOF);
> > +
> > +	ret = request_threaded_irq(cvt->out_eof_irq,
> > +				   ipu_ic_norotate_irq, ipu_ic_bh,
> > +				   0, "ipu-ic", cvt);
> > +	if (ret < 0) {
> > +		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
> > +			 cvt->out_eof_irq);
> > +		cvt->out_eof_irq = -1;
> > +		goto err;
> > +	}
> > +
> > +	cvt->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
> > +						     cvt->rotation_out_chan,
> > +						     IPU_IRQ_EOF);
> > +
> > +	ret = request_threaded_irq(cvt->rot_out_eof_irq,
> > +				   ipu_ic_rotate_irq, ipu_ic_bh,
> > +				   0, "ipu-ic", cvt);
> > +	if (ret < 0) {
> > +		dev_err(priv->ipu->dev, "could not acquire irq %d\n",
> > +			cvt->rot_out_eof_irq);
> > +		cvt->rot_out_eof_irq = -1;
> > +		goto err;
> > +	}
> > +
> > +	return 0;
> > +err:
> > +	ipu_ic_release_ipu_resources(cvt);
> > +	return ret;
> > +}
> > +
> > +static int ipu_ic_fill_image(struct image_converter_ctx *ctx,
> > +			     struct ipu_ic_image *ic_image,
> > +			     struct ipu_image *image,
> > +			     enum image_convert_type type)
> > +{
> > +	struct ipu_ic_priv *priv = ctx->cvt->ic->priv;
> > +
> > +	ic_image->base = *image;
> > +	ic_image->type = type;
> > +
> > +	ic_image->fmt = ipu_ic_get_format(image->pix.pixelformat);
> > +	if (!ic_image->fmt) {
> > +		dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
> > +			type == IMAGE_CONVERT_OUT ? "Output" : "Input");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (ic_image->fmt->y_depth)
> > +		ic_image->stride = (ic_image->fmt->y_depth *
> > +				    ic_image->base.pix.width) >> 3;
> > +	else
> > +		ic_image->stride  = ic_image->base.pix.bytesperline;
> > +
> > +	ipu_ic_calc_tile_dimensions(ctx, ic_image);
> > +	ipu_ic_calc_tile_offsets(ctx, ic_image);
> > +
> > +	return 0;
> > +}
> > +
> > +/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
> > +static unsigned int clamp_align(unsigned int x, unsigned int min,
> > +				unsigned int max, unsigned int align)
> > +{
> > +	/* Bits that must be zero to be aligned */
> > +	unsigned int mask = ~((1 << align) - 1);
> > +
> > +	/* Clamp to aligned min and max */
> > +	x = clamp(x, (min + ~mask) & mask, max & mask);
> > +
> > +	/* Round to nearest aligned value */
> > +	if (align)
> > +		x = (x + (1 << (align - 1))) & mask;
> > +
> > +	return x;
> > +}
> > +
> > +/*
> > + * We have to adjust the tile width such that the tile physaddrs and
> > + * U and V plane offsets are multiples of 8 bytes as required by
> > + * the IPU DMA Controller. For the planar formats, this corresponds
> > + * to a pixel alignment of 16 (but use a more formal equation since
> > + * the variables are available). For all the packed formats, 8 is
> > + * good enough.
> > + */
> > +static inline u32 tile_width_align(const struct ipu_ic_pixfmt *fmt)
> > +{
> > +	return fmt->y_depth ? (64 * fmt->uv_width_dec) / fmt->y_depth : 8;
> > +}
> > +
> > +/*
> > + * For tile height alignment, we have to ensure that the output tile
> > + * heights are multiples of 8 lines if the IRT is required by the
> > + * given rotation mode (the IRT performs rotations on 8x8 blocks
> > + * at a time). If the IRT is not used, or for input image tiles,
> > + * 2 lines are good enough.
> > + */
> > +static inline u32 tile_height_align(enum image_convert_type type,
> > +				    enum ipu_rotate_mode rot_mode)
> > +{
> > +	return (type == IMAGE_CONVERT_OUT &&
> > +		ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
> > +}
> > +
> > +/* Adjusts input/output images to IPU restrictions */
> > +int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
> > +			     enum ipu_rotate_mode rot_mode)
> > +{
> > +	const struct ipu_ic_pixfmt *infmt, *outfmt;
> > +	unsigned int num_in_rows, num_in_cols;
> > +	unsigned int num_out_rows, num_out_cols;
> > +	u32 w_align, h_align;
> > +
> > +	infmt = ipu_ic_get_format(in->pix.pixelformat);
> > +	outfmt = ipu_ic_get_format(out->pix.pixelformat);
> > +
> > +	/* set some defaults if needed */
> > +	if (!infmt) {
> > +		in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
> > +		infmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
> > +	}
> > +	if (!outfmt) {
> > +		out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
> > +		outfmt = ipu_ic_get_format(V4L2_PIX_FMT_RGB24);
> > +	}
> > +
> > +	if (!in->pix.width || !in->pix.height) {
> > +		in->pix.width = 640;
> > +		in->pix.height = 480;
> > +	}
> > +	if (!out->pix.width || !out->pix.height) {
> > +		out->pix.width = 640;
> > +		out->pix.height = 480;
> > +	}
> > +
> > +	/* image converter does not handle fields */
> > +	in->pix.field = out->pix.field = V4L2_FIELD_NONE;
> > +
> > +	/* resizer cannot downsize more than 4:1 */
> > +	if (ipu_rot_mode_is_irt(rot_mode)) {
> > +		out->pix.height = max_t(__u32, out->pix.height,
> > +					in->pix.width / 4);
> > +		out->pix.width = max_t(__u32, out->pix.width,
> > +				       in->pix.height / 4);
> > +	} else {
> > +		out->pix.width = max_t(__u32, out->pix.width,
> > +				       in->pix.width / 4);
> > +		out->pix.height = max_t(__u32, out->pix.height,
> > +					in->pix.height / 4);
> > +	}
> > +
> > +	/* get tiling rows/cols from output format */
> > +	num_out_rows = ipu_ic_num_stripes(out->pix.height);
> > +	num_out_cols = ipu_ic_num_stripes(out->pix.width);
> > +	if (ipu_rot_mode_is_irt(rot_mode)) {
> > +		num_in_rows = num_out_cols;
> > +		num_in_cols = num_out_rows;
> > +	} else {
> > +		num_in_rows = num_out_rows;
> > +		num_in_cols = num_out_cols;
> > +	}
> > +
> > +	/* align input width/height */
> > +	w_align = ilog2(tile_width_align(infmt) * num_in_cols);
> > +	h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
> > +			num_in_rows);
> > +	in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
> > +	in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
> > +
> > +	/* align output width/height */
> > +	w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
> > +	h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
> > +			num_out_rows);
> > +	out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
> > +	out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
> > +
> > +	/* set input/output strides and image sizes */
> > +	in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
> > +	in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
> > +	out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
> > +	out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
> > +
> > +/*
> > + * this is used by ipu_image_convert_prepare() to verify set input and
> > + * output images are valid before starting the conversion. Clients can
> > + * also call it before calling ipu_image_convert_prepare().
> > + */
> > +int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
> > +			     enum ipu_rotate_mode rot_mode)
> > +{
> > +	struct ipu_image testin, testout;
> > +	int ret;
> > +
> > +	testin = *in;
> > +	testout = *out;
> > +
> > +	ret = ipu_image_convert_adjust(&testin, &testout, rot_mode);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (testin.pix.width != in->pix.width ||
> > +	    testin.pix.height != in->pix.height ||
> > +	    testout.pix.width != out->pix.width ||
> > +	    testout.pix.height != out->pix.height)
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
> > +
> > +/*
> > + * Call ipu_image_convert_prepare() to prepare for the conversion of
> > + * given images and rotation mode. Returns a new conversion context.
> > + */
> > +struct image_converter_ctx *
> > +ipu_image_convert_prepare(struct ipu_ic *ic,
> > +			  struct ipu_image *in, struct ipu_image *out,
> > +			  enum ipu_rotate_mode rot_mode,
> > +			  image_converter_cb_t complete,
> > +			  void *complete_context)
> > +{
> > +	struct ipu_ic_priv *priv = ic->priv;
> > +	struct image_converter *cvt = &ic->cvt;
> > +	struct ipu_ic_image *s_image, *d_image;
> > +	struct image_converter_ctx *ctx;
> > +	unsigned long flags;
> > +	bool get_res;
> > +	int ret;
> > +
> > +	if (!ic || !in || !out || !complete)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	/* verify the in/out images before continuing */
> > +	ret = ipu_image_convert_verify(in, out, rot_mode);
> > +	if (ret) {
> > +		dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
> > +			__func__);
> > +		return ERR_PTR(ret);
> > +	}
> > +
> > +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> > +	if (!ctx)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	dev_dbg(priv->ipu->dev, "%s: ctx %p\n", __func__, ctx);
> > +
> > +	ctx->cvt = cvt;
> > +	init_completion(&ctx->aborted);
> > +
> > +	s_image = &ctx->in;
> > +	d_image = &ctx->out;
> > +
> > +	/* set tiling and rotation */
> > +	d_image->num_rows = ipu_ic_num_stripes(out->pix.height);
> > +	d_image->num_cols = ipu_ic_num_stripes(out->pix.width);
> > +	if (ipu_rot_mode_is_irt(rot_mode)) {
> > +		s_image->num_rows = d_image->num_cols;
> > +		s_image->num_cols = d_image->num_rows;
> > +	} else {
> > +		s_image->num_rows = d_image->num_rows;
> > +		s_image->num_cols = d_image->num_cols;
> > +	}
> > +
> > +	ctx->num_tiles = d_image->num_cols * d_image->num_rows;
> > +	ctx->rot_mode = rot_mode;
> > +
> > +	ret = ipu_ic_fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
> > +	if (ret)
> > +		goto out_free;
> > +	ret = ipu_ic_fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
> > +	if (ret)
> > +		goto out_free;
> > +
> > +	ipu_ic_calc_out_tile_map(ctx);
> > +
> > +	ipu_ic_dump_format(ctx, s_image);
> > +	ipu_ic_dump_format(ctx, d_image);
> > +
> > +	ctx->complete = complete;
> > +	ctx->complete_context = complete_context;
> > +
> > +	/*
> > +	 * Can we use double-buffering for this operation? If there is
> > +	 * only one tile (the whole image can be converted in a single
> > +	 * operation) there's no point in using double-buffering. Also,
> > +	 * the IPU's IDMAC channels allow only a single U and V plane
> > +	 * offset shared between both buffers, but these offsets change
> > +	 * for every tile, and therefore would have to be updated for
> > +	 * each buffer which is not possible. So double-buffering is
> > +	 * impossible when either the source or destination images are
> > +	 * a planar format (YUV420, YUV422P, etc.).
> > +	 */
> > +	ctx->double_buffering = (ctx->num_tiles > 1 &&
> > +				 !s_image->fmt->y_depth &&
> > +				 !d_image->fmt->y_depth);
> > +
> > +	if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
> > +		ret = ipu_ic_alloc_dma_buf(priv, &ctx->rot_intermediate[0],
> > +					   d_image->tile.size);
> > +		if (ret)
> > +			goto out_free;
> > +		if (ctx->double_buffering) {
> > +			ret = ipu_ic_alloc_dma_buf(priv,
> > +						   &ctx->rot_intermediate[1],
> > +						   d_image->tile.size);
> > +			if (ret)
> > +				goto out_free_dmabuf0;
> > +		}
> > +	}
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	get_res = list_empty(&cvt->ctx_list);
> > +
> > +	list_add_tail(&ctx->list, &cvt->ctx_list);
> > +
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +
> > +	if (get_res) {
> > +		ret = ipu_ic_get_ipu_resources(cvt);
> > +		if (ret)
> > +			goto out_free_dmabuf1;
> > +	}
> > +
> > +	return ctx;
> > +
> > +out_free_dmabuf1:
> > +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +	list_del(&ctx->list);
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +out_free_dmabuf0:
> > +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
> > +out_free:
> > +	kfree(ctx);
> > +	return ERR_PTR(ret);
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
> > +
> > +/*
> > + * Carry out a single image conversion. Only the physaddr's of the input
> > + * and output image buffers are needed. The conversion context must have
> > + * been created previously with ipu_image_convert_prepare(). Returns the
> > + * new run object.
> > + */
> > +struct image_converter_run *
> > +ipu_image_convert_run(struct image_converter_ctx *ctx,
> > +		      dma_addr_t in_phys, dma_addr_t out_phys)
> > +{
> > +	struct image_converter *cvt = ctx->cvt;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	struct image_converter_run *run;
> > +	unsigned long flags;
> > +	int ret = 0;
> > +
> > +	run = kzalloc(sizeof(*run), GFP_KERNEL);
> > +	if (!run)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	run->ctx = ctx;
> > +	run->in_phys = in_phys;
> > +	run->out_phys = out_phys;
> > +
> > +	dev_dbg(priv->ipu->dev, "%s: ctx %p run %p\n", __func__,
> > +		ctx, run);
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	if (ctx->aborting) {
> > +		ret = -EIO;
> > +		goto unlock;
> > +	}
> > +
> > +	list_add_tail(&run->list, &cvt->pending_q);
> > +
> > +	if (!cvt->current_run) {
> > +		ret = ipu_ic_run(run);
> > +		if (ret)
> > +			cvt->current_run = NULL;
> > +	}
> > +unlock:
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +
> > +	if (ret) {
> > +		kfree(run);
> > +		run = ERR_PTR(ret);
> > +	}
> > +
> > +	return run;
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_run);
> > +
> > +/* Abort any active or pending conversions for this context */
> > +void ipu_image_convert_abort(struct image_converter_ctx *ctx)
> > +{
> > +	struct image_converter *cvt = ctx->cvt;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	struct image_converter_run *run, *active_run, *tmp;
> > +	unsigned long flags;
> > +	int run_count, ret;
> > +	bool need_abort;
> > +
> > +	reinit_completion(&ctx->aborted);
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	/* move all remaining pending runs in this context to done_q */
> > +	list_for_each_entry_safe(run, tmp, &cvt->pending_q, list) {
> > +		if (run->ctx != ctx)
> > +			continue;
> > +		run->status = -EIO;
> > +		list_move_tail(&run->list, &cvt->done_q);
> > +	}
> > +
> > +	run_count = ipu_ic_get_run_count(ctx, &cvt->done_q);
> > +	active_run = (cvt->current_run && cvt->current_run->ctx == ctx) ?
> > +		cvt->current_run : NULL;
> > +
> > +	need_abort = (run_count || active_run);
> > +
> > +	ctx->aborting = need_abort;
> > +
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +
> > +	if (!need_abort) {
> > +		dev_dbg(priv->ipu->dev, "%s: no abort needed for ctx %p\n",
> > +			__func__, ctx);
> > +		return;
> > +	}
> > +
> > +	dev_dbg(priv->ipu->dev,
> > +		 "%s: wait for completion: %d runs, active run %p\n",
> > +		 __func__, run_count, active_run);
> > +
> > +	ret = wait_for_completion_timeout(&ctx->aborted,
> > +					  msecs_to_jiffies(10000));
> > +	if (ret == 0) {
> > +		dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
> > +		ipu_ic_force_abort(ctx);
> > +	}
> > +
> > +	ctx->aborting = false;
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
> > +
> > +/* Unprepare image conversion context */
> > +void ipu_image_convert_unprepare(struct image_converter_ctx *ctx)
> > +{
> > +	struct image_converter *cvt = ctx->cvt;
> > +	struct ipu_ic_priv *priv = cvt->ic->priv;
> > +	unsigned long flags;
> > +	bool put_res;
> > +
> > +	/* make sure no runs are hanging around */
> > +	ipu_image_convert_abort(ctx);
> > +
> > +	dev_dbg(priv->ipu->dev, "%s: removing ctx %p\n", __func__, ctx);
> > +
> > +	spin_lock_irqsave(&cvt->irqlock, flags);
> > +
> > +	list_del(&ctx->list);
> > +
> > +	put_res = list_empty(&cvt->ctx_list);
> > +
> > +	spin_unlock_irqrestore(&cvt->irqlock, flags);
> > +
> > +	if (put_res)
> > +		ipu_ic_release_ipu_resources(cvt);
> > +
> > +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[1]);
> > +	ipu_ic_free_dma_buf(priv, &ctx->rot_intermediate[0]);
> > +
> > +	kfree(ctx);
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
> > +
> > +/*
> > + * "Canned" asynchronous single image conversion. On successful return
> > + * caller must call ipu_image_convert_unprepare() after conversion completes.
> > + * Returns the new conversion context.
> > + */
> > +struct image_converter_ctx *
> > +ipu_image_convert(struct ipu_ic *ic,
> > +		  struct ipu_image *in, struct ipu_image *out,
> > +		  enum ipu_rotate_mode rot_mode,
> > +		  image_converter_cb_t complete,
> > +		  void *complete_context)
> > +{
> > +	struct image_converter_ctx *ctx;
> > +	struct image_converter_run *run;
> > +
> > +	ctx = ipu_image_convert_prepare(ic, in, out, rot_mode,
> > +					complete, complete_context);
> > +	if (IS_ERR(ctx))
> > +		return ctx;
> > +
> > +	run = ipu_image_convert_run(ctx, in->phys0, out->phys0);
> > +	if (IS_ERR(run)) {
> > +		ipu_image_convert_unprepare(ctx);
> > +		return ERR_PTR(PTR_ERR(run));
> > +	}
> > +
> > +	return ctx;
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert);
> > +
> > +/* "Canned" synchronous single image conversion */
> > +static void image_convert_sync_complete(void *data,
> > +					struct image_converter_run *run,
> > +					int err)
> > +{
> > +	struct completion *comp = data;
> > +
> > +	complete(comp);
> > +}
> > +
> > +int ipu_image_convert_sync(struct ipu_ic *ic,
> > +			   struct ipu_image *in, struct ipu_image *out,
> > +			   enum ipu_rotate_mode rot_mode)
> > +{
> > +	struct image_converter_ctx *ctx;
> > +	struct completion comp;
> > +	int ret;
> > +
> > +	init_completion(&comp);
> > +
> > +	ctx = ipu_image_convert(ic, in, out, rot_mode,
> > +				image_convert_sync_complete, &comp);
> > +	if (IS_ERR(ctx))
> > +		return PTR_ERR(ctx);
> > +
> > +	ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
> > +	ret = (ret == 0) ? -ETIMEDOUT : 0;
> > +
> > +	ipu_image_convert_unprepare(ctx);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
> > +
> >  int ipu_ic_enable(struct ipu_ic *ic)
> >  {
> >  	struct ipu_ic_priv *priv = ic->priv;
> > @@ -759,6 +2428,7 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
> >  	ipu->ic_priv = priv;
> >  
> >  	spin_lock_init(&priv->lock);
> > +
> >  	priv->base = devm_ioremap(dev, base, PAGE_SIZE);
> >  	if (!priv->base)
> >  		return -ENOMEM;
> > @@ -771,10 +2441,21 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
> >  	priv->ipu = ipu;
> >  
> >  	for (i = 0; i < IC_NUM_TASKS; i++) {
> > -		priv->task[i].task = i;
> > -		priv->task[i].priv = priv;
> > -		priv->task[i].reg = &ic_task_reg[i];
> > -		priv->task[i].bit = &ic_task_bit[i];
> > +		struct ipu_ic *ic = &priv->task[i];
> > +		struct image_converter *cvt = &ic->cvt;
> > +
> > +		ic->task = i;
> > +		ic->priv = priv;
> > +		ic->reg = &ic_task_reg[i];
> > +		ic->bit = &ic_task_bit[i];
> > +		ic->ch = &ic_task_ch[i];
> > +
> > +		cvt->ic = ic;
> > +		spin_lock_init(&cvt->irqlock);
> > +		INIT_LIST_HEAD(&cvt->ctx_list);
> > +		INIT_LIST_HEAD(&cvt->pending_q);
> > +		INIT_LIST_HEAD(&cvt->done_q);
> > +		cvt->out_eof_irq = cvt->rot_out_eof_irq = -1;
> >  	}
> >  
> >  	return 0;
> > diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
> > index 8f77ddb..5938a69 100644
> > --- a/include/video/imx-ipu-v3.h
> > +++ b/include/video/imx-ipu-v3.h
> > @@ -63,17 +63,25 @@ enum ipu_csi_dest {
> >  /*
> >   * Enumeration of IPU rotation modes
> >   */
> > +#define IPU_ROT_BIT_VFLIP (1 << 0)
> > +#define IPU_ROT_BIT_HFLIP (1 << 1)
> > +#define IPU_ROT_BIT_90    (1 << 2)
> > +
> >  enum ipu_rotate_mode {
> >  	IPU_ROTATE_NONE = 0,
> > -	IPU_ROTATE_VERT_FLIP,
> > -	IPU_ROTATE_HORIZ_FLIP,
> > -	IPU_ROTATE_180,
> > -	IPU_ROTATE_90_RIGHT,
> > -	IPU_ROTATE_90_RIGHT_VFLIP,
> > -	IPU_ROTATE_90_RIGHT_HFLIP,
> > -	IPU_ROTATE_90_LEFT,
> > +	IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
> > +	IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
> > +	IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
> > +	IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
> > +	IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
> > +	IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
> > +	IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
> > +			      IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
> >  };
> >  
> > +/* 90-degree rotations require the IRT unit */
> > +#define ipu_rot_mode_is_irt(m) ((m) >= IPU_ROTATE_90_RIGHT)
> > +
> >  enum ipu_color_space {
> >  	IPUV3_COLORSPACE_RGB,
> >  	IPUV3_COLORSPACE_YUV,
> > @@ -320,6 +328,7 @@ enum ipu_ic_task {
> >  };
> >  
> >  struct ipu_ic;
> > +
> >  int ipu_ic_task_init(struct ipu_ic *ic,
> >  		     int in_width, int in_height,
> >  		     int out_width, int out_height,
> > @@ -335,6 +344,40 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
> >  			  u32 width, u32 height, int burst_size,
> >  			  enum ipu_rotate_mode rot);
> >  int ipu_ic_set_src(struct ipu_ic *ic, int csi_id, bool vdi);
> > +
> > +struct image_converter_ctx;
> > +struct image_converter_run;
> > +
> > +typedef void (*image_converter_cb_t)(void *ctx,
> > +				     struct image_converter_run *run,
> > +				     int err);
> > +
> > +int ipu_image_convert_enum_format(int index, const char **desc, u32 *fourcc);
> > +int ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
> > +			     enum ipu_rotate_mode rot_mode);
> > +int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
> > +			     enum ipu_rotate_mode rot_mode);
> > +struct image_converter_ctx *
> > +ipu_image_convert_prepare(struct ipu_ic *ic,
> > +			  struct ipu_image *in, struct ipu_image *out,
> > +			  enum ipu_rotate_mode rot_mode,
> > +			  image_converter_cb_t complete,
> > +			  void *complete_context);
> > +void ipu_image_convert_unprepare(struct image_converter_ctx *ctx);
> > +struct image_converter_run *
> > +ipu_image_convert_run(struct image_converter_ctx *ctx,
> > +		      dma_addr_t in_phys, dma_addr_t out_phys);
> > +void ipu_image_convert_abort(struct image_converter_ctx *ctx);
> > +struct image_converter_ctx *
> > +ipu_image_convert(struct ipu_ic *ic,
> > +		  struct ipu_image *in, struct ipu_image *out,
> > +		  enum ipu_rotate_mode rot_mode,
> > +		  image_converter_cb_t complete,
> > +		  void *complete_context);
> > +int ipu_image_convert_sync(struct ipu_ic *ic,
> > +			   struct ipu_image *in, struct ipu_image *out,
> > +			   enum ipu_rotate_mode rot_mode);
> > +
> >  int ipu_ic_enable(struct ipu_ic *ic);
> >  int ipu_ic_disable(struct ipu_ic *ic);
> >  struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
> 
> 
> 
> Thanks,
> Mauro



Thanks,
Mauro

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

* Re: [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling
  2016-07-13 18:58     ` Mauro Carvalho Chehab
  2016-07-13 19:06       ` Mauro Carvalho Chehab
@ 2016-07-13 22:24       ` Steve Longerbeam
  1 sibling, 0 replies; 105+ messages in thread
From: Steve Longerbeam @ 2016-07-13 22:24 UTC (permalink / raw)
  To: Mauro Carvalho Chehab, Steve Longerbeam; +Cc: linux-media

On 07/13/2016 11:58 AM, Mauro Carvalho Chehab wrote:
>
> We don't do image rotation in software inside the Kernel! This is
> something that should be done either by some hardware block or
> in userspace.

Hi Mauro, I'm not sure I follow you. This is all hardware conversions.

>
> We also don't do image conversions inside the Kernel. Same applies
> to other similar codes on this patch.

Again, I don't follow you. Of course the kernel can do image conversion!
Again this is all hardware based image conversion, using the i.MX6 IPU
Image Converter unit.

Steve


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

end of thread, other threads:[~2016-07-13 22:24 UTC | newest]

Thread overview: 105+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-14 22:48 [PATCH 00/38] i.MX5/6 Video Capture Steve Longerbeam
2016-06-14 22:48 ` [PATCH 01/38] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
2016-06-14 22:48 ` [PATCH 02/38] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
2016-06-14 22:48 ` [PATCH 03/38] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 04/38] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 05/38] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
2016-06-14 22:49 ` [PATCH 06/38] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 07/38] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
2016-06-14 22:49 ` [PATCH 08/38] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 09/38] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
2016-06-14 22:49 ` [PATCH 10/38] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
2016-06-14 22:49 ` [PATCH 11/38] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
2016-06-14 22:49 ` [PATCH 12/38] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
2016-06-14 22:49 ` [PATCH 13/38] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
2016-06-14 22:49 ` [PATCH 14/38] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
2016-06-14 22:49 ` [PATCH 15/38] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
2016-06-14 22:49 ` [PATCH 16/38] gpu: ipu-v3: rename CSI client device Steve Longerbeam
2016-06-14 22:49 ` [PATCH 17/38] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
2016-06-14 22:49 ` [PATCH 18/38] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
2016-06-14 22:49 ` [PATCH 19/38] ARM: dts: imx6-sabrelite: add video capture ports and connections Steve Longerbeam
2016-06-16  8:32   ` [19/38] " Gary Bisson
2016-06-17 15:18     ` Gary Bisson
2016-06-17 16:09       ` Jack Mitchell
2016-06-17 19:00       ` tchellRe: " Steve Longerbeam
2016-06-18  9:05         ` Hans Verkuil
2016-06-17 19:01       ` Steve Longerbeam
2016-06-20  9:33         ` Gary Bisson
2016-06-20  9:44           ` Jack Mitchell
2016-06-20 10:16             ` Gary Bisson
2016-06-20 10:44               ` Jack Mitchell
2016-06-21 17:17                 ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 20/38] ARM: dts: imx6-sabresd: " Steve Longerbeam
2016-06-14 22:49 ` [PATCH 21/38] ARM: dts: imx6-sabreauto: create i2cmux for i2c3 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 22/38] ARM: dts: imx6-sabreauto: add reset-gpios property for max7310 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 23/38] ARM: dts: imx6-sabreauto: add pinctrl for gpt input capture Steve Longerbeam
2016-06-14 22:49 ` [PATCH 24/38] ARM: dts: imx6-sabreauto: add video capture ports and connections Steve Longerbeam
2016-06-14 22:49 ` [PATCH 25/38] ARM: dts: imx6qdl: add mem2mem device for sabre* boards Steve Longerbeam
2016-06-14 22:49 ` [PATCH 26/38] gpio: pca953x: Add reset-gpios property Steve Longerbeam
2016-06-14 22:49 ` [PATCH 27/38] clocksource/drivers/imx: add input capture support Steve Longerbeam
2016-06-14 22:49 ` [PATCH 28/38] v4l: Add signal lock status to source change events Steve Longerbeam
2016-07-01  7:24   ` Hans Verkuil
2016-07-02  3:59     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 29/38] media: Add camera interface driver for i.MX5/6 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 30/38] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
2016-06-14 22:49 ` [PATCH 31/38] media: imx: Add video switch Steve Longerbeam
2016-06-16 16:13   ` Ian Arkver
2016-06-17 20:14     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 32/38] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 33/38] media: imx: Add support for Parallel OV5642 Steve Longerbeam
2016-06-14 22:49 ` [PATCH 34/38] media: imx: Add support for ADV7180 Video Decoder Steve Longerbeam
2016-06-16 11:33   ` Lars-Peter Clausen
2016-06-17 20:33     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 35/38] media: adv7180: add power pin control Steve Longerbeam
2016-06-15 16:05   ` Lars-Peter Clausen
2016-06-16  1:34     ` Steve Longerbeam
2016-06-14 22:49 ` [PATCH 36/38] media: adv7180: implement g_parm Steve Longerbeam
2016-06-14 22:49 ` [PATCH 37/38] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
2016-06-14 22:49 ` [PATCH 38/38] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers Steve Longerbeam
2016-06-15 10:43 ` [PATCH 00/38] i.MX5/6 Video Capture Jack Mitchell
2016-06-15 10:47   ` Hans Verkuil
2016-06-16  1:37   ` Steve Longerbeam
2016-06-16  9:49     ` Jack Mitchell
2016-06-16 17:02       ` Steve Longerbeam
2016-06-16 17:43         ` Nicolas Dufresne
2016-06-16 17:43         ` Nicolas Dufresne
2016-06-17  7:10         ` Hans Verkuil
2016-06-17 17:35           ` Steve Longerbeam
2016-06-18  9:01             ` Hans Verkuil
2016-06-28 18:54 ` Tim Harvey
2016-06-28 20:10   ` Hans Verkuil
2016-07-01  7:19     ` Hans Verkuil
2016-07-02  3:38   ` Steve Longerbeam
2016-07-06 23:06 ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Steve Longerbeam
2016-07-06 23:06   ` [PATCH 01/28] gpu: ipu-v3: Add Video Deinterlacer unit Steve Longerbeam
2016-07-06 23:06   ` [PATCH 02/28] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 03/28] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 04/28] gpu: ipu-v3: Add ipu_get_num() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 05/28] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
2016-07-06 23:06   ` [PATCH 06/28] gpu: ipu-v3: Add ipu_set_vdi_src_mux() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 07/28] gpu: ipu-v3: Add VDI input IDMAC channels Steve Longerbeam
2016-07-06 23:06   ` [PATCH 08/28] gpu: ipu-v3: Add ipu_csi_set_src() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 09/28] gpu: ipu-v3: Add ipu_ic_set_src() Steve Longerbeam
2016-07-06 23:06   ` [PATCH 10/28] gpu: ipu-v3: set correct full sensor frame for PAL/NTSC Steve Longerbeam
2016-07-06 23:06   ` [PATCH 11/28] gpu: ipu-v3: Fix CSI data format for 16-bit media bus formats Steve Longerbeam
2016-07-06 23:06   ` [PATCH 12/28] gpu: ipu-v3: Fix CSI0 blur in NTSC format Steve Longerbeam
2016-07-06 23:06   ` [PATCH 13/28] gpu: ipu-v3: Fix IRT usage Steve Longerbeam
2016-07-06 23:06   ` [PATCH 14/28] gpu: ipu-ic: Add complete image conversion support with tiling Steve Longerbeam
2016-07-13 18:58     ` Mauro Carvalho Chehab
2016-07-13 19:06       ` Mauro Carvalho Chehab
2016-07-13 22:24       ` Steve Longerbeam
2016-07-06 23:06   ` [PATCH 15/28] gpu: ipu-ic: allow multiple handles to ic Steve Longerbeam
2016-07-06 23:06   ` [PATCH 16/28] gpu: ipu-v3: rename CSI client device Steve Longerbeam
2016-07-06 23:06   ` [PATCH 17/28] gpio: pca953x: Add optional reset gpio control Steve Longerbeam
2016-07-06 23:06   ` [PATCH 18/28] clocksource/drivers/imx: add input capture support Steve Longerbeam
2016-07-06 23:06   ` [PATCH 19/28] media: Add i.MX5/6 camera interface driver Steve Longerbeam
2016-07-06 23:06   ` [PATCH 20/28] media: imx: Add MIPI CSI-2 Receiver driver Steve Longerbeam
2016-07-06 23:06   ` [PATCH 21/28] media: imx: Add video switch Steve Longerbeam
2016-07-06 23:06   ` [PATCH 22/28] media: imx: Add support for MIPI CSI-2 OV5640 Steve Longerbeam
2016-07-06 23:06   ` [PATCH 23/28] media: imx: Add support for Parallel OV5642 Steve Longerbeam
2016-07-06 23:06   ` [PATCH 24/28] media: Add i.MX5/6 mem2mem driver Steve Longerbeam
2016-07-06 23:06   ` [PATCH 25/28] ARM: dts: imx6qdl: Flesh out MIPI CSI2 receiver node Steve Longerbeam
2016-07-06 23:06   ` [PATCH 26/28] ARM: dts: imx6qdl: Add mipi_ipu1/2 video muxes, mipi_csi, and their connections Steve Longerbeam
2016-07-06 23:11   ` [PATCH 27/28] ARM: dts: imx6qdl: add mem2mem devices Steve Longerbeam
2016-07-06 23:11     ` [PATCH 28/28] ARM: imx_v6_v7_defconfig: Enable staging video4linux drivers Steve Longerbeam
2016-07-07 13:31   ` [PATCH 00/28] i.MX5/6 Video Capture, v2 Tim Harvey

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.