diff mbox

[34/38] media: imx: Add support for ADV7180 Video Decoder

Message ID 1465944574-15745-35-git-send-email-steve_longerbeam@mentor.com (mailing list archive)
State New, archived
Headers show

Commit Message

Steve Longerbeam June 14, 2016, 10:49 p.m. UTC
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

Comments

Lars-Peter Clausen June 16, 2016, 11:33 a.m. UTC | #1
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

--
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
Steve Longerbeam June 17, 2016, 8:33 p.m. UTC | #2
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

--
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
diff mbox

Patch

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