|
@@ -124,6 +124,7 @@
|
|
|
#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_CSI_REG_PWRDN 0x00
|
|
@@ -161,12 +162,14 @@
|
|
|
#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
|
|
|
|
|
|
#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
|
|
|
+#define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
|
|
|
|
|
|
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;
|
|
@@ -190,7 +193,9 @@ struct adv7180_state {
|
|
|
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, \
|
|
@@ -234,6 +239,12 @@ static int adv7180_set_video_standard(struct adv7180_state *state,
|
|
|
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 */
|
|
@@ -438,6 +449,8 @@ static int adv7180_set_power(struct adv7180_state *state, bool on)
|
|
|
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);
|
|
@@ -558,25 +571,97 @@ 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)
|
|
|
{
|
|
|
- return adv7180_mbus_fmt(sd, &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)
|
|
|
{
|
|
|
- return adv7180_mbus_fmt(sd, &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,
|
|
@@ -684,6 +769,10 @@ static int adv7182_init(struct adv7180_state *state)
|
|
|
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);
|
|
@@ -858,7 +947,7 @@ static const struct adv7180_chip_info adv7182_info = {
|
|
|
};
|
|
|
|
|
|
static const struct adv7180_chip_info adv7280_info = {
|
|
|
- .flags = ADV7180_FLAG_V2,
|
|
|
+ .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) |
|
|
@@ -872,7 +961,7 @@ static const struct adv7180_chip_info adv7280_info = {
|
|
|
};
|
|
|
|
|
|
static const struct adv7180_chip_info adv7280_m_info = {
|
|
|
- .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
|
|
|
+ .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) |
|
|
@@ -952,6 +1041,40 @@ static const struct adv7180_chip_info adv7281_ma_info = {
|
|
|
.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;
|
|
@@ -969,6 +1092,8 @@ static int init_device(struct adv7180_state *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 */
|
|
@@ -1022,6 +1147,7 @@ static int adv7180_probe(struct i2c_client *client,
|
|
|
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) {
|
|
@@ -1031,6 +1157,15 @@ static int adv7180_probe(struct i2c_client *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;
|
|
@@ -1045,7 +1180,7 @@ static int adv7180_probe(struct i2c_client *client,
|
|
|
|
|
|
ret = adv7180_init_controls(state);
|
|
|
if (ret)
|
|
|
- goto err_unregister_csi_client;
|
|
|
+ goto err_unregister_vpp_client;
|
|
|
|
|
|
state->pad.flags = MEDIA_PAD_FL_SOURCE;
|
|
|
sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
|
|
@@ -1078,6 +1213,9 @@ err_media_entity_cleanup:
|
|
|
media_entity_cleanup(&sd->entity);
|
|
|
err_free_ctrl:
|
|
|
adv7180_exit_controls(state);
|
|
|
+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);
|
|
@@ -1098,6 +1236,8 @@ static int adv7180_remove(struct i2c_client *client)
|
|
|
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);
|
|
|
|
|
@@ -1114,8 +1254,8 @@ static const struct i2c_device_id adv7180_id[] = {
|
|
|
{ "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)&adv7281_info },
|
|
|
- { "adv7282-m", (kernel_ulong_t)&adv7281_m_info },
|
|
|
+ { "adv7282", (kernel_ulong_t)&adv7282_info },
|
|
|
+ { "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
|
|
|
{},
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(i2c, adv7180_id);
|