From 851a54effbd808daf8b961f1dc6156c06a96d5f1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 23 Jan 2015 12:52:32 -0300 Subject: [PATCH] [media] adv7180: Add I2P support Some of the devices supported by this driver have a interlaced-to- progressive converter which can optionally be enabled. This patch adds support for enabling and disabling the I2P converter on such devices. I2P mode can be enabled by selecting V4L2_FIELD_NONE instead of V4L2_FIELD_INTERLACED for the format. Signed-off-by: Lars-Peter Clausen Acked-by: Federico Vaga Acked-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 156 ++++++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 195d28f48c30..3c1c866d1e3c 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -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); -- 2.39.2