blob: abd615927a48f5b6ff2b2daed5622834d811b48d [file] [log] [blame]
/*
* Copyright 2005-2014 Freescale Semiconductor, 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
*/
/*!
* @file adv7180.c
*
* @brief Analog Device ADV7180 video decoder functions
*
* @ingroup Camera
*/
#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 ADV7180_VOLTAGE_ANALOG 1800000
#define ADV7180_VOLTAGE_DIGITAL_CORE 1800000
#define ADV7180_VOLTAGE_DIGITAL_IO 3300000
#define ADV7180_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;
static int adv7180_probe(struct i2c_client *adapter,
const struct i2c_device_id *id);
static int adv7180_detach(struct i2c_client *client);
static const struct i2c_device_id adv7180_id[] = {
{"adv7180", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, adv7180_id);
static struct i2c_driver adv7180_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "adv7180",
},
.probe = adv7180_probe,
.remove = adv7180_detach,
.id_table = adv7180_id,
};
/*!
* Maintains the information on the current state of the sensor.
*/
struct sensor {
struct sensor_data sen;
v4l2_std_id std_id;
} adv7180_data;
/*! List of input video formats supported. The video formats is corresponding
* with v4l2 id in video_fmt_t
*/
typedef enum {
ADV7180_NTSC = 0, /*!< Locked on (M) NTSC video signal. */
ADV7180_PAL, /*!< (B, G, H, I, N)PAL video signal. */
ADV7180_NOT_LOCKED, /*!< Not locked on a signal. */
} video_fmt_idx;
/*! Number of video standards supported (including 'not locked' signal). */
#define ADV7180_STD_MAX (ADV7180_PAL + 1)
/*! Video format structure. */
typedef struct {
int v4l2_id; /*!< Video for linux ID. */
char name[16]; /*!< Name (e.g., "NTSC", "PAL", etc.) */
u16 raw_width; /*!< Raw width. */
u16 raw_height; /*!< Raw height. */
u16 active_width; /*!< Active width. */
u16 active_height; /*!< Active height. */
int frame_rate; /*!< Frame rate. */
} video_fmt_t;
/*! Description of video formats supported.
*
* PAL: raw=720x625, active=720x576.
* NTSC: raw=720x525, active=720x480.
*/
static video_fmt_t video_fmts[] = {
{ /*! NTSC */
.v4l2_id = V4L2_STD_NTSC,
.name = "NTSC",
.raw_width = 720, /* SENS_FRM_WIDTH */
.raw_height = 525, /* SENS_FRM_HEIGHT */
.active_width = 720, /* ACT_FRM_WIDTH plus 1 */
.active_height = 480, /* ACT_FRM_WIDTH plus 1 */
.frame_rate = 30,
},
{ /*! (B, G, H, I, N) PAL */
.v4l2_id = V4L2_STD_PAL,
.name = "PAL",
.raw_width = 720,
.raw_height = 625,
.active_width = 720,
.active_height = 576,
.frame_rate = 25,
},
{ /*! Unlocked standard */
.v4l2_id = V4L2_STD_ALL,
.name = "Autodetect",
.raw_width = 720,
.raw_height = 625,
.active_width = 720,
.active_height = 576,
.frame_rate = 0,
},
};
/*!* Standard index of ADV7180. */
static video_fmt_idx video_idx = ADV7180_PAL;
/*! @brief This mutex is used to provide mutual exclusion.
*
* Create a mutex that can be used to provide mutually exclusive
* read/write access to the globally accessible data structures
* and variables that were defined above.
*/
static DEFINE_MUTEX(mutex);
#define IF_NAME "adv7180"
#define ADV7180_INPUT_CTL 0x00 /* Input Control */
#define ADV7180_STATUS_1 0x10 /* Status #1 */
#define ADV7180_BRIGHTNESS 0x0a /* Brightness */
#define ADV7180_IDENT 0x11 /* IDENT */
#define ADV7180_VSYNC_FIELD_CTL_1 0x31 /* VSYNC Field Control #1 */
#define ADV7180_MANUAL_WIN_CTL 0x3d /* Manual Window Control */
#define ADV7180_SD_SATURATION_CB 0xe3 /* SD Saturation Cb */
#define ADV7180_SD_SATURATION_CR 0xe4 /* SD Saturation Cr */
#define ADV7180_PWR_MNG 0x0f /* Power Management */
/* supported controls */
/* This hasn't been fully implemented yet.
* This is how it should work, though. */
static struct v4l2_queryctrl adv7180_qctrl[] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0, /* check this value */
.maximum = 255, /* check this value */
.step = 1, /* check this value */
.default_value = 127, /* check this value */
.flags = 0,
}, {
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Saturation",
.minimum = 0, /* check this value */
.maximum = 255, /* check this value */
.step = 0x1, /* check this value */
.default_value = 127, /* check this value */
.flags = 0,
}
};
static inline void adv7180_power_down(int enable)
{
gpio_set_value_cansleep(pwn_gpio, !enable);
msleep(2);
}
static int adv7180_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,
ADV7180_VOLTAGE_DIGITAL_IO,
ADV7180_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");
}
dvdd_regulator = devm_regulator_get(dev, "DVDD");
if (!IS_ERR(dvdd_regulator)) {
regulator_set_voltage(dvdd_regulator,
ADV7180_VOLTAGE_DIGITAL_CORE,
ADV7180_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");
}
avdd_regulator = devm_regulator_get(dev, "AVDD");
if (!IS_ERR(avdd_regulator)) {
regulator_set_voltage(avdd_regulator,
ADV7180_VOLTAGE_ANALOG,
ADV7180_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");
}
pvdd_regulator = devm_regulator_get(dev, "PVDD");
if (!IS_ERR(pvdd_regulator)) {
regulator_set_voltage(pvdd_regulator,
ADV7180_VOLTAGE_PLL,
ADV7180_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");
}
return ret;
}
/***********************************************************************
* I2C transfert.
***********************************************************************/
/*! 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 inline int adv7180_read(u8 reg)
{
int val;
val = i2c_smbus_read_byte_data(adv7180_data.sen.i2c_client, reg);
if (val < 0) {
dev_dbg(&adv7180_data.sen.i2c_client->dev,
"%s:read reg error: reg=%2x\n", __func__, reg);
return -1;
}
return val;
}
/*! 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(u8 reg, u8 val)
{
s32 ret;
ret = i2c_smbus_write_byte_data(adv7180_data.sen.i2c_client, reg, val);
if (ret < 0) {
dev_dbg(&adv7180_data.sen.i2c_client->dev,
"%s:write reg error:reg=%2x,val=%2x\n", __func__,
reg, val);
return -1;
}
return 0;
}
/***********************************************************************
* mxc_v4l2_capture interface.
***********************************************************************/
/*!
* Return attributes of current video standard.
* Since this device autodetects the current standard, this function also
* sets the values that need to be changed if the standard changes.
* There is no set std equivalent function.
*
* @return None.
*/
static void adv7180_get_std(v4l2_std_id *std)
{
int status_1, standard, idx;
bool locked;
dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_get_std\n");
status_1 = adv7180_read(ADV7180_STATUS_1);
locked = status_1 & 0x1;
standard = status_1 & 0x70;
mutex_lock(&mutex);
*std = V4L2_STD_ALL;
idx = ADV7180_NOT_LOCKED;
if (locked) {
if (standard == 0x40) {
*std = V4L2_STD_PAL;
idx = ADV7180_PAL;
} else if (standard == 0) {
*std = V4L2_STD_NTSC;
idx = ADV7180_NTSC;
}
}
mutex_unlock(&mutex);
/* This assumes autodetect which this device uses. */
if (*std != adv7180_data.std_id) {
video_idx = idx;
adv7180_data.std_id = *std;
adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width;
adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height;
}
}
/***********************************************************************
* IOCTL Functions from v4l2_int_ioctl_desc.
***********************************************************************/
/*!
* ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num
* s: pointer to standard V4L2 device structure
* p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
*
* Gets slave interface parameters.
* Calculates the required xclk value to support the requested
* clock parameters in p. This value is returned in the p
* parameter.
*
* vidioc_int_g_ifparm returns platform-specific information about the
* interface settings used by the sensor.
*
* Called on open.
*/
static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
{
dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_ifparm\n");
if (s == NULL) {
pr_err(" ERROR!! no slave device set!\n");
return -1;
}
/* Initialize structure to 0s then set any non-0 values. */
memset(p, 0, sizeof(*p));
p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */
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;
/* ADV7180 has a dedicated clock so no clock settings needed. */
return 0;
}
/*!
* Sets the camera power.
*
* s pointer to the camera device
* on if 1, power is to be turned on. 0 means power is to be turned off
*
* ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
* @s: pointer to standard V4L2 device structure
* @on: power state to which device is to be set
*
* Sets devices power state to requrested state, if possible.
* This is called on open, close, suspend and resume.
*/
static int ioctl_s_power(struct v4l2_int_device *s, int on)
{
struct sensor *sensor = s->priv;
dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_s_power\n");
if (on && !sensor->sen.on) {
if (adv7180_write_reg(ADV7180_PWR_MNG, 0x04) != 0)
return -EIO;
/*
* FIXME:Additional 400ms to wait the chip to be stable?
* This is a workaround for preview scrolling issue.
*/
msleep(400);
} else if (!on && sensor->sen.on) {
if (adv7180_write_reg(ADV7180_PWR_MNG, 0x24) != 0)
return -EIO;
}
sensor->sen.on = on;
return 0;
}
/*!
* ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
* @s: pointer to standard V4L2 device structure
* @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
*
* Returns the sensor's video CAPTURE parameters.
*/
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;
dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_parm\n");
switch (a->type) {
/* These are all the possible cases. */
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
pr_debug(" 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:
pr_debug("ioctl_g_parm:type is unknown %d\n", a->type);
break;
}
return 0;
}
/*!
* ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
* @s: pointer to standard V4L2 device structure
* @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
*
* Configures the sensor to use the input parameters, if possible. If
* not possible, reverts to the old parameters and returns the
* appropriate error code.
*
* This driver cannot change these settings.
*/
static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
{
dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_parm\n");
switch (a->type) {
/* These are all the possible cases. */
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:
pr_debug(" type is unknown - %d\n", a->type);
break;
}
return 0;
}
/*!
* ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
* @s: pointer to standard V4L2 device structure
* @f: pointer to standard V4L2 v4l2_format structure
*
* Returns the sensor's current pixel format in the v4l2_format
* parameter.
*/
static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
{
struct sensor *sensor = s->priv;
dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_g_fmt_cap\n");
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
pr_debug(" 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: {
v4l2_std_id std;
adv7180_get_std(&std);
f->fmt.pix.pixelformat = (u32)std;
}
break;
default:
f->fmt.pix = sensor->sen.pix;
break;
}
return 0;
}
/*!
* ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
* @s: pointer to standard V4L2 device structure
* @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
*
* If the requested control is supported, returns the control information
* from the video_control[] array. Otherwise, returns -EINVAL if the
* control is not supported.
*/
static int ioctl_queryctrl(struct v4l2_int_device *s,
struct v4l2_queryctrl *qc)
{
int i;
dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_queryctrl\n");
for (i = 0; i < ARRAY_SIZE(adv7180_qctrl); i++)
if (qc->id && qc->id == adv7180_qctrl[i].id) {
memcpy(qc, &(adv7180_qctrl[i]),
sizeof(*qc));
return 0;
}
return -EINVAL;
}
/*!
* ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
* @s: pointer to standard V4L2 device structure
* @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
*
* If the requested control is supported, returns the control's current
* value from the video_control[] array. Otherwise, returns -EINVAL
* if the control is not supported.
*/
static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
{
int ret = 0;
int sat = 0;
dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_g_ctrl\n");
switch (vc->id) {
case V4L2_CID_BRIGHTNESS:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_BRIGHTNESS\n");
adv7180_data.sen.brightness = adv7180_read(ADV7180_BRIGHTNESS);
vc->value = adv7180_data.sen.brightness;
break;
case V4L2_CID_CONTRAST:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_CONTRAST\n");
vc->value = adv7180_data.sen.contrast;
break;
case V4L2_CID_SATURATION:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_SATURATION\n");
sat = adv7180_read(ADV7180_SD_SATURATION_CB);
adv7180_data.sen.saturation = sat;
vc->value = adv7180_data.sen.saturation;
break;
case V4L2_CID_HUE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_HUE\n");
vc->value = adv7180_data.sen.hue;
break;
case V4L2_CID_AUTO_WHITE_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_AUTO_WHITE_BALANCE\n");
break;
case V4L2_CID_DO_WHITE_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_DO_WHITE_BALANCE\n");
break;
case V4L2_CID_RED_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_RED_BALANCE\n");
vc->value = adv7180_data.sen.red;
break;
case V4L2_CID_BLUE_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_BLUE_BALANCE\n");
vc->value = adv7180_data.sen.blue;
break;
case V4L2_CID_GAMMA:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_GAMMA\n");
break;
case V4L2_CID_EXPOSURE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_EXPOSURE\n");
vc->value = adv7180_data.sen.ae_mode;
break;
case V4L2_CID_AUTOGAIN:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_AUTOGAIN\n");
break;
case V4L2_CID_GAIN:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_GAIN\n");
break;
case V4L2_CID_HFLIP:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_HFLIP\n");
break;
case V4L2_CID_VFLIP:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_VFLIP\n");
break;
default:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" Default case\n");
vc->value = 0;
ret = -EPERM;
break;
}
return ret;
}
/*!
* ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
* @s: pointer to standard V4L2 device structure
* @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
*
* If the requested control is supported, sets the control's current
* value in HW (and updates the video_control[] array). Otherwise,
* returns -EINVAL if the control is not supported.
*/
static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc)
{
int retval = 0;
u8 tmp;
dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_s_ctrl\n");
switch (vc->id) {
case V4L2_CID_BRIGHTNESS:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_BRIGHTNESS\n");
tmp = vc->value;
adv7180_write_reg(ADV7180_BRIGHTNESS, tmp);
adv7180_data.sen.brightness = vc->value;
break;
case V4L2_CID_CONTRAST:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_CONTRAST\n");
break;
case V4L2_CID_SATURATION:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_SATURATION\n");
tmp = vc->value;
adv7180_write_reg(ADV7180_SD_SATURATION_CB, tmp);
adv7180_write_reg(ADV7180_SD_SATURATION_CR, tmp);
adv7180_data.sen.saturation = vc->value;
break;
case V4L2_CID_HUE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_HUE\n");
break;
case V4L2_CID_AUTO_WHITE_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_AUTO_WHITE_BALANCE\n");
break;
case V4L2_CID_DO_WHITE_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_DO_WHITE_BALANCE\n");
break;
case V4L2_CID_RED_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_RED_BALANCE\n");
break;
case V4L2_CID_BLUE_BALANCE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_BLUE_BALANCE\n");
break;
case V4L2_CID_GAMMA:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_GAMMA\n");
break;
case V4L2_CID_EXPOSURE:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_EXPOSURE\n");
break;
case V4L2_CID_AUTOGAIN:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_AUTOGAIN\n");
break;
case V4L2_CID_GAIN:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_GAIN\n");
break;
case V4L2_CID_HFLIP:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_HFLIP\n");
break;
case V4L2_CID_VFLIP:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" V4L2_CID_VFLIP\n");
break;
default:
dev_dbg(&adv7180_data.sen.i2c_client->dev,
" Default case\n");
retval = -EPERM;
break;
}
return retval;
}
/*!
* ioctl_enum_framesizes - V4L2 sensor interface handler for
* VIDIOC_ENUM_FRAMESIZES ioctl
* @s: pointer to standard V4L2 device structure
* @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure
*
* Return 0 if successful, otherwise -EINVAL.
*/
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;
}
/*!
* ioctl_enum_frameintervals - V4L2 sensor interface handler for
* VIDIOC_ENUM_FRAMEINTERVALS ioctl
* @s: pointer to standard V4L2 device structure
* @fival: standard V4L2 VIDIOC_ENUM_FRAMEINTERVALS ioctl structure
*
* Return 0 if successful, otherwise -EINVAL.
*/
static int ioctl_enum_frameintervals(struct v4l2_int_device *s,
struct v4l2_frmivalenum *fival)
{
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;
}
/*!
* ioctl_g_chip_ident - V4L2 sensor interface handler for
* VIDIOC_DBG_G_CHIP_IDENT ioctl
* @s: pointer to standard V4L2 device structure
* @id: pointer to int
*
* Return 0.
*/
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,
"adv7180_decoder");
((struct v4l2_dbg_chip_ident *)id)->ident = V4L2_IDENT_ADV7180;
return 0;
}
/*!
* ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
* @s: pointer to standard V4L2 device structure
*/
static int ioctl_init(struct v4l2_int_device *s)
{
dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180:ioctl_init\n");
return 0;
}
/*!
* ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
* @s: pointer to standard V4L2 device structure
*
* Initialise the device when slave attaches to the master.
*/
static int ioctl_dev_init(struct v4l2_int_device *s)
{
dev_dbg(&adv7180_data.sen.i2c_client->dev, "adv7180:ioctl_dev_init\n");
return 0;
}
/*!
* This structure defines all the ioctls for this module.
*/
static struct v4l2_int_ioctl_desc adv7180_ioctl_desc[] = {
{vidioc_int_dev_init_num, (v4l2_int_ioctl_func*)ioctl_dev_init},
/*!
* Delinitialise the dev. at slave detach.
* The complement of ioctl_dev_init.
*/
/* {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func *)ioctl_dev_exit}, */
{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_g_needs_reset_num,
(v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */
/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */
{vidioc_int_init_num, (v4l2_int_ioctl_func*)ioctl_init},
/*!
* VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
*/
/* {vidioc_int_enum_fmt_cap_num,
(v4l2_int_ioctl_func *)ioctl_enum_fmt_cap}, */
/*!
* VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.
* This ioctl is used to negotiate the image capture size and
* pixel format without actually making it take effect.
*/
/* {vidioc_int_try_fmt_cap_num,
(v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */
{vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap},
/*!
* If the requested format is supported, configures the HW to use that
* format, returns error code if format not supported or HW can't be
* correctly configured.
*/
/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_s_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 adv7180_slave = {
.ioctls = adv7180_ioctl_desc,
.num_ioctls = ARRAY_SIZE(adv7180_ioctl_desc),
};
static struct v4l2_int_device adv7180_int_device = {
.module = THIS_MODULE,
.name = "adv7180",
.type = v4l2_int_type_slave,
.u = {
.slave = &adv7180_slave,
},
};
/***********************************************************************
* I2C client and driver.
***********************************************************************/
/*! ADV7180 Reset function.
*
* @return None.
*/
static void adv7180_hard_reset(bool cvbs)
{
dev_dbg(&adv7180_data.sen.i2c_client->dev,
"In adv7180:adv7180_hard_reset\n");
if (cvbs) {
/* Set CVBS input on AIN1 */
adv7180_write_reg(ADV7180_INPUT_CTL, 0x00);
} else {
/*
* Set YPbPr input on AIN1,4,5 and normal
* operations(autodection of all stds).
*/
adv7180_write_reg(ADV7180_INPUT_CTL, 0x09);
}
/* Datasheet recommends */
adv7180_write_reg(0x01, 0xc8);
adv7180_write_reg(0x02, 0x04);
adv7180_write_reg(0x03, 0x00);
adv7180_write_reg(0x04, 0x45);
adv7180_write_reg(0x05, 0x00);
adv7180_write_reg(0x06, 0x02);
adv7180_write_reg(0x07, 0x7F);
adv7180_write_reg(0x08, 0x80);
adv7180_write_reg(0x0A, 0x00);
adv7180_write_reg(0x0B, 0x00);
adv7180_write_reg(0x0C, 0x36);
adv7180_write_reg(0x0D, 0x7C);
adv7180_write_reg(0x0E, 0x00);
adv7180_write_reg(0x0F, 0x00);
adv7180_write_reg(0x13, 0x00);
adv7180_write_reg(0x14, 0x12);
adv7180_write_reg(0x15, 0x00);
adv7180_write_reg(0x16, 0x00);
adv7180_write_reg(0x17, 0x01);
adv7180_write_reg(0x18, 0x93);
adv7180_write_reg(0xF1, 0x19);
adv7180_write_reg(0x1A, 0x00);
adv7180_write_reg(0x1B, 0x00);
adv7180_write_reg(0x1C, 0x00);
adv7180_write_reg(0x1D, 0x40);
adv7180_write_reg(0x1E, 0x00);
adv7180_write_reg(0x1F, 0x00);
adv7180_write_reg(0x20, 0x00);
adv7180_write_reg(0x21, 0x00);
adv7180_write_reg(0x22, 0x00);
adv7180_write_reg(0x23, 0xC0);
adv7180_write_reg(0x24, 0x00);
adv7180_write_reg(0x25, 0x00);
adv7180_write_reg(0x26, 0x00);
adv7180_write_reg(0x27, 0x58);
adv7180_write_reg(0x28, 0x00);
adv7180_write_reg(0x29, 0x00);
adv7180_write_reg(0x2A, 0x00);
adv7180_write_reg(0x2B, 0xE1);
adv7180_write_reg(0x2C, 0xAE);
adv7180_write_reg(0x2D, 0xF4);
adv7180_write_reg(0x2E, 0x00);
adv7180_write_reg(0x2F, 0xF0);
adv7180_write_reg(0x30, 0x00);
adv7180_write_reg(0x31, 0x12);
adv7180_write_reg(0x32, 0x41);
adv7180_write_reg(0x33, 0x84);
adv7180_write_reg(0x34, 0x00);
adv7180_write_reg(0x35, 0x02);
adv7180_write_reg(0x36, 0x00);
adv7180_write_reg(0x37, 0x01);
adv7180_write_reg(0x38, 0x80);
adv7180_write_reg(0x39, 0xC0);
adv7180_write_reg(0x3A, 0x10);
adv7180_write_reg(0x3B, 0x05);
adv7180_write_reg(0x3C, 0x58);
adv7180_write_reg(0x3D, 0xB2);
adv7180_write_reg(0x3E, 0x64);
adv7180_write_reg(0x3F, 0xE4);
adv7180_write_reg(0x40, 0x90);
adv7180_write_reg(0x41, 0x01);
adv7180_write_reg(0x42, 0x7E);
adv7180_write_reg(0x43, 0xA4);
adv7180_write_reg(0x44, 0xFF);
adv7180_write_reg(0x45, 0xB6);
adv7180_write_reg(0x46, 0x12);
adv7180_write_reg(0x48, 0x00);
adv7180_write_reg(0x49, 0x00);
adv7180_write_reg(0x4A, 0x00);
adv7180_write_reg(0x4B, 0x00);
adv7180_write_reg(0x4C, 0x00);
adv7180_write_reg(0x4D, 0xEF);
adv7180_write_reg(0x4E, 0x08);
adv7180_write_reg(0x4F, 0x08);
adv7180_write_reg(0x50, 0x08);
adv7180_write_reg(0x51, 0x24);
adv7180_write_reg(0x52, 0x0B);
adv7180_write_reg(0x53, 0x4E);
adv7180_write_reg(0x54, 0x80);
adv7180_write_reg(0x55, 0x00);
adv7180_write_reg(0x56, 0x10);
adv7180_write_reg(0x57, 0x00);
adv7180_write_reg(0x58, 0x00);
adv7180_write_reg(0x59, 0x00);
adv7180_write_reg(0x5A, 0x00);
adv7180_write_reg(0x5B, 0x00);
adv7180_write_reg(0x5C, 0x00);
adv7180_write_reg(0x5D, 0x00);
adv7180_write_reg(0x5E, 0x00);
adv7180_write_reg(0x5F, 0x00);
adv7180_write_reg(0x60, 0x00);
adv7180_write_reg(0x61, 0x00);
adv7180_write_reg(0x62, 0x20);
adv7180_write_reg(0x63, 0x00);
adv7180_write_reg(0x64, 0x00);
adv7180_write_reg(0x65, 0x00);
adv7180_write_reg(0x66, 0x00);
adv7180_write_reg(0x67, 0x03);
adv7180_write_reg(0x68, 0x01);
adv7180_write_reg(0x69, 0x00);
adv7180_write_reg(0x6A, 0x00);
adv7180_write_reg(0x6B, 0xC0);
adv7180_write_reg(0x6C, 0x00);
adv7180_write_reg(0x6D, 0x00);
adv7180_write_reg(0x6E, 0x00);
adv7180_write_reg(0x6F, 0x00);
adv7180_write_reg(0x70, 0x00);
adv7180_write_reg(0x71, 0x00);
adv7180_write_reg(0x72, 0x00);
adv7180_write_reg(0x73, 0x10);
adv7180_write_reg(0x74, 0x04);
adv7180_write_reg(0x75, 0x01);
adv7180_write_reg(0x76, 0x00);
adv7180_write_reg(0x77, 0x3F);
adv7180_write_reg(0x78, 0xFF);
adv7180_write_reg(0x79, 0xFF);
adv7180_write_reg(0x7A, 0xFF);
adv7180_write_reg(0x7B, 0x1E);
adv7180_write_reg(0x7C, 0xC0);
adv7180_write_reg(0x7D, 0x00);
adv7180_write_reg(0x7E, 0x00);
adv7180_write_reg(0x7F, 0x00);
adv7180_write_reg(0x80, 0x00);
adv7180_write_reg(0x81, 0xC0);
adv7180_write_reg(0x82, 0x04);
adv7180_write_reg(0x83, 0x00);
adv7180_write_reg(0x84, 0x0C);
adv7180_write_reg(0x85, 0x02);
adv7180_write_reg(0x86, 0x03);
adv7180_write_reg(0x87, 0x63);
adv7180_write_reg(0x88, 0x5A);
adv7180_write_reg(0x89, 0x08);
adv7180_write_reg(0x8A, 0x10);
adv7180_write_reg(0x8B, 0x00);
adv7180_write_reg(0x8C, 0x40);
adv7180_write_reg(0x8D, 0x00);
adv7180_write_reg(0x8E, 0x40);
adv7180_write_reg(0x8F, 0x00);
adv7180_write_reg(0x90, 0x00);
adv7180_write_reg(0x91, 0x50);
adv7180_write_reg(0x92, 0x00);
adv7180_write_reg(0x93, 0x00);
adv7180_write_reg(0x94, 0x00);
adv7180_write_reg(0x95, 0x00);
adv7180_write_reg(0x96, 0x00);
adv7180_write_reg(0x97, 0xF0);
adv7180_write_reg(0x98, 0x00);
adv7180_write_reg(0x99, 0x00);
adv7180_write_reg(0x9A, 0x00);
adv7180_write_reg(0x9B, 0x00);
adv7180_write_reg(0x9C, 0x00);
adv7180_write_reg(0x9D, 0x00);
adv7180_write_reg(0x9E, 0x00);
adv7180_write_reg(0x9F, 0x00);
adv7180_write_reg(0xA0, 0x00);
adv7180_write_reg(0xA1, 0x00);
adv7180_write_reg(0xA2, 0x00);
adv7180_write_reg(0xA3, 0x00);
adv7180_write_reg(0xA4, 0x00);
adv7180_write_reg(0xA5, 0x00);
adv7180_write_reg(0xA6, 0x00);
adv7180_write_reg(0xA7, 0x00);
adv7180_write_reg(0xA8, 0x00);
adv7180_write_reg(0xA9, 0x00);
adv7180_write_reg(0xAA, 0x00);
adv7180_write_reg(0xAB, 0x00);
adv7180_write_reg(0xAC, 0x00);
adv7180_write_reg(0xAD, 0x00);
adv7180_write_reg(0xAE, 0x60);
adv7180_write_reg(0xAF, 0x00);
adv7180_write_reg(0xB0, 0x00);
adv7180_write_reg(0xB1, 0x60);
adv7180_write_reg(0xB2, 0x1C);
adv7180_write_reg(0xB3, 0x54);
adv7180_write_reg(0xB4, 0x00);
adv7180_write_reg(0xB5, 0x00);
adv7180_write_reg(0xB6, 0x00);
adv7180_write_reg(0xB7, 0x13);
adv7180_write_reg(0xB8, 0x03);
adv7180_write_reg(0xB9, 0x33);
adv7180_write_reg(0xBF, 0x02);
adv7180_write_reg(0xC0, 0x00);
adv7180_write_reg(0xC1, 0x00);
adv7180_write_reg(0xC2, 0x00);
adv7180_write_reg(0xC3, 0x00);
adv7180_write_reg(0xC4, 0x00);
adv7180_write_reg(0xC5, 0x81);
adv7180_write_reg(0xC6, 0x00);
adv7180_write_reg(0xC7, 0x00);
adv7180_write_reg(0xC8, 0x00);
adv7180_write_reg(0xC9, 0x04);
adv7180_write_reg(0xCC, 0x69);
adv7180_write_reg(0xCD, 0x00);
adv7180_write_reg(0xCE, 0x01);
adv7180_write_reg(0xCF, 0xB4);
adv7180_write_reg(0xD0, 0x00);
adv7180_write_reg(0xD1, 0x10);
adv7180_write_reg(0xD2, 0xFF);
adv7180_write_reg(0xD3, 0xFF);
adv7180_write_reg(0xD4, 0x7F);
adv7180_write_reg(0xD5, 0x7F);
adv7180_write_reg(0xD6, 0x3E);
adv7180_write_reg(0xD7, 0x08);
adv7180_write_reg(0xD8, 0x3C);
adv7180_write_reg(0xD9, 0x08);
adv7180_write_reg(0xDA, 0x3C);
adv7180_write_reg(0xDB, 0x9B);
adv7180_write_reg(0xDC, 0xAC);
adv7180_write_reg(0xDD, 0x4C);
adv7180_write_reg(0xDE, 0x00);
adv7180_write_reg(0xDF, 0x00);
adv7180_write_reg(0xE0, 0x14);
adv7180_write_reg(0xE1, 0x80);
adv7180_write_reg(0xE2, 0x80);
adv7180_write_reg(0xE3, 0x80);
adv7180_write_reg(0xE4, 0x80);
adv7180_write_reg(0xE5, 0x25);
adv7180_write_reg(0xE6, 0x44);
adv7180_write_reg(0xE7, 0x63);
adv7180_write_reg(0xE8, 0x65);
adv7180_write_reg(0xE9, 0x14);
adv7180_write_reg(0xEA, 0x63);
adv7180_write_reg(0xEB, 0x55);
adv7180_write_reg(0xEC, 0x55);
adv7180_write_reg(0xEE, 0x00);
adv7180_write_reg(0xEF, 0x4A);
adv7180_write_reg(0xF0, 0x44);
adv7180_write_reg(0xF1, 0x0C);
adv7180_write_reg(0xF2, 0x32);
adv7180_write_reg(0xF3, 0x00);
adv7180_write_reg(0xF4, 0x3F);
adv7180_write_reg(0xF5, 0xE0);
adv7180_write_reg(0xF6, 0x69);
adv7180_write_reg(0xF7, 0x10);
adv7180_write_reg(0xF8, 0x00);
adv7180_write_reg(0xF9, 0x03);
adv7180_write_reg(0xFA, 0xFA);
adv7180_write_reg(0xFB, 0x40);
}
/*! ADV7180 I2C attach function.
*
* @param *adapter struct i2c_adapter *.
*
* @return Error code indicating success or failure.
*/
/*!
* 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)
{
int rev_id;
int ret = 0;
u32 cvbs = true;
struct pinctrl *pinctrl;
struct device *dev = &client->dev;
printk(KERN_ERR"DBG sensor data is at %p\n", &adv7180_data);
/* ov5640 pinctrl */
pinctrl = devm_pinctrl_get_select_default(dev);
if (IS_ERR(pinctrl)) {
dev_err(dev, "setup pinctrl failed\n");
return PTR_ERR(pinctrl);
}
/* request power down pin */
pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
if (!gpio_is_valid(pwn_gpio)) {
dev_err(dev, "no sensor pwdn pin available\n");
return -ENODEV;
}
ret = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
"adv7180_pwdn");
if (ret < 0) {
dev_err(dev, "no power pin available!\n");
return ret;
}
adv7180_regulator_enable(dev);
adv7180_power_down(0);
msleep(1);
/* Set initial values for the sensor struct. */
memset(&adv7180_data, 0, sizeof(adv7180_data));
adv7180_data.sen.i2c_client = client;
adv7180_data.sen.streamcap.timeperframe.denominator = 30;
adv7180_data.sen.streamcap.timeperframe.numerator = 1;
adv7180_data.std_id = V4L2_STD_ALL;
video_idx = ADV7180_NOT_LOCKED;
adv7180_data.sen.pix.width = video_fmts[video_idx].raw_width;
adv7180_data.sen.pix.height = video_fmts[video_idx].raw_height;
adv7180_data.sen.pix.pixelformat = V4L2_PIX_FMT_UYVY; /* YUV422 */
adv7180_data.sen.pix.priv = 1; /* 1 is used to indicate TV in */
adv7180_data.sen.on = true;
adv7180_data.sen.sensor_clk = devm_clk_get(dev, "csi_mclk");
if (IS_ERR(adv7180_data.sen.sensor_clk)) {
dev_err(dev, "get mclk failed\n");
return PTR_ERR(adv7180_data.sen.sensor_clk);
}
ret = of_property_read_u32(dev->of_node, "mclk",
&adv7180_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 *) &(adv7180_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",
&(adv7180_data.sen.csi));
if (ret) {
dev_err(dev, "csi_id invalid\n");
return ret;
}
clk_prepare_enable(adv7180_data.sen.sensor_clk);
dev_dbg(&adv7180_data.sen.i2c_client->dev,
"%s:adv7180 probe i2c address is 0x%02X\n",
__func__, adv7180_data.sen.i2c_client->addr);
/*! Read the revision ID of the tvin chip */
rev_id = adv7180_read(ADV7180_IDENT);
dev_dbg(dev,
"%s:Analog Device adv7%2X0 detected!\n", __func__,
rev_id);
ret = of_property_read_u32(dev->of_node, "cvbs", &(cvbs));
if (ret) {
dev_err(dev, "cvbs setting is not found\n");
cvbs = true;
}
/*! ADV7180 initialization. */
adv7180_hard_reset(cvbs);
pr_debug(" type is %d (expect %d)\n",
adv7180_int_device.type, v4l2_int_type_slave);
pr_debug(" num ioctls is %d\n",
adv7180_int_device.u.slave->num_ioctls);
/* This function attaches this structure to the /dev/video0 device.
* The pointer in priv points to the adv7180_data structure here.*/
adv7180_int_device.priv = &adv7180_data;
ret = v4l2_int_device_register(&adv7180_int_device);
clk_disable_unprepare(adv7180_data.sen.sensor_clk);
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)
{
dev_dbg(&adv7180_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);
/* Power down via i2c */
adv7180_write_reg(ADV7180_PWR_MNG, 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(&adv7180_int_device);
return 0;
}
/*!
* ADV7180 init function.
* Called on insmod.
*
* @return Error code indicating success or failure.
*/
static __init int adv7180_init(void)
{
u8 err = 0;
pr_debug("In adv7180_init\n");
/* Tells the i2c driver what functions to call for this driver. */
err = i2c_add_driver(&adv7180_i2c_driver);
if (err != 0)
pr_err("%s:driver registration failed, error=%d\n",
__func__, err);
return err;
}
/*!
* ADV7180 cleanup function.
* Called on rmmod.
*
* @return Error code indicating success or failure.
*/
static void __exit adv7180_clean(void)
{
dev_dbg(&adv7180_data.sen.i2c_client->dev, "In adv7180_clean\n");
i2c_del_driver(&adv7180_i2c_driver);
}
module_init(adv7180_init);
module_exit(adv7180_clean);
MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("Anolog Device ADV7180 video decoder driver");
MODULE_LICENSE("GPL");