summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSanchayan Maity <maitysanchayan@gmail.com>2016-05-18 18:37:45 +0530
committerMax Krummenacher <max.krummenacher@toradex.com>2016-06-24 11:08:17 +0200
commit481bf320816c56c726197d6155341c2f94aeb6be (patch)
tree9d9a4fcc0507cd5aab24e22ebd158b4b52ac3843
parentf1b52e225091711d60704717af34c2f321c032b3 (diff)
media: Add support for ADV7280 decoder driver
Add support for ADV7280 decoder found on Toradex Analogue Camera Adapter V2.0A Signed-off-by: Sanchayan Maity <sanchayan.maity@toradex.com> Acked-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
-rw-r--r--drivers/media/platform/mxc/capture/Kconfig6
-rw-r--r--drivers/media/platform/mxc/capture/Makefile3
-rw-r--r--drivers/media/platform/mxc/capture/adv7280.c1080
-rw-r--r--include/media/v4l2-chip-ident.h3
4 files changed, 1092 insertions, 0 deletions
diff --git a/drivers/media/platform/mxc/capture/Kconfig b/drivers/media/platform/mxc/capture/Kconfig
index 2d42c571fce2..e5758bd4db68 100644
--- a/drivers/media/platform/mxc/capture/Kconfig
+++ b/drivers/media/platform/mxc/capture/Kconfig
@@ -41,6 +41,12 @@ config MXC_TVIN_ADV7180
---help---
If you plan to use the adv7180 video decoder with your MXC system, say Y here.
+config MXC_TVIN_ADV7280
+ tristate "Analog Device adv7280 TV Decoder Input support"
+ depends on !VIDEO_MXC_EMMA_CAMERA && I2C
+ ---help---
+ If you plan to use the adv7280 video decoder with your MXC system, say Y here.
+
config MXC_TVIN_MAX9526
tristate "Maxim Integrated MAX9526 NTSC/PAL Decoder Input support"
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
diff --git a/drivers/media/platform/mxc/capture/Makefile b/drivers/media/platform/mxc/capture/Makefile
index bfaf31163cca..ed7a165edd89 100644
--- a/drivers/media/platform/mxc/capture/Makefile
+++ b/drivers/media/platform/mxc/capture/Makefile
@@ -21,6 +21,9 @@ obj-$(CONFIG_MXC_HDMI_CSI2_TC358743) += tc358743_h2c_bridge.o
adv7180_tvin-objs := adv7180.o
obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o
+adv7280_tvin-objs := adv7280.o
+obj-$(CONFIG_MXC_TVIN_ADV7280) += adv7280_tvin.o
+
max9526_tvin-objs := max9526.o
obj-$(CONFIG_MXC_TVIN_MAX9526) += max9526_tvin.o
diff --git a/drivers/media/platform/mxc/capture/adv7280.c b/drivers/media/platform/mxc/capture/adv7280.c
new file mode 100644
index 000000000000..b31e09ab9414
--- /dev/null
+++ b/drivers/media/platform/mxc/capture/adv7280.c
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2016 Toradex AG.
+ *
+ * Based on adv7180/max9526 driver for iMX6 and adv7280 driver for Tegra
+ * by Antmicro
+ *
+ * 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
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <media/v4l2-chip-ident.h>
+#include "v4l2-int-device.h"
+#include "mxc_v4l2_capture.h"
+
+#define ADV7280_VOLTAGE_ANALOG 1800000
+#define ADV7280_VOLTAGE_DIGITAL_CORE 1800000
+#define ADV7280_VOLTAGE_DIGITAL_IO 3300000
+#define ADV7280_VOLTAGE_PLL 1800000
+
+static struct regulator *dvddio_regulator;
+static struct regulator *dvdd_regulator;
+static struct regulator *avdd_regulator;
+static struct regulator *pvdd_regulator;
+static int pwn_gpio;
+
+struct sensor {
+ struct sensor_data sen;
+ v4l2_std_id std_id;
+ int rev_id;
+} adv7280_data;
+
+typedef enum {
+ ADV7280_NTSC = 0, /*!< Locked on (M) NTSC video signal. */
+ ADV7280_PAL, /*!< (B, G, H, I, N)PAL video signal. */
+ ADV7280_NOT_LOCKED, /*!< Not locked on a signal. */
+} video_fmt_idx;
+
+struct video_fmt_t {
+ int v4l2_id;
+ char name[16];
+ u16 raw_width;
+ u16 raw_height;
+ u16 active_width;
+ u16 active_height;
+ int frame_rate;
+};
+
+static const struct video_fmt_t video_fmts[] = {
+ {
+ .v4l2_id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .raw_width = 720,
+ .raw_height = 525,
+ .active_width = 720,
+ .active_height = 480,
+ .frame_rate = 30,
+ },
+ {
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .raw_width = 720,
+ .raw_height = 625,
+ .active_width = 720,
+ .active_height = 576,
+ .frame_rate = 25,
+ },
+ {
+ .v4l2_id = V4L2_STD_ALL,
+ .name = "Autodetect",
+ .raw_width = 720,
+ .raw_height = 625,
+ .active_width = 720,
+ .active_height = 576,
+ .frame_rate = 0,
+ },
+};
+
+static video_fmt_idx video_idx = ADV7280_PAL;
+
+static DEFINE_MUTEX(mutex);
+
+#define IF_NAME "adv7280"
+
+#define ADV7280_INPUT_CONTROL 0x00
+#define ADV7280_VIDEO_SELECTION_1 0x01
+#define ADV7280_VIDEO_SELECTION_2 0x02
+#define ADV7280_OUTPUT_CONTROL 0x03
+#define ADV7280_EXTENDED_OUTPUT_CONTROL 0x04
+#define ADV7280_AUTODETECT_ENABLE 0x07
+#define ADV7280_BRIGHTNESS 0x0A
+#define ADV7280_ADI_CONTROL_1 0x0E
+#define ADV7280_POWER_MANAGEMENT 0x0F
+#define ADV7280_STATUS_1 0x10
+#define ADV7280_IDENT 0x11
+#define ADV7280_STATUS_3 0x13
+#define ADV7280_SHAPING_FILTER_CONTROL_1 0x17
+#define ADV7280_ADI_CONTROL_2 0x1D
+#define ADV7280_PIXEL_DELAY_CONTROL 0x27
+#define ADV7280_VPP_SLAVE_ADDRESS 0xFD
+#define ADV7280_OUTPUT_SYNC_SELECT_2 0x6B
+#define ADV7280_SD_SATURATION_CB 0xe3
+#define ADV7280_SD_SATURATION_CR 0xe4
+
+#define HW_DEINT /* Enable hardware deinterlacer */
+#define VPP_SLAVE_ADDRESS 0x42
+
+#define VPP_DEINT_RESET 0x41
+#define VPP_I2C_DEINT_ENABLE 0x55
+#define VPP_ADV_TIMING_MODE_EN 0x5B
+
+#define ADV7280_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
+#define ADV7280_AUTODETECT_DEFAULT 0x7f
+
+#define ADV7280_INPUT_CONTROL_COMPOSITE_IN1 0x00
+#define ADV7280_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
+
+#define ADV7280_INPUT_CONTROL_NTSC_M 0x50
+#define ADV7280_INPUT_CONTROL_PAL60 0x60
+#define ADV7280_INPUT_CONTROL_NTSC_443 0x70
+#define ADV7280_INPUT_CONTROL_PAL_BG 0x80
+#define ADV7280_INPUT_CONTROL_PAL_N 0x90
+#define ADV7280_INPUT_CONTROL_PAL_M 0xa0
+#define ADV7280_INPUT_CONTROL_PAL_M_PED 0xb0
+#define ADV7280_INPUT_CONTROL_PAL_COMB_N 0xc0
+#define ADV7280_INPUT_CONTROL_PAL_COMB_N_PED 0xd0
+#define ADV7280_INPUT_CONTROL_PAL_SECAM 0xe0
+
+#define ADV7280_STATUS1_IN_LOCK 0x01
+/* Autodetection results */
+#define ADV7280_STATUS1_AUTOD_MASK 0x70
+#define ADV7280_STATUS1_AUTOD_NTSM_M_J 0x00
+#define ADV7280_STATUS1_AUTOD_NTSC_4_43 0x10
+#define ADV7280_STATUS1_AUTOD_PAL_M 0x20
+#define ADV7280_STATUS1_AUTOD_PAL_60 0x30
+#define ADV7280_STATUS1_AUTOD_PAL_B_G 0x40
+#define ADV7280_STATUS1_AUTOD_SECAM 0x50
+#define ADV7280_STATUS1_AUTOD_PAL_COMB 0x60
+#define ADV7280_STATUS1_AUTOD_SECAM_525 0x70
+
+static const struct v4l2_queryctrl adv7280_qctrl[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 127,
+ .flags = 0,
+ }
+};
+
+static inline void adv7280_power_down(int enable)
+{
+ if (gpio_is_valid(pwn_gpio)) {
+ gpio_set_value_cansleep(pwn_gpio, !enable);
+ msleep(2);
+ }
+}
+
+static int adv7280_regulator_enable(struct device *dev)
+{
+ int ret = 0;
+
+ dvddio_regulator = devm_regulator_get(dev, "DOVDD");
+
+ if (!IS_ERR(dvddio_regulator)) {
+ regulator_set_voltage(dvddio_regulator,
+ ADV7280_VOLTAGE_DIGITAL_IO,
+ ADV7280_VOLTAGE_DIGITAL_IO);
+ ret = regulator_enable(dvddio_regulator);
+ if (ret) {
+ dev_err(dev, "set io voltage failed\n");
+ return ret;
+ } else {
+ dev_dbg(dev, "set io voltage ok\n");
+ }
+ } else {
+ dev_warn(dev, "cannot get io voltage\n");
+ dvddio_regulator = NULL;
+ }
+
+ dvdd_regulator = devm_regulator_get(dev, "DVDD");
+ if (!IS_ERR(dvdd_regulator)) {
+ regulator_set_voltage(dvdd_regulator,
+ ADV7280_VOLTAGE_DIGITAL_CORE,
+ ADV7280_VOLTAGE_DIGITAL_CORE);
+ ret = regulator_enable(dvdd_regulator);
+ if (ret) {
+ dev_err(dev, "set core voltage failed\n");
+ return ret;
+ } else {
+ dev_dbg(dev, "set core voltage ok\n");
+ }
+ } else {
+ dev_warn(dev, "cannot get core voltage\n");
+ dvdd_regulator = NULL;
+ }
+
+ avdd_regulator = devm_regulator_get(dev, "AVDD");
+ if (!IS_ERR(avdd_regulator)) {
+ regulator_set_voltage(avdd_regulator,
+ ADV7280_VOLTAGE_ANALOG,
+ ADV7280_VOLTAGE_ANALOG);
+ ret = regulator_enable(avdd_regulator);
+ if (ret) {
+ dev_err(dev, "set analog voltage failed\n");
+ return ret;
+ } else {
+ dev_dbg(dev, "set analog voltage ok\n");
+ }
+ } else {
+ dev_warn(dev, "cannot get analog voltage\n");
+ avdd_regulator = NULL;
+ }
+
+ pvdd_regulator = devm_regulator_get(dev, "PVDD");
+ if (!IS_ERR(pvdd_regulator)) {
+ regulator_set_voltage(pvdd_regulator,
+ ADV7280_VOLTAGE_PLL,
+ ADV7280_VOLTAGE_PLL);
+ ret = regulator_enable(pvdd_regulator);
+ if (ret) {
+ dev_err(dev, "set pll voltage failed\n");
+ return ret;
+ } else {
+ dev_dbg(dev, "set pll voltage ok\n");
+ }
+ } else {
+ dev_warn(dev, "cannot get pll voltage\n");
+ pvdd_regulator = NULL;
+ }
+
+ return ret;
+}
+
+static inline int adv7280_read(u8 reg)
+{
+ int val;
+
+ val = i2c_smbus_read_byte_data(adv7280_data.sen.i2c_client, reg);
+ if (val < 0) {
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ "%s:read reg error: reg=%2x\n", __func__, reg);
+ return val;
+ }
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ "%s:read reg 0x%2x, val: 0x%2x\n", __func__, reg, val);
+
+ return val;
+}
+
+static int adv7280_write_reg(u8 reg, u8 val)
+{
+ s32 ret;
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ "%s:write reg 0x%2x, val: 0x%2x\n", __func__, reg, val);
+
+ ret = i2c_smbus_write_byte_data(adv7280_data.sen.i2c_client, reg, val);
+ if (ret < 0) {
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ "%s:write reg error:reg=%2x,val=%2x\n", __func__,
+ reg, val);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adv7280_write_reg_with_client(struct i2c_client *client,
+ u8 reg, u8 val)
+{
+ s32 ret;
+
+ dev_dbg(&client->dev,
+ "%s:write reg 0x%2x, val: 0x%2x\n", __func__, reg, val);
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0) {
+ dev_dbg(&client->dev,
+ "%s:write reg error:reg=%2x,val=%2x\n", __func__,
+ reg, val);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void adv7280_get_std(v4l2_std_id *std)
+{
+ int status_1, standard, idx;
+ bool locked = 0;
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "In %s\n", __func__);
+
+ status_1 = adv7280_read(ADV7280_STATUS_1);
+ locked = status_1 & ADV7280_STATUS1_IN_LOCK;
+ standard = status_1 & ADV7280_STATUS1_AUTOD_MASK;
+
+ mutex_lock(&mutex);
+ *std = V4L2_STD_ALL;
+ idx = ADV7280_NOT_LOCKED;
+ if (locked) {
+ if (standard == ADV7280_STATUS1_AUTOD_PAL_B_G) {
+ *std = V4L2_STD_PAL;
+ idx = ADV7280_PAL;
+ } else if (standard == ADV7280_STATUS1_AUTOD_NTSM_M_J) {
+ *std = V4L2_STD_NTSC;
+ idx = ADV7280_NTSC;
+ }
+ }
+ mutex_unlock(&mutex);
+
+ /* This assumes autodetect which this device uses. */
+ if (*std != adv7280_data.std_id) {
+ video_idx = idx;
+ adv7280_data.std_id = *std;
+ adv7280_data.sen.pix.width = video_fmts[video_idx].raw_width;
+ adv7280_data.sen.pix.height = video_fmts[video_idx].raw_height;
+ }
+}
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ struct device *dev = &adv7280_data.sen.i2c_client->dev;
+
+ dev_dbg(dev, "adv7280: %s\n", __func__);
+
+ if (s == NULL) {
+ dev_err(dev, " ERROR!! no slave device set!\n");
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*p));
+ p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */
+#if 0
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.nobt_hs_inv = 1;
+ p->u.bt656.bt_sync_correct = 1;
+#else
+ p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT;
+ p->u.bt656.nobt_hs_inv = 0;
+ p->u.bt656.bt_sync_correct = 0;
+#ifdef HW_DEINT
+ p->u.bt656.clock_curr = 1; /* BT656 de-interlace clock mode */
+#endif
+#endif
+ /* ADV7280 has a dedicated clock so no clock settings needed. */
+
+ return 0;
+}
+
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct sensor *sensor = s->priv;
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "adv7280: %s\n", __func__);
+
+ if (on && !sensor->sen.on) {
+ if (adv7280_write_reg(ADV7280_POWER_MANAGEMENT, 0x04) != 0)
+ return -EIO;
+
+ msleep(400);
+
+ } else if (!on && sensor->sen.on) {
+ if (adv7280_write_reg(ADV7280_POWER_MANAGEMENT, 0x24) != 0)
+ return -EIO;
+ }
+
+ sensor->sen.on = on;
+
+ return 0;
+}
+
+static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+ struct device *dev = &adv7280_data.sen.i2c_client->dev;
+
+ dev_dbg(dev, "adv7280: %s\n", __func__);
+
+ switch (a->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ dev_dbg(dev, " type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cparm->capability = sensor->sen.streamcap.capability;
+ cparm->timeperframe = sensor->sen.streamcap.timeperframe;
+ cparm->capturemode = sensor->sen.streamcap.capturemode;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ break;
+
+ default:
+ dev_dbg(dev, "ioctl_g_parm:type is unknown %d\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct device *dev = &adv7280_data.sen.i2c_client->dev;
+
+ dev_dbg(dev, "adv7280: %s\n", __func__);
+
+ switch (a->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ break;
+
+ default:
+ dev_dbg(dev, " type is unknown - %d\n", a->type);
+ break;
+ }
+
+ return 0;
+}
+
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct device *dev = &adv7280_data.sen.i2c_client->dev;
+ struct sensor *sensor = s->priv;
+ v4l2_std_id std;
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "adv7280: %s\n", __func__);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ dev_dbg(dev, " Returning size of %dx%d\n",
+ sensor->sen.pix.width, sensor->sen.pix.height);
+ f->fmt.pix = sensor->sen.pix;
+ break;
+
+ case V4L2_BUF_TYPE_PRIVATE:
+ adv7280_get_std(&std);
+ f->fmt.pix.pixelformat = (u32)std;
+ break;
+
+ default:
+ f->fmt.pix = sensor->sen.pix;
+ break;
+ }
+
+ return 0;
+}
+
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "adv7280: %s\n", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(adv7280_qctrl); i++)
+ if (qc->id && qc->id == adv7280_qctrl[i].id) {
+ *qc = adv7280_qctrl[i];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int ret = 0;
+ int sat = 0;
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "adv7280: %s\n", __func__);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_BRIGHTNESS\n");
+ adv7280_data.sen.brightness = adv7280_read(ADV7280_BRIGHTNESS);
+ vc->value = adv7280_data.sen.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_CONTRAST\n");
+ vc->value = adv7280_data.sen.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_SATURATION\n");
+ sat = adv7280_read(ADV7280_SD_SATURATION_CB);
+ adv7280_data.sen.saturation = sat;
+ vc->value = adv7280_data.sen.saturation;
+ break;
+ case V4L2_CID_HUE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_HUE\n");
+ vc->value = adv7280_data.sen.hue;
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_RED_BALANCE\n");
+ vc->value = adv7280_data.sen.red;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_BLUE_BALANCE\n");
+ vc->value = adv7280_data.sen.blue;
+ break;
+ case V4L2_CID_GAMMA:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_EXPOSURE\n");
+ vc->value = adv7280_data.sen.ae_mode;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_VFLIP\n");
+ break;
+ default:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " Default case\n");
+ vc->value = 0;
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
+{
+ int retval = 0;
+ u8 tmp;
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "adv7280: %s\n", __func__);
+
+ switch (vc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_BRIGHTNESS\n");
+ tmp = vc->value;
+ adv7280_write_reg(ADV7280_BRIGHTNESS, tmp);
+ adv7280_data.sen.brightness = vc->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_SATURATION\n");
+ tmp = vc->value;
+ adv7280_write_reg(ADV7280_SD_SATURATION_CB, tmp);
+ adv7280_write_reg(ADV7280_SD_SATURATION_CR, tmp);
+ adv7280_data.sen.saturation = vc->value;
+ break;
+ case V4L2_CID_HUE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_HUE\n");
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_AUTO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_DO_WHITE_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_DO_WHITE_BALANCE\n");
+ break;
+ case V4L2_CID_RED_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_RED_BALANCE\n");
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_BLUE_BALANCE\n");
+ break;
+ case V4L2_CID_GAMMA:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_GAMMA\n");
+ break;
+ case V4L2_CID_EXPOSURE:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_EXPOSURE\n");
+ break;
+ case V4L2_CID_AUTOGAIN:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_AUTOGAIN\n");
+ break;
+ case V4L2_CID_GAIN:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_GAIN\n");
+ break;
+ case V4L2_CID_HFLIP:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_HFLIP\n");
+ break;
+ case V4L2_CID_VFLIP:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " V4L2_CID_VFLIP\n");
+ break;
+ default:
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ " Default case\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ return retval;
+}
+
+static int ioctl_enum_framesizes(struct v4l2_int_device *s,
+ struct v4l2_frmsizeenum *fsize)
+{
+ if (fsize->index >= 1)
+ return -EINVAL;
+
+ fsize->discrete.width = video_fmts[video_idx].active_width;
+ fsize->discrete.height = video_fmts[video_idx].active_height;
+
+ return 0;
+}
+
+static int ioctl_enum_frameintervals(struct v4l2_int_device *s,
+ struct v4l2_frmivalenum *fival)
+{
+ struct video_fmt_t fmt;
+ int i;
+
+ if (fival->index != 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(video_fmts) - 1; i++) {
+ fmt = video_fmts[i];
+ if (fival->width == fmt.active_width &&
+ fival->height == fmt.active_height) {
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator = fmt.frame_rate;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id)
+{
+ ((struct v4l2_dbg_chip_ident *)id)->match.type =
+ V4L2_CHIP_MATCH_I2C_DRIVER;
+ strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name,
+ "adv7280_decoder");
+ ((struct v4l2_dbg_chip_ident *)id)->ident = V4L2_IDENT_ADV7280;
+
+ return 0;
+}
+
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "adv7280: %s\n", __func__);
+
+ return 0;
+}
+
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ dev_dbg(&adv7280_data.sen.i2c_client->dev, "adv7280: %s\n", __func__);
+
+ return 0;
+}
+
+static struct v4l2_int_ioctl_desc adv7280_ioctl_desc[] = {
+
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*)ioctl_dev_init},
+
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func*)ioctl_s_power},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*)ioctl_g_ifparm},
+ {vidioc_int_init_num, (v4l2_int_ioctl_func*)ioctl_init},
+
+ {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap},
+
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func*)ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func*)ioctl_s_parm},
+ {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func*)ioctl_queryctrl},
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func*)ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func*)ioctl_s_ctrl},
+ {vidioc_int_enum_framesizes_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_framesizes},
+ {vidioc_int_enum_frameintervals_num,
+ (v4l2_int_ioctl_func *)
+ ioctl_enum_frameintervals},
+ {vidioc_int_g_chip_ident_num,
+ (v4l2_int_ioctl_func *)ioctl_g_chip_ident},
+};
+
+static struct v4l2_int_slave adv7280_slave = {
+ .ioctls = adv7280_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(adv7280_ioctl_desc),
+};
+
+static struct v4l2_int_device adv7280_int_device = {
+ .module = THIS_MODULE,
+ .name = "adv7280",
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &adv7280_slave,
+ },
+};
+
+static int adv7280_hard_reset(void)
+{
+ int ret;
+
+ struct i2c_client vpp_client = {
+ .flags = adv7280_data.sen.i2c_client->flags,
+ .addr = VPP_SLAVE_ADDRESS,
+ .name = "ADV7280_VPP",
+ .adapter = adv7280_data.sen.i2c_client->adapter,
+ .dev = adv7280_data.sen.i2c_client->dev,
+ };
+
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ "In %s\n", __func__);
+
+ /* Reset */
+ ret = adv7280_write_reg(ADV7280_POWER_MANAGEMENT, 0xA0);
+ if (ret < 0)
+ goto error;
+ msleep(10);
+
+ /* Initialize adv7280 */
+ /* Exit Power Down Mode */
+ ret = adv7280_write_reg(ADV7280_POWER_MANAGEMENT, 0x00);
+ if (ret < 0)
+ goto error;
+
+ /* analog devices recommends */
+ ret = adv7280_write_reg(ADV7280_ADI_CONTROL_1, 0x80);
+ if (ret < 0)
+ goto error;
+
+ /* analog devices recommends */
+ ret = adv7280_write_reg(0x9C, 0x00);
+ if (ret < 0)
+ goto error;
+
+ /* analog devices recommends */
+ ret = adv7280_write_reg(0x9C, 0xFF);
+ if (ret < 0)
+ goto error;
+
+ /* Enter User Sub Map */
+ ret = adv7280_write_reg(ADV7280_ADI_CONTROL_1, 0x00);
+ if (ret < 0)
+ goto error;
+
+ /* Enable Pixel & Sync output drivers */
+ ret = adv7280_write_reg(ADV7280_OUTPUT_CONTROL, 0x0C);
+ if (ret < 0)
+ goto error;
+
+ /* Power-up INTRQ, HS & VS pads */
+ ret = adv7280_write_reg(ADV7280_EXTENDED_OUTPUT_CONTROL, 0x07);
+ if (ret < 0)
+ goto error;
+
+ /* Enable SH1 */
+ /*
+ ret = adv7280_write_reg(ADV7280_SHAPING_FILTER_CONTROL_1, 0x41);
+ if (ret < 0)
+ goto error;
+ */
+
+ /* Disable comb filtering */
+ /*
+ ret = adv7280_write_reg(0x39, 0x24);
+ if (ret < 0)
+ goto error;
+ */
+
+ /* Enable LLC output driver */
+ ret = adv7280_write_reg(ADV7280_ADI_CONTROL_2, 0x40);
+ if (ret < 0)
+ goto error;
+
+ /* VSYNC on VS/FIELD/SFL pin */
+ ret = adv7280_write_reg(ADV7280_OUTPUT_SYNC_SELECT_2, 0x01);
+ if (ret < 0)
+ goto error;
+
+ /* Enable autodetection */
+ ret = adv7280_write_reg(ADV7280_INPUT_CONTROL,
+ ADV7280_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM |
+ (ADV7280_INPUT_CONTROL_COMPOSITE_IN1 +
+ 0));
+ if (ret < 0)
+ goto error;
+
+ ret = adv7280_write_reg(ADV7280_AUTODETECT_ENABLE,
+ ADV7280_AUTODETECT_DEFAULT);
+ if (ret < 0)
+ goto error;
+
+ /* ITU-R BT.656-4 compatible */
+ /*ret = adv7280_write_reg(ADV7280_EXTENDED_OUTPUT_CONTROL,
+ ADV7280_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
+ if (ret < 0)
+ goto error;
+ */
+
+ /* analog devices recommends */
+ ret = adv7280_write_reg(0x52, 0xCD);
+ if (ret < 0)
+ goto error;
+
+ /* analog devices recommends */
+ ret = adv7280_write_reg(0x80, 0x51);
+ if (ret < 0)
+ goto error;
+
+ /* analog devices recommends */
+ ret = adv7280_write_reg(0x81, 0x51);
+ if (ret < 0)
+ goto error;
+
+ /* analog devices recommends */
+ ret = adv7280_write_reg(0x82, 0x68);
+ if (ret < 0)
+ goto error;
+
+#ifdef HW_DEINT
+ /* Set VPP Map */
+ ret = adv7280_write_reg(ADV7280_VPP_SLAVE_ADDRESS, (VPP_SLAVE_ADDRESS << 1));
+ if (ret < 0)
+ goto error;
+
+ /* VPP - not documented */
+ ret = adv7280_write_reg_with_client(&vpp_client,
+ 0xA3, 0x00);
+ if (ret < 0)
+ goto error;
+
+ /* VPP - Enbable Advanced Timing Mode */
+ ret = adv7280_write_reg_with_client(&vpp_client,
+ VPP_ADV_TIMING_MODE_EN, 0x00);
+ if (ret < 0)
+ goto error;
+
+ /* VPP - Enable Deinterlacer */
+ ret = adv7280_write_reg_with_client(&vpp_client,
+ VPP_I2C_DEINT_ENABLE, 0x80);
+ if (ret < 0)
+ goto error;
+#endif
+
+ return 0;
+
+error:
+ return ret;
+}
+
+static int adv7280_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct pinctrl *pinctrl;
+ struct device *dev = &client->dev;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(dev, "Required functionality not supported by I2C adapter\n");
+ return -EIO;
+ }
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ pinctrl = devm_pinctrl_get_select_default(dev);
+ if (IS_ERR(pinctrl)) {
+ dev_err(dev, "setup pinctrl failed\n");
+ return PTR_ERR(pinctrl);
+ }
+
+ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
+ if (!gpio_is_valid(pwn_gpio))
+ dev_warn(dev, "no sensor pwdn pin available\n");
+ else {
+ ret = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
+ "adv7280_pwdn");
+ if (ret < 0)
+ dev_err(dev, "no power pin available!\n");
+ }
+
+ adv7280_regulator_enable(dev);
+
+ adv7280_power_down(0);
+
+ msleep(1);
+
+ memset(&adv7280_data, 0, sizeof(adv7280_data));
+ adv7280_data.sen.i2c_client = client;
+ adv7280_data.sen.streamcap.timeperframe.denominator = 30;
+ adv7280_data.sen.streamcap.timeperframe.numerator = 1;
+ adv7280_data.std_id = V4L2_STD_ALL;
+ video_idx = ADV7280_NOT_LOCKED;
+ adv7280_data.sen.pix.width = video_fmts[video_idx].raw_width;
+ adv7280_data.sen.pix.height = video_fmts[video_idx].raw_height;
+ adv7280_data.sen.pix.pixelformat = V4L2_PIX_FMT_UYVY; /* YUV422 */
+ adv7280_data.sen.pix.priv = 1; /* 1 is used to indicate TV in */
+ adv7280_data.sen.on = true;
+
+ adv7280_data.sen.sensor_clk = devm_clk_get(dev, "csi_mclk");
+ if (IS_ERR(adv7280_data.sen.sensor_clk)) {
+ dev_err(dev, "get mclk failed\n");
+ return PTR_ERR(adv7280_data.sen.sensor_clk);
+ }
+
+ ret = of_property_read_u32(dev->of_node, "mclk",
+ &adv7280_data.sen.mclk);
+ if (ret) {
+ dev_err(dev, "mclk frequency is invalid\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(
+ dev->of_node, "mclk_source",
+ (u32 *) &(adv7280_data.sen.mclk_source));
+ if (ret) {
+ dev_err(dev, "mclk_source invalid\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "csi_id",
+ &(adv7280_data.sen.csi));
+ if (ret) {
+ dev_err(dev, "csi_id invalid\n");
+ return ret;
+ }
+
+ /* ADV7280 is always parallel IF */
+ adv7280_data.sen.mipi_camera = 0;
+
+ clk_prepare_enable(adv7280_data.sen.sensor_clk);
+
+ adv7280_data.rev_id = adv7280_read(ADV7280_IDENT);
+ switch (adv7280_data.rev_id) {
+ case 0x42:
+ dev_dbg(dev,
+ "%s:Analog Device adv7280 ident %2X detected!\n",
+ __func__, adv7280_data.rev_id);
+ break;
+ default:
+ dev_err(dev,
+ "%s:Analog Device adv7280 not detected %d!\n", __func__,
+ adv7280_data.rev_id);
+ return -ENODEV;
+
+ }
+
+ dev_dbg(dev, " type is %d (expect %d)\n",
+ adv7280_int_device.type, v4l2_int_type_slave);
+ dev_dbg(dev, " num ioctls is %d\n",
+ adv7280_int_device.u.slave->num_ioctls);
+
+ ret = adv7280_hard_reset();
+ if (ret) {
+ dev_err(dev, "error resetting adv7280\n");
+ return ret;
+ }
+
+ /* This function attaches this structure to the /dev/video0 device.
+ * The pointer in priv points to the adv7280_data structure here.*/
+ adv7280_int_device.priv = &adv7280_data;
+ ret = v4l2_int_device_register(&adv7280_int_device);
+
+ clk_disable_unprepare(adv7280_data.sen.sensor_clk);
+
+ return 0;
+}
+
+static int adv7280_detach(struct i2c_client *client)
+{
+ dev_dbg(&adv7280_data.sen.i2c_client->dev,
+ "%s:Removing %s video decoder @ 0x%02X from adapter %s\n",
+ __func__, IF_NAME, client->addr << 1, client->adapter->name);
+
+ adv7280_write_reg(ADV7280_POWER_MANAGEMENT, 0x24);
+
+ if (dvddio_regulator)
+ regulator_disable(dvddio_regulator);
+
+ if (dvdd_regulator)
+ regulator_disable(dvdd_regulator);
+
+ if (avdd_regulator)
+ regulator_disable(avdd_regulator);
+
+ if (pvdd_regulator)
+ regulator_disable(pvdd_regulator);
+
+ v4l2_int_device_unregister(&adv7280_int_device);
+
+ return 0;
+}
+
+static const struct i2c_device_id adv7280_id[] = {
+ {"adv7280", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, adv7280_id);
+
+static struct i2c_driver adv7280_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7280",
+ },
+ .probe = adv7280_probe,
+ .remove = adv7280_detach,
+ .id_table = adv7280_id,
+};
+module_i2c_driver(adv7280_i2c_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("Analog Device ADV7280 video decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index b4c8416c3d1f..284d4d1695c5 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -185,6 +185,9 @@ enum {
/* module saa7191: just ident 7191 */
V4L2_IDENT_SAA7191 = 7191,
+ /* module adv7280: just ident 7280 */
+ V4L2_IDENT_ADV7280 = 7280,
+
/* module ths7303: just ident 7303 */
V4L2_IDENT_THS7303 = 7303,