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

[media] adv7180: Add support for the adv7280-m/adv7281-m/adv7281-ma/adv7282-m

This patch adds support for the adv7280-m/adv2781-m/adv7281-ma/adv7282-m
devices to the adv7180 driver. They are very similar to the
adv7280/adv7281/adv7282 but instead of parallel video out they feature a
MIPI CSI2 transmitter.

The CSI2 transmitter is configured via a separate I2C address, so we need to
register a dummy device for it.
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 bf7dcb80
......@@ -124,6 +124,11 @@
#define ADV7180_REG_NTSC_V_BIT_END 0x00E6
#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
#define ADV7180_REG_CSI_SLAVE_ADDR 0xFE
#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
......@@ -155,10 +160,13 @@
#define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
struct adv7180_state;
#define ADV7180_FLAG_RESET_POWERED BIT(0)
#define ADV7180_FLAG_V2 BIT(1)
#define ADV7180_FLAG_MIPI_CSI2 BIT(2)
struct adv7180_chip_info {
unsigned int flags;
......@@ -181,6 +189,7 @@ struct adv7180_state {
struct i2c_client *client;
unsigned int register_page;
struct i2c_client *csi_client;
const struct adv7180_chip_info *chip_info;
};
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
......@@ -213,6 +222,12 @@ static int adv7180_read(struct adv7180_state *state, unsigned int reg)
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)
{
......@@ -405,13 +420,31 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
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 adv7180_write(state, ADV7180_REG_PWR_MAN, 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);
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)
......@@ -549,6 +582,14 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd,
static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
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.
......@@ -556,6 +597,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
V4L2_MBUS_DATA_ACTIVE_HIGH;
cfg->type = V4L2_MBUS_BT656;
}
return 0;
}
......@@ -638,20 +680,32 @@ static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
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_V2) {
/* ADI recommended writes for improved video quality */
adv7180_write(state, 0x0080, 0x51);
adv7180_write(state, 0x0081, 0x51);
adv7180_write(state, 0x0082, 0x68);
adv7180_write(state, 0x0004, 0x17);
} else {
adv7180_write(state, 0x0004, 0x07);
}
/* 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, 0x0013, 0x00);
adv7180_write(state, 0x001d, 0x40);
}
adv7180_write(state, 0x0013, 0x00);
return 0;
}
......@@ -817,15 +871,81 @@ static const struct adv7180_chip_info adv7280_info = {
.select_input = adv7182_select_input,
};
static const struct adv7180_chip_info adv7280_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_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,
.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,
......@@ -904,6 +1024,13 @@ static int adv7180_probe(struct i2c_client *client,
state->client = client;
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;
}
state->irq = client->irq;
mutex_init(&state->mutex);
state->autodetect = true;
......@@ -918,7 +1045,7 @@ static int adv7180_probe(struct i2c_client *client,
ret = adv7180_init_controls(state);
if (ret)
goto err_unreg_subdev;
goto err_unregister_csi_client;
state->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
......@@ -951,7 +1078,9 @@ static int adv7180_probe(struct i2c_client *client,
media_entity_cleanup(&sd->entity);
err_free_ctrl:
adv7180_exit_controls(state);
err_unreg_subdev:
err_unregister_csi_client:
if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
i2c_unregister_device(state->csi_client);
mutex_destroy(&state->mutex);
return ret;
}
......@@ -968,7 +1097,12 @@ static int adv7180_remove(struct i2c_client *client)
media_entity_cleanup(&sd->entity);
adv7180_exit_controls(state);
if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
i2c_unregister_device(state->csi_client);
mutex_destroy(&state->mutex);
return 0;
}
......@@ -976,8 +1110,12 @@ static const struct i2c_device_id adv7180_id[] = {
{ "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)&adv7281_info },
{ "adv7282-m", (kernel_ulong_t)&adv7281_m_info },
{},
};
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