Commit 851a54ef authored by Lars-Peter Clausen's avatar Lars-Peter Clausen Committed by Mauro Carvalho Chehab

[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: default avatarLars-Peter Clausen <lars@metafoo.de>
Acked-by: default avatarFederico Vaga <federico.vaga@gmail.com>
Acked-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent b37135e3
...@@ -124,6 +124,7 @@ ...@@ -124,6 +124,7 @@
#define ADV7180_REG_NTSC_V_BIT_END 0x00E6 #define ADV7180_REG_NTSC_V_BIT_END 0x00E6
#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F #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_CSI_SLAVE_ADDR 0xFE
#define ADV7180_CSI_REG_PWRDN 0x00 #define ADV7180_CSI_REG_PWRDN 0x00
...@@ -161,12 +162,14 @@ ...@@ -161,12 +162,14 @@
#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11 #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44 #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
#define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
struct adv7180_state; struct adv7180_state;
#define ADV7180_FLAG_RESET_POWERED BIT(0) #define ADV7180_FLAG_RESET_POWERED BIT(0)
#define ADV7180_FLAG_V2 BIT(1) #define ADV7180_FLAG_V2 BIT(1)
#define ADV7180_FLAG_MIPI_CSI2 BIT(2) #define ADV7180_FLAG_MIPI_CSI2 BIT(2)
#define ADV7180_FLAG_I2P BIT(3)
struct adv7180_chip_info { struct adv7180_chip_info {
unsigned int flags; unsigned int flags;
...@@ -190,7 +193,9 @@ struct adv7180_state { ...@@ -190,7 +193,9 @@ struct adv7180_state {
struct i2c_client *client; struct i2c_client *client;
unsigned int register_page; unsigned int register_page;
struct i2c_client *csi_client; struct i2c_client *csi_client;
struct i2c_client *vpp_client;
const struct adv7180_chip_info *chip_info; const struct adv7180_chip_info *chip_info;
enum v4l2_field field;
}; };
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
struct adv7180_state, \ struct adv7180_state, \
...@@ -234,6 +239,12 @@ static int adv7180_set_video_standard(struct adv7180_state *state, ...@@ -234,6 +239,12 @@ static int adv7180_set_video_standard(struct adv7180_state *state,
return state->chip_info->set_std(state, 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) static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
{ {
/* in case V4L2_IN_ST_NO_SIGNAL */ /* in case V4L2_IN_ST_NO_SIGNAL */
...@@ -438,6 +449,8 @@ static int adv7180_set_power(struct adv7180_state *state, bool on) ...@@ -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, 0xD8, 0x65);
adv7180_csi_write(state, 0xE0, 0x09); adv7180_csi_write(state, 0xE0, 0x09);
adv7180_csi_write(state, 0x2C, 0x00); adv7180_csi_write(state, 0x2C, 0x00);
if (state->field == V4L2_FIELD_NONE)
adv7180_csi_write(state, 0x1D, 0x80);
adv7180_csi_write(state, 0x00, 0x00); adv7180_csi_write(state, 0x00, 0x00);
} else { } else {
adv7180_csi_write(state, 0x00, 0x80); adv7180_csi_write(state, 0x00, 0x80);
...@@ -558,25 +571,97 @@ static int adv7180_mbus_fmt(struct v4l2_subdev *sd, ...@@ -558,25 +571,97 @@ static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
fmt->field = V4L2_FIELD_INTERLACED;
fmt->width = 720; fmt->width = 720;
fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576; fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
return 0; 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, static int adv7180_get_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *format) 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, static int adv7180_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *format) 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, static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
...@@ -684,6 +769,10 @@ static int adv7182_init(struct adv7180_state *state) ...@@ -684,6 +769,10 @@ static int adv7182_init(struct adv7180_state *state)
adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR, adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
ADV7180_DEFAULT_CSI_I2C_ADDR << 1); 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) { if (state->chip_info->flags & ADV7180_FLAG_V2) {
/* ADI recommended writes for improved video quality */ /* ADI recommended writes for improved video quality */
adv7180_write(state, 0x0080, 0x51); adv7180_write(state, 0x0080, 0x51);
...@@ -858,7 +947,7 @@ static const struct adv7180_chip_info adv7182_info = { ...@@ -858,7 +947,7 @@ static const struct adv7180_chip_info adv7182_info = {
}; };
static const struct adv7180_chip_info adv7280_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) | .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN2) |
BIT(ADV7182_INPUT_CVBS_AIN3) | BIT(ADV7182_INPUT_CVBS_AIN3) |
...@@ -872,7 +961,7 @@ static const struct adv7180_chip_info adv7280_info = { ...@@ -872,7 +961,7 @@ static const struct adv7180_chip_info adv7280_info = {
}; };
static const struct adv7180_chip_info adv7280_m_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) | .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
BIT(ADV7182_INPUT_CVBS_AIN2) | BIT(ADV7182_INPUT_CVBS_AIN2) |
BIT(ADV7182_INPUT_CVBS_AIN3) | BIT(ADV7182_INPUT_CVBS_AIN3) |
...@@ -952,6 +1041,40 @@ static const struct adv7180_chip_info adv7281_ma_info = { ...@@ -952,6 +1041,40 @@ static const struct adv7180_chip_info adv7281_ma_info = {
.select_input = adv7182_select_input, .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) static int init_device(struct adv7180_state *state)
{ {
int ret; int ret;
...@@ -969,6 +1092,8 @@ static int init_device(struct adv7180_state *state) ...@@ -969,6 +1092,8 @@ static int init_device(struct adv7180_state *state)
if (ret) if (ret)
goto out_unlock; goto out_unlock;
adv7180_set_field_mode(state);
/* register for interrupts */ /* register for interrupts */
if (state->irq > 0) { if (state->irq > 0) {
/* config the Interrupt pin to be active low */ /* config the Interrupt pin to be active low */
...@@ -1022,6 +1147,7 @@ static int adv7180_probe(struct i2c_client *client, ...@@ -1022,6 +1147,7 @@ static int adv7180_probe(struct i2c_client *client,
return -ENOMEM; return -ENOMEM;
state->client = client; state->client = client;
state->field = V4L2_FIELD_INTERLACED;
state->chip_info = (struct adv7180_chip_info *)id->driver_data; state->chip_info = (struct adv7180_chip_info *)id->driver_data;
if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
...@@ -1031,6 +1157,15 @@ static int adv7180_probe(struct i2c_client *client, ...@@ -1031,6 +1157,15 @@ static int adv7180_probe(struct i2c_client *client,
return -ENOMEM; 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; state->irq = client->irq;
mutex_init(&state->mutex); mutex_init(&state->mutex);
state->autodetect = true; state->autodetect = true;
...@@ -1045,7 +1180,7 @@ static int adv7180_probe(struct i2c_client *client, ...@@ -1045,7 +1180,7 @@ static int adv7180_probe(struct i2c_client *client,
ret = adv7180_init_controls(state); ret = adv7180_init_controls(state);
if (ret) if (ret)
goto err_unregister_csi_client; goto err_unregister_vpp_client;
state->pad.flags = MEDIA_PAD_FL_SOURCE; state->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
...@@ -1078,6 +1213,9 @@ static int adv7180_probe(struct i2c_client *client, ...@@ -1078,6 +1213,9 @@ static int adv7180_probe(struct i2c_client *client,
media_entity_cleanup(&sd->entity); media_entity_cleanup(&sd->entity);
err_free_ctrl: err_free_ctrl:
adv7180_exit_controls(state); 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: err_unregister_csi_client:
if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
i2c_unregister_device(state->csi_client); i2c_unregister_device(state->csi_client);
...@@ -1098,6 +1236,8 @@ static int adv7180_remove(struct i2c_client *client) ...@@ -1098,6 +1236,8 @@ static int adv7180_remove(struct i2c_client *client)
media_entity_cleanup(&sd->entity); media_entity_cleanup(&sd->entity);
adv7180_exit_controls(state); 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) if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
i2c_unregister_device(state->csi_client); i2c_unregister_device(state->csi_client);
...@@ -1114,8 +1254,8 @@ static const struct i2c_device_id adv7180_id[] = { ...@@ -1114,8 +1254,8 @@ static const struct i2c_device_id adv7180_id[] = {
{ "adv7281", (kernel_ulong_t)&adv7281_info }, { "adv7281", (kernel_ulong_t)&adv7281_info },
{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info }, { "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info }, { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
{ "adv7282", (kernel_ulong_t)&adv7281_info }, { "adv7282", (kernel_ulong_t)&adv7282_info },
{ "adv7282-m", (kernel_ulong_t)&adv7281_m_info }, { "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
{}, {},
}; };
MODULE_DEVICE_TABLE(i2c, adv7180_id); MODULE_DEVICE_TABLE(i2c, adv7180_id);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment