1381 lines
38 KiB
C
1381 lines
38 KiB
C
/*
|
|
* 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");
|