aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/adv7180.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/adv7180.c')
-rw-r--r--drivers/media/i2c/adv7180.c1010
1 files changed, 812 insertions, 198 deletions
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index bffe6eb528a3..b75878c27c2a 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -30,56 +30,60 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <linux/mutex.h>
-
-#define ADV7180_INPUT_CONTROL_REG 0x00
-#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00
-#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
-#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20
-#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30
-#define ADV7180_INPUT_CONTROL_NTSC_J 0x40
-#define ADV7180_INPUT_CONTROL_NTSC_M 0x50
-#define ADV7180_INPUT_CONTROL_PAL60 0x60
-#define ADV7180_INPUT_CONTROL_NTSC_443 0x70
-#define ADV7180_INPUT_CONTROL_PAL_BG 0x80
-#define ADV7180_INPUT_CONTROL_PAL_N 0x90
-#define ADV7180_INPUT_CONTROL_PAL_M 0xa0
-#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0
-#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0
-#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0
-#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0
-#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0
+#include <linux/delay.h>
+
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM 0x0
+#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED 0x1
+#define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM 0x2
+#define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM 0x3
+#define ADV7180_STD_NTSC_J 0x4
+#define ADV7180_STD_NTSC_M 0x5
+#define ADV7180_STD_PAL60 0x6
+#define ADV7180_STD_NTSC_443 0x7
+#define ADV7180_STD_PAL_BG 0x8
+#define ADV7180_STD_PAL_N 0x9
+#define ADV7180_STD_PAL_M 0xa
+#define ADV7180_STD_PAL_M_PED 0xb
+#define ADV7180_STD_PAL_COMB_N 0xc
+#define ADV7180_STD_PAL_COMB_N_PED 0xd
+#define ADV7180_STD_PAL_SECAM 0xe
+#define ADV7180_STD_PAL_SECAM_PED 0xf
+
+#define ADV7180_REG_INPUT_CONTROL 0x0000
#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f
-#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04
+#define ADV7182_REG_INPUT_VIDSEL 0x0002
+
+#define ADV7180_REG_EXTENDED_OUTPUT_CONTROL 0x0004
#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
-#define ADV7180_AUTODETECT_ENABLE_REG 0x07
+#define ADV7180_REG_AUTODETECT_ENABLE 0x07
#define ADV7180_AUTODETECT_DEFAULT 0x7f
/* Contrast */
-#define ADV7180_CON_REG 0x08 /*Unsigned */
+#define ADV7180_REG_CON 0x0008 /*Unsigned */
#define ADV7180_CON_MIN 0
#define ADV7180_CON_DEF 128
#define ADV7180_CON_MAX 255
/* Brightness*/
-#define ADV7180_BRI_REG 0x0a /*Signed */
+#define ADV7180_REG_BRI 0x000a /*Signed */
#define ADV7180_BRI_MIN -128
#define ADV7180_BRI_DEF 0
#define ADV7180_BRI_MAX 127
/* Hue */
-#define ADV7180_HUE_REG 0x0b /*Signed, inverted */
+#define ADV7180_REG_HUE 0x000b /*Signed, inverted */
#define ADV7180_HUE_MIN -127
#define ADV7180_HUE_DEF 0
#define ADV7180_HUE_MAX 128
-#define ADV7180_ADI_CTRL_REG 0x0e
-#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
+#define ADV7180_REG_CTRL 0x000e
+#define ADV7180_CTRL_IRQ_SPACE 0x20
-#define ADV7180_PWR_MAN_REG 0x0f
+#define ADV7180_REG_PWR_MAN 0x0f
#define ADV7180_PWR_MAN_ON 0x04
#define ADV7180_PWR_MAN_OFF 0x24
#define ADV7180_PWR_MAN_RES 0x80
-#define ADV7180_STATUS1_REG 0x10
+#define ADV7180_REG_STATUS1 0x0010
#define ADV7180_STATUS1_IN_LOCK 0x01
#define ADV7180_STATUS1_AUTOD_MASK 0x70
#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
@@ -91,49 +95,161 @@
#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60
#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70
-#define ADV7180_IDENT_REG 0x11
+#define ADV7180_REG_IDENT 0x0011
#define ADV7180_ID_7180 0x18
-#define ADV7180_ICONF1_ADI 0x40
+#define ADV7180_REG_ICONF1 0x0040
#define ADV7180_ICONF1_ACTIVE_LOW 0x01
#define ADV7180_ICONF1_PSYNC_ONLY 0x10
#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
/* Saturation */
-#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */
-#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */
+#define ADV7180_REG_SD_SAT_CB 0x00e3 /*Unsigned */
+#define ADV7180_REG_SD_SAT_CR 0x00e4 /*Unsigned */
#define ADV7180_SAT_MIN 0
#define ADV7180_SAT_DEF 128
#define ADV7180_SAT_MAX 255
#define ADV7180_IRQ1_LOCK 0x01
#define ADV7180_IRQ1_UNLOCK 0x02
-#define ADV7180_ISR1_ADI 0x42
-#define ADV7180_ICR1_ADI 0x43
-#define ADV7180_IMR1_ADI 0x44
-#define ADV7180_IMR2_ADI 0x48
+#define ADV7180_REG_ISR1 0x0042
+#define ADV7180_REG_ICR1 0x0043
+#define ADV7180_REG_IMR1 0x0044
+#define ADV7180_REG_IMR2 0x0048
#define ADV7180_IRQ3_AD_CHANGE 0x08
-#define ADV7180_ISR3_ADI 0x4A
-#define ADV7180_ICR3_ADI 0x4B
-#define ADV7180_IMR3_ADI 0x4C
-#define ADV7180_IMR4_ADI 0x50
+#define ADV7180_REG_ISR3 0x004A
+#define ADV7180_REG_ICR3 0x004B
+#define ADV7180_REG_IMR3 0x004C
+#define ADV7180_REG_IMR4 0x50
-#define ADV7180_NTSC_V_BIT_END_REG 0xE6
+#define ADV7180_REG_NTSC_V_BIT_END 0x00E6
#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
+#define ADV7180_REG_VPP_SLAVE_ADDR 0xFD
+#define ADV7180_REG_CSI_SLAVE_ADDR 0xFE
+
+#define ADV7180_REG_FLCONTROL 0x40e0
+#define ADV7180_FLCONTROL_FL_ENABLE 0x1
+
+#define ADV7180_CSI_REG_PWRDN 0x00
+#define ADV7180_CSI_PWRDN 0x80
+
+#define ADV7180_INPUT_CVBS_AIN1 0x00
+#define ADV7180_INPUT_CVBS_AIN2 0x01
+#define ADV7180_INPUT_CVBS_AIN3 0x02
+#define ADV7180_INPUT_CVBS_AIN4 0x03
+#define ADV7180_INPUT_CVBS_AIN5 0x04
+#define ADV7180_INPUT_CVBS_AIN6 0x05
+#define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
+#define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
+#define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
+#define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
+#define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
+
+#define ADV7182_INPUT_CVBS_AIN1 0x00
+#define ADV7182_INPUT_CVBS_AIN2 0x01
+#define ADV7182_INPUT_CVBS_AIN3 0x02
+#define ADV7182_INPUT_CVBS_AIN4 0x03
+#define ADV7182_INPUT_CVBS_AIN5 0x04
+#define ADV7182_INPUT_CVBS_AIN6 0x05
+#define ADV7182_INPUT_CVBS_AIN7 0x06
+#define ADV7182_INPUT_CVBS_AIN8 0x07
+#define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
+#define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
+#define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
+#define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
+#define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
+#define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
+#define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
+#define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
+#define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
+#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
+
+#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
+#define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
+
+#define V4L2_CID_ADV_FAST_SWITCH (V4L2_CID_USER_ADV7180_BASE + 0x00)
+
+struct adv7180_state;
+
+#define ADV7180_FLAG_RESET_POWERED BIT(0)
+#define ADV7180_FLAG_V2 BIT(1)
+#define ADV7180_FLAG_MIPI_CSI2 BIT(2)
+#define ADV7180_FLAG_I2P BIT(3)
+
+struct adv7180_chip_info {
+ unsigned int flags;
+ unsigned int valid_input_mask;
+ int (*set_std)(struct adv7180_state *st, unsigned int std);
+ int (*select_input)(struct adv7180_state *st, unsigned int input);
+ int (*init)(struct adv7180_state *state);
+};
+
struct adv7180_state {
struct v4l2_ctrl_handler ctrl_hdl;
struct v4l2_subdev sd;
+ struct media_pad pad;
struct mutex mutex; /* mutual excl. when accessing chip */
int irq;
v4l2_std_id curr_norm;
bool autodetect;
bool powered;
u8 input;
+
+ struct i2c_client *client;
+ unsigned int register_page;
+ struct i2c_client *csi_client;
+ struct i2c_client *vpp_client;
+ const struct adv7180_chip_info *chip_info;
+ enum v4l2_field field;
};
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
struct adv7180_state, \
ctrl_hdl)->sd)
+static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
+{
+ if (state->register_page != page) {
+ i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
+ page);
+ state->register_page = page;
+ }
+
+ return 0;
+}
+
+static int adv7180_write(struct adv7180_state *state, unsigned int reg,
+ unsigned int value)
+{
+ lockdep_assert_held(&state->mutex);
+ adv7180_select_page(state, reg >> 8);
+ return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
+}
+
+static int adv7180_read(struct adv7180_state *state, unsigned int reg)
+{
+ lockdep_assert_held(&state->mutex);
+ adv7180_select_page(state, reg >> 8);
+ return i2c_smbus_read_byte_data(state->client, reg & 0xff);
+}
+
+static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
+ unsigned int value)
+{
+ return i2c_smbus_write_byte_data(state->csi_client, reg, value);
+}
+
+static int adv7180_set_video_standard(struct adv7180_state *state,
+ unsigned int std)
+{
+ return state->chip_info->set_std(state, std);
+}
+
+static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
+ unsigned int value)
+{
+ return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
+}
+
static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
{
/* in case V4L2_IN_ST_NO_SIGNAL */
@@ -165,22 +281,22 @@ static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
static int v4l2_std_to_adv7180(v4l2_std_id std)
{
if (std == V4L2_STD_PAL_60)
- return ADV7180_INPUT_CONTROL_PAL60;
+ return ADV7180_STD_PAL60;
if (std == V4L2_STD_NTSC_443)
- return ADV7180_INPUT_CONTROL_NTSC_443;
+ return ADV7180_STD_NTSC_443;
if (std == V4L2_STD_PAL_N)
- return ADV7180_INPUT_CONTROL_PAL_N;
+ return ADV7180_STD_PAL_N;
if (std == V4L2_STD_PAL_M)
- return ADV7180_INPUT_CONTROL_PAL_M;
+ return ADV7180_STD_PAL_M;
if (std == V4L2_STD_PAL_Nc)
- return ADV7180_INPUT_CONTROL_PAL_COMB_N;
+ return ADV7180_STD_PAL_COMB_N;
if (std & V4L2_STD_PAL)
- return ADV7180_INPUT_CONTROL_PAL_BG;
+ return ADV7180_STD_PAL_BG;
if (std & V4L2_STD_NTSC)
- return ADV7180_INPUT_CONTROL_NTSC_M;
+ return ADV7180_STD_NTSC_M;
if (std & V4L2_STD_SECAM)
- return ADV7180_INPUT_CONTROL_PAL_SECAM;
+ return ADV7180_STD_PAL_SECAM;
return -EINVAL;
}
@@ -193,10 +309,10 @@ static u32 adv7180_status_to_v4l2(u8 status1)
return 0;
}
-static int __adv7180_status(struct i2c_client *client, u32 *status,
+static int __adv7180_status(struct adv7180_state *state, u32 *status,
v4l2_std_id *std)
{
- int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
+ int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
if (status1 < 0)
return status1;
@@ -225,7 +341,7 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
if (!state->autodetect || state->irq > 0)
*std = state->curr_norm;
else
- err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
+ err = __adv7180_status(state, NULL, std);
mutex_unlock(&state->mutex);
return err;
@@ -236,26 +352,19 @@ static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
{
struct adv7180_state *state = to_state(sd);
int ret = mutex_lock_interruptible(&state->mutex);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
if (ret)
return ret;
- /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
- * all inputs and let the card driver take care of validation
- */
- if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
+ if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
+ ret = -EINVAL;
goto out;
+ }
- ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG);
-
- if (ret < 0)
- goto out;
+ ret = state->chip_info->select_input(state, input);
- ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_INPUT_CONTROL_REG, ret | input);
- state->input = input;
+ if (ret == 0)
+ state->input = input;
out:
mutex_unlock(&state->mutex);
return ret;
@@ -268,74 +377,104 @@ static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
if (ret)
return ret;
- ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
+ ret = __adv7180_status(state, status, NULL);
mutex_unlock(&state->mutex);
return ret;
}
+static int adv7180_program_std(struct adv7180_state *state)
+{
+ int ret;
+
+ if (state->autodetect) {
+ ret = adv7180_set_video_standard(state,
+ ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
+ if (ret < 0)
+ return ret;
+
+ __adv7180_status(state, NULL, &state->curr_norm);
+ } else {
+ ret = v4l2_std_to_adv7180(state->curr_norm);
+ if (ret < 0)
+ return ret;
+
+ ret = adv7180_set_video_standard(state, ret);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7180_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = mutex_lock_interruptible(&state->mutex);
+
if (ret)
return ret;
/* all standards -> autodetect */
if (std == V4L2_STD_ALL) {
- ret =
- i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
- ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
- | state->input);
- if (ret < 0)
- goto out;
-
- __adv7180_status(client, NULL, &state->curr_norm);
state->autodetect = true;
} else {
+ /* Make sure we can support this std */
ret = v4l2_std_to_adv7180(std);
if (ret < 0)
goto out;
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_INPUT_CONTROL_REG,
- ret | state->input);
- if (ret < 0)
- goto out;
-
state->curr_norm = std;
state->autodetect = false;
}
- ret = 0;
+
+ ret = adv7180_program_std(state);
out:
mutex_unlock(&state->mutex);
return ret;
}
-static int adv7180_set_power(struct adv7180_state *state,
- struct i2c_client *client, bool on)
+static int adv7180_set_power(struct adv7180_state *state, bool on)
{
u8 val;
+ int ret;
if (on)
val = ADV7180_PWR_MAN_ON;
else
val = ADV7180_PWR_MAN_OFF;
- return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val);
+ ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
+ if (ret)
+ return ret;
+
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+ if (on) {
+ adv7180_csi_write(state, 0xDE, 0x02);
+ adv7180_csi_write(state, 0xD2, 0xF7);
+ adv7180_csi_write(state, 0xD8, 0x65);
+ adv7180_csi_write(state, 0xE0, 0x09);
+ adv7180_csi_write(state, 0x2C, 0x00);
+ if (state->field == V4L2_FIELD_NONE)
+ adv7180_csi_write(state, 0x1D, 0x80);
+ adv7180_csi_write(state, 0x00, 0x00);
+ } else {
+ adv7180_csi_write(state, 0x00, 0x80);
+ }
+ }
+
+ return 0;
}
static int adv7180_s_power(struct v4l2_subdev *sd, int on)
{
struct adv7180_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret;
ret = mutex_lock_interruptible(&state->mutex);
if (ret)
return ret;
- ret = adv7180_set_power(state, client, on);
+ ret = adv7180_set_power(state, on);
if (ret == 0)
state->powered = on;
@@ -347,7 +486,6 @@ static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
struct adv7180_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
int ret = mutex_lock_interruptible(&state->mutex);
int val;
@@ -356,26 +494,36 @@ static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
val = ctrl->val;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val);
+ ret = adv7180_write(state, ADV7180_REG_BRI, val);
break;
case V4L2_CID_HUE:
/*Hue is inverted according to HSL chart */
- ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val);
+ ret = adv7180_write(state, ADV7180_REG_HUE, -val);
break;
case V4L2_CID_CONTRAST:
- ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val);
+ ret = adv7180_write(state, ADV7180_REG_CON, val);
break;
case V4L2_CID_SATURATION:
/*
*This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
*Let's not confuse the user, everybody understands saturation
*/
- ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
- val);
+ ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
if (ret < 0)
break;
- ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
- val);
+ ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
+ break;
+ case V4L2_CID_ADV_FAST_SWITCH:
+ if (ctrl->val) {
+ /* ADI required write */
+ adv7180_write(state, 0x80d9, 0x44);
+ adv7180_write(state, ADV7180_REG_FLCONTROL,
+ ADV7180_FLCONTROL_FL_ENABLE);
+ } else {
+ /* ADI required write */
+ adv7180_write(state, 0x80d9, 0xc4);
+ adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00);
+ }
break;
default:
ret = -EINVAL;
@@ -389,6 +537,16 @@ static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
.s_ctrl = adv7180_s_ctrl,
};
+static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = {
+ .ops = &adv7180_ctrl_ops,
+ .id = V4L2_CID_ADV_FAST_SWITCH,
+ .name = "Fast Switching",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+};
+
static int adv7180_init_controls(struct adv7180_state *state)
{
v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
@@ -405,6 +563,8 @@ static int adv7180_init_controls(struct adv7180_state *state)
v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
V4L2_CID_HUE, ADV7180_HUE_MIN,
ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
+ v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL);
+
state->sd.ctrl_handler = &state->ctrl_hdl;
if (state->ctrl_hdl.error) {
int err = state->ctrl_hdl.error;
@@ -421,13 +581,14 @@ static void adv7180_exit_controls(struct adv7180_state *state)
v4l2_ctrl_handler_free(&state->ctrl_hdl);
}
-static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
- u32 *code)
+static int adv7180_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
{
- if (index > 0)
+ if (code->index != 0)
return -EINVAL;
- *code = MEDIA_BUS_FMT_YUYV8_2X8;
+ code->code = MEDIA_BUS_FMT_YUYV8_2X8;
return 0;
}
@@ -439,23 +600,118 @@ static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- fmt->field = V4L2_FIELD_INTERLACED;
fmt->width = 720;
fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
return 0;
}
+static int adv7180_set_field_mode(struct adv7180_state *state)
+{
+ if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
+ return 0;
+
+ if (state->field == V4L2_FIELD_NONE) {
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+ adv7180_csi_write(state, 0x01, 0x20);
+ adv7180_csi_write(state, 0x02, 0x28);
+ adv7180_csi_write(state, 0x03, 0x38);
+ adv7180_csi_write(state, 0x04, 0x30);
+ adv7180_csi_write(state, 0x05, 0x30);
+ adv7180_csi_write(state, 0x06, 0x80);
+ adv7180_csi_write(state, 0x07, 0x70);
+ adv7180_csi_write(state, 0x08, 0x50);
+ }
+ adv7180_vpp_write(state, 0xa3, 0x00);
+ adv7180_vpp_write(state, 0x5b, 0x00);
+ adv7180_vpp_write(state, 0x55, 0x80);
+ } else {
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+ adv7180_csi_write(state, 0x01, 0x18);
+ adv7180_csi_write(state, 0x02, 0x18);
+ adv7180_csi_write(state, 0x03, 0x30);
+ adv7180_csi_write(state, 0x04, 0x20);
+ adv7180_csi_write(state, 0x05, 0x28);
+ adv7180_csi_write(state, 0x06, 0x40);
+ adv7180_csi_write(state, 0x07, 0x58);
+ adv7180_csi_write(state, 0x08, 0x30);
+ }
+ adv7180_vpp_write(state, 0xa3, 0x70);
+ adv7180_vpp_write(state, 0x5b, 0x80);
+ adv7180_vpp_write(state, 0x55, 0x00);
+ }
+
+ return 0;
+}
+
+static int adv7180_get_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7180_state *state = to_state(sd);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ format->format = *v4l2_subdev_get_try_format(fh, 0);
+ } else {
+ adv7180_mbus_fmt(sd, &format->format);
+ format->format.field = state->field;
+ }
+
+ return 0;
+}
+
+static int adv7180_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7180_state *state = to_state(sd);
+ struct v4l2_mbus_framefmt *framefmt;
+
+ switch (format->format.field) {
+ case V4L2_FIELD_NONE:
+ if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
+ format->format.field = V4L2_FIELD_INTERLACED;
+ break;
+ default:
+ format->format.field = V4L2_FIELD_INTERLACED;
+ break;
+ }
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ framefmt = &format->format;
+ if (state->field != format->format.field) {
+ state->field = format->format.field;
+ adv7180_set_power(state, false);
+ adv7180_set_field_mode(state);
+ adv7180_set_power(state, true);
+ }
+ } else {
+ framefmt = v4l2_subdev_get_try_format(fh, 0);
+ *framefmt = format->format;
+ }
+
+ return adv7180_mbus_fmt(sd, framefmt);
+}
+
static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
- /*
- * The ADV7180 sensor supports BT.601/656 output modes.
- * The BT.656 is default and not yet configurable by s/w.
- */
- cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_BT656;
+ struct adv7180_state *state = to_state(sd);
+
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_1_LANE |
+ V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+ } else {
+ /*
+ * The ADV7180 sensor supports BT.601/656 output modes.
+ * The BT.656 is default and not yet configurable by s/w.
+ */
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_BT656;
+ }
return 0;
}
@@ -465,139 +721,439 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
.s_routing = adv7180_s_routing,
- .enum_mbus_fmt = adv7180_enum_mbus_fmt,
- .try_mbus_fmt = adv7180_mbus_fmt,
- .g_mbus_fmt = adv7180_mbus_fmt,
- .s_mbus_fmt = adv7180_mbus_fmt,
.g_mbus_config = adv7180_g_mbus_config,
};
+
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.s_power = adv7180_s_power,
};
+static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
+ .enum_mbus_code = adv7180_enum_mbus_code,
+ .set_fmt = adv7180_set_pad_format,
+ .get_fmt = adv7180_get_pad_format,
+};
+
static const struct v4l2_subdev_ops adv7180_ops = {
.core = &adv7180_core_ops,
.video = &adv7180_video_ops,
+ .pad = &adv7180_pad_ops,
};
static irqreturn_t adv7180_irq(int irq, void *devid)
{
struct adv7180_state *state = devid;
- struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
u8 isr3;
mutex_lock(&state->mutex);
- i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
- ADV7180_ADI_CTRL_IRQ_SPACE);
- isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
+ isr3 = adv7180_read(state, ADV7180_REG_ISR3);
/* clear */
- i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
- i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
+ adv7180_write(state, ADV7180_REG_ICR3, isr3);
if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
- __adv7180_status(client, NULL, &state->curr_norm);
+ __adv7180_status(state, NULL, &state->curr_norm);
mutex_unlock(&state->mutex);
return IRQ_HANDLED;
}
-static int init_device(struct i2c_client *client, struct adv7180_state *state)
+static int adv7180_init(struct adv7180_state *state)
{
int ret;
- /* Initialize adv7180 */
- /* Enable autodetection */
- if (state->autodetect) {
- ret =
- i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
- ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
- | state->input);
- if (ret < 0)
- return ret;
-
- ret =
- i2c_smbus_write_byte_data(client,
- ADV7180_AUTODETECT_ENABLE_REG,
- ADV7180_AUTODETECT_DEFAULT);
- if (ret < 0)
- return ret;
- } else {
- ret = v4l2_std_to_adv7180(state->curr_norm);
- if (ret < 0)
- return ret;
-
- ret =
- i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
- ret | state->input);
- if (ret < 0)
- return ret;
-
- }
/* ITU-R BT.656-4 compatible */
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
+ ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
if (ret < 0)
return ret;
/* Manually set V bit end position in NTSC mode */
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_NTSC_V_BIT_END_REG,
+ return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
+}
+
+static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
+{
+ return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
+ (std << 4) | state->input);
+}
+
+static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
+{
+ int ret;
+
+ ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
if (ret < 0)
return ret;
- /* read current norm */
- __adv7180_status(client, NULL, &state->curr_norm);
+ ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
+ ret |= input;
+ return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
+}
- /* register for interrupts */
- if (state->irq > 0) {
- ret = request_threaded_irq(state->irq, NULL, adv7180_irq,
- IRQF_ONESHOT, KBUILD_MODNAME, state);
- if (ret)
- return ret;
+static int adv7182_init(struct adv7180_state *state)
+{
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
+ adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
+ ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
+
+ if (state->chip_info->flags & ADV7180_FLAG_I2P)
+ adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
+ ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
+
+ if (state->chip_info->flags & ADV7180_FLAG_V2) {
+ /* ADI recommended writes for improved video quality */
+ adv7180_write(state, 0x0080, 0x51);
+ adv7180_write(state, 0x0081, 0x51);
+ adv7180_write(state, 0x0082, 0x68);
+ }
- ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
- ADV7180_ADI_CTRL_IRQ_SPACE);
- if (ret < 0)
- goto err;
+ /* ADI required writes */
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+ adv7180_write(state, 0x0003, 0x4e);
+ adv7180_write(state, 0x0004, 0x57);
+ adv7180_write(state, 0x001d, 0xc0);
+ } else {
+ if (state->chip_info->flags & ADV7180_FLAG_V2)
+ adv7180_write(state, 0x0004, 0x17);
+ else
+ adv7180_write(state, 0x0004, 0x07);
+ adv7180_write(state, 0x0003, 0x0c);
+ adv7180_write(state, 0x001d, 0x40);
+ }
+
+ adv7180_write(state, 0x0013, 0x00);
+
+ return 0;
+}
+
+static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
+{
+ return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
+}
+
+enum adv7182_input_type {
+ ADV7182_INPUT_TYPE_CVBS,
+ ADV7182_INPUT_TYPE_DIFF_CVBS,
+ ADV7182_INPUT_TYPE_SVIDEO,
+ ADV7182_INPUT_TYPE_YPBPR,
+};
+
+static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
+{
+ switch (input) {
+ case ADV7182_INPUT_CVBS_AIN1:
+ case ADV7182_INPUT_CVBS_AIN2:
+ case ADV7182_INPUT_CVBS_AIN3:
+ case ADV7182_INPUT_CVBS_AIN4:
+ case ADV7182_INPUT_CVBS_AIN5:
+ case ADV7182_INPUT_CVBS_AIN6:
+ case ADV7182_INPUT_CVBS_AIN7:
+ case ADV7182_INPUT_CVBS_AIN8:
+ return ADV7182_INPUT_TYPE_CVBS;
+ case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
+ case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
+ case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
+ case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
+ return ADV7182_INPUT_TYPE_SVIDEO;
+ case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
+ case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
+ return ADV7182_INPUT_TYPE_YPBPR;
+ case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
+ case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
+ case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
+ case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
+ return ADV7182_INPUT_TYPE_DIFF_CVBS;
+ default: /* Will never happen */
+ return 0;
+ }
+}
+
+/* ADI recommended writes to registers 0x52, 0x53, 0x54 */
+static unsigned int adv7182_lbias_settings[][3] = {
+ [ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
+ [ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
+ [ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
+ [ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
+};
+
+static unsigned int adv7280_lbias_settings[][3] = {
+ [ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
+ [ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
+ [ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
+ [ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
+};
+
+static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
+{
+ enum adv7182_input_type input_type;
+ unsigned int *lbias;
+ unsigned int i;
+ int ret;
+
+ ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
+ if (ret)
+ return ret;
+
+ /* Reset clamp circuitry - ADI recommended writes */
+ adv7180_write(state, 0x809c, 0x00);
+ adv7180_write(state, 0x809c, 0xff);
+
+ input_type = adv7182_get_input_type(input);
+
+ switch (input_type) {
+ case ADV7182_INPUT_TYPE_CVBS:
+ case ADV7182_INPUT_TYPE_DIFF_CVBS:
+ /* ADI recommends to use the SH1 filter */
+ adv7180_write(state, 0x0017, 0x41);
+ break;
+ default:
+ adv7180_write(state, 0x0017, 0x01);
+ break;
+ }
+
+ if (state->chip_info->flags & ADV7180_FLAG_V2)
+ lbias = adv7280_lbias_settings[input_type];
+ else
+ lbias = adv7182_lbias_settings[input_type];
+
+ for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
+ adv7180_write(state, 0x0052 + i, lbias[i]);
+
+ if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
+ /* ADI required writes to make differential CVBS work */
+ adv7180_write(state, 0x005f, 0xa8);
+ adv7180_write(state, 0x005a, 0x90);
+ adv7180_write(state, 0x0060, 0xb0);
+ adv7180_write(state, 0x80b6, 0x08);
+ adv7180_write(state, 0x80c0, 0xa0);
+ } else {
+ adv7180_write(state, 0x005f, 0xf0);
+ adv7180_write(state, 0x005a, 0xd0);
+ adv7180_write(state, 0x0060, 0x10);
+ adv7180_write(state, 0x80b6, 0x9c);
+ adv7180_write(state, 0x80c0, 0x00);
+ }
+
+ return 0;
+}
+
+static const struct adv7180_chip_info adv7180_info = {
+ .flags = ADV7180_FLAG_RESET_POWERED,
+ /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
+ * all inputs and let the card driver take care of validation
+ */
+ .valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
+ BIT(ADV7180_INPUT_CVBS_AIN2) |
+ BIT(ADV7180_INPUT_CVBS_AIN3) |
+ BIT(ADV7180_INPUT_CVBS_AIN4) |
+ BIT(ADV7180_INPUT_CVBS_AIN5) |
+ BIT(ADV7180_INPUT_CVBS_AIN6) |
+ BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
+ BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
+ BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+ BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
+ .init = adv7180_init,
+ .set_std = adv7180_set_std,
+ .select_input = adv7180_select_input,
+};
+
+static const struct adv7180_chip_info adv7182_info = {
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN3) |
+ BIT(ADV7182_INPUT_CVBS_AIN4) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7280_info = {
+ .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN3) |
+ BIT(ADV7182_INPUT_CVBS_AIN4) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7280_m_info = {
+ .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN3) |
+ BIT(ADV7182_INPUT_CVBS_AIN4) |
+ BIT(ADV7182_INPUT_CVBS_AIN5) |
+ BIT(ADV7182_INPUT_CVBS_AIN6) |
+ BIT(ADV7182_INPUT_CVBS_AIN7) |
+ BIT(ADV7182_INPUT_CVBS_AIN8) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+ BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+ BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7281_info = {
+ .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN7) |
+ BIT(ADV7182_INPUT_CVBS_AIN8) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7281_m_info = {
+ .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN3) |
+ BIT(ADV7182_INPUT_CVBS_AIN4) |
+ BIT(ADV7182_INPUT_CVBS_AIN7) |
+ BIT(ADV7182_INPUT_CVBS_AIN8) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+ BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+static const struct adv7180_chip_info adv7281_ma_info = {
+ .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN3) |
+ BIT(ADV7182_INPUT_CVBS_AIN4) |
+ BIT(ADV7182_INPUT_CVBS_AIN5) |
+ BIT(ADV7182_INPUT_CVBS_AIN6) |
+ BIT(ADV7182_INPUT_CVBS_AIN7) |
+ BIT(ADV7182_INPUT_CVBS_AIN8) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+ BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
+ BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7282_info = {
+ .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN7) |
+ BIT(ADV7182_INPUT_CVBS_AIN8) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+
+static const struct adv7180_chip_info adv7282_m_info = {
+ .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
+ .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
+ BIT(ADV7182_INPUT_CVBS_AIN2) |
+ BIT(ADV7182_INPUT_CVBS_AIN3) |
+ BIT(ADV7182_INPUT_CVBS_AIN4) |
+ BIT(ADV7182_INPUT_CVBS_AIN7) |
+ BIT(ADV7182_INPUT_CVBS_AIN8) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
+ .init = adv7182_init,
+ .set_std = adv7182_set_std,
+ .select_input = adv7182_select_input,
+};
+
+static int init_device(struct adv7180_state *state)
+{
+ int ret;
+
+ mutex_lock(&state->mutex);
+
+ adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
+ usleep_range(2000, 10000);
+
+ ret = state->chip_info->init(state);
+ if (ret)
+ goto out_unlock;
+
+ ret = adv7180_program_std(state);
+ if (ret)
+ goto out_unlock;
+
+ adv7180_set_field_mode(state);
+
+ /* register for interrupts */
+ if (state->irq > 0) {
/* config the Interrupt pin to be active low */
- ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
+ ret = adv7180_write(state, ADV7180_REG_ICONF1,
ADV7180_ICONF1_ACTIVE_LOW |
ADV7180_ICONF1_PSYNC_ONLY);
if (ret < 0)
- goto err;
+ goto out_unlock;
- ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
+ ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
if (ret < 0)
- goto err;
+ goto out_unlock;
- ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
+ ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
if (ret < 0)
- goto err;
+ goto out_unlock;
/* enable AD change interrupts interrupts */
- ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
+ ret = adv7180_write(state, ADV7180_REG_IMR3,
ADV7180_IRQ3_AD_CHANGE);
if (ret < 0)
- goto err;
+ goto out_unlock;
- ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
+ ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
if (ret < 0)
- goto err;
-
- ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
- 0);
- if (ret < 0)
- goto err;
+ goto out_unlock;
}
- return 0;
+out_unlock:
+ mutex_unlock(&state->mutex);
-err:
- free_irq(state->irq, state);
return ret;
}
@@ -616,26 +1172,63 @@ static int adv7180_probe(struct i2c_client *client,
client->addr, client->adapter->name);
state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
- if (state == NULL) {
- ret = -ENOMEM;
- goto err;
+ if (state == NULL)
+ return -ENOMEM;
+
+ state->client = client;
+ state->field = V4L2_FIELD_INTERLACED;
+ state->chip_info = (struct adv7180_chip_info *)id->driver_data;
+
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
+ state->csi_client = i2c_new_dummy(client->adapter,
+ ADV7180_DEFAULT_CSI_I2C_ADDR);
+ if (!state->csi_client)
+ return -ENOMEM;
+ }
+
+ if (state->chip_info->flags & ADV7180_FLAG_I2P) {
+ state->vpp_client = i2c_new_dummy(client->adapter,
+ ADV7180_DEFAULT_VPP_I2C_ADDR);
+ if (!state->vpp_client) {
+ ret = -ENOMEM;
+ goto err_unregister_csi_client;
+ }
}
state->irq = client->irq;
mutex_init(&state->mutex);
state->autodetect = true;
- state->powered = true;
+ if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
+ state->powered = true;
+ else
+ state->powered = false;
state->input = 0;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
ret = adv7180_init_controls(state);
if (ret)
- goto err_unreg_subdev;
- ret = init_device(client, state);
+ goto err_unregister_vpp_client;
+
+ state->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+ ret = media_entity_init(&sd->entity, 1, &state->pad, 0);
if (ret)
goto err_free_ctrl;
+ ret = init_device(state);
+ if (ret)
+ goto err_media_entity_cleanup;
+
+ if (state->irq) {
+ ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ KBUILD_MODNAME, state);
+ if (ret)
+ goto err_media_entity_cleanup;
+ }
+
ret = v4l2_async_register_subdev(sd);
if (ret)
goto err_free_irq;
@@ -645,11 +1238,17 @@ static int adv7180_probe(struct i2c_client *client,
err_free_irq:
if (state->irq > 0)
free_irq(client->irq, state);
+err_media_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
err_free_ctrl:
adv7180_exit_controls(state);
-err_unreg_subdev:
+err_unregister_vpp_client:
+ if (state->chip_info->flags & ADV7180_FLAG_I2P)
+ i2c_unregister_device(state->vpp_client);
+err_unregister_csi_client:
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
+ i2c_unregister_device(state->csi_client);
mutex_destroy(&state->mutex);
-err:
return ret;
}
@@ -663,15 +1262,32 @@ static int adv7180_remove(struct i2c_client *client)
if (state->irq > 0)
free_irq(client->irq, state);
+ media_entity_cleanup(&sd->entity);
adv7180_exit_controls(state);
+
+ if (state->chip_info->flags & ADV7180_FLAG_I2P)
+ i2c_unregister_device(state->vpp_client);
+ if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
+ i2c_unregister_device(state->csi_client);
+
mutex_destroy(&state->mutex);
+
return 0;
}
static const struct i2c_device_id adv7180_id[] = {
- {KBUILD_MODNAME, 0},
+ { "adv7180", (kernel_ulong_t)&adv7180_info },
+ { "adv7182", (kernel_ulong_t)&adv7182_info },
+ { "adv7280", (kernel_ulong_t)&adv7280_info },
+ { "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
+ { "adv7281", (kernel_ulong_t)&adv7281_info },
+ { "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
+ { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
+ { "adv7282", (kernel_ulong_t)&adv7282_info },
+ { "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
{},
};
+MODULE_DEVICE_TABLE(i2c, adv7180_id);
#ifdef CONFIG_PM_SLEEP
static int adv7180_suspend(struct device *dev)
@@ -680,7 +1296,7 @@ static int adv7180_suspend(struct device *dev)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct adv7180_state *state = to_state(sd);
- return adv7180_set_power(state, client, false);
+ return adv7180_set_power(state, false);
}
static int adv7180_resume(struct device *dev)
@@ -690,14 +1306,14 @@ static int adv7180_resume(struct device *dev)
struct adv7180_state *state = to_state(sd);
int ret;
- if (state->powered) {
- ret = adv7180_set_power(state, client, true);
- if (ret)
- return ret;
- }
- ret = init_device(client, state);
+ ret = init_device(state);
if (ret < 0)
return ret;
+
+ ret = adv7180_set_power(state, state->powered);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -708,8 +1324,6 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
#define ADV7180_PM_OPS NULL
#endif
-MODULE_DEVICE_TABLE(i2c, adv7180_id);
-
static struct i2c_driver adv7180_driver = {
.driver = {
.owner = THIS_MODULE,