|
@@ -124,6 +124,11 @@
|
|
#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_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_AIN1 0x00
|
|
#define ADV7180_INPUT_CVBS_AIN2 0x01
|
|
#define ADV7180_INPUT_CVBS_AIN2 0x01
|
|
#define ADV7180_INPUT_CVBS_AIN3 0x02
|
|
#define ADV7180_INPUT_CVBS_AIN3 0x02
|
|
@@ -155,10 +160,13 @@
|
|
#define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
|
|
#define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
|
|
#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
|
|
#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
|
|
|
|
|
|
|
|
+#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
|
|
|
|
+
|
|
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)
|
|
|
|
|
|
struct adv7180_chip_info {
|
|
struct adv7180_chip_info {
|
|
unsigned int flags;
|
|
unsigned int flags;
|
|
@@ -181,6 +189,7 @@ struct adv7180_state {
|
|
|
|
|
|
struct i2c_client *client;
|
|
struct i2c_client *client;
|
|
unsigned int register_page;
|
|
unsigned int register_page;
|
|
|
|
+ struct i2c_client *csi_client;
|
|
const struct adv7180_chip_info *chip_info;
|
|
const struct adv7180_chip_info *chip_info;
|
|
};
|
|
};
|
|
#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \
|
|
#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);
|
|
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,
|
|
static int adv7180_set_video_standard(struct adv7180_state *state,
|
|
unsigned int std)
|
|
unsigned int std)
|
|
{
|
|
{
|
|
@@ -405,13 +420,31 @@ out:
|
|
static int adv7180_set_power(struct adv7180_state *state, bool on)
|
|
static int adv7180_set_power(struct adv7180_state *state, bool on)
|
|
{
|
|
{
|
|
u8 val;
|
|
u8 val;
|
|
|
|
+ int ret;
|
|
|
|
|
|
if (on)
|
|
if (on)
|
|
val = ADV7180_PWR_MAN_ON;
|
|
val = ADV7180_PWR_MAN_ON;
|
|
else
|
|
else
|
|
val = ADV7180_PWR_MAN_OFF;
|
|
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)
|
|
static int adv7180_s_power(struct v4l2_subdev *sd, int on)
|
|
@@ -549,13 +582,22 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd,
|
|
static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
|
|
static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
|
|
struct v4l2_mbus_config *cfg)
|
|
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;
|
|
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)
|
|
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) {
|
|
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);
|
|
adv7180_write(state, 0x0081, 0x51);
|
|
adv7180_write(state, 0x0081, 0x51);
|
|
adv7180_write(state, 0x0082, 0x68);
|
|
adv7180_write(state, 0x0082, 0x68);
|
|
- adv7180_write(state, 0x0004, 0x17);
|
|
|
|
- } else {
|
|
|
|
- adv7180_write(state, 0x0004, 0x07);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* ADI required writes */
|
|
/* ADI required writes */
|
|
- adv7180_write(state, 0x0003, 0x0c);
|
|
|
|
|
|
+ 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);
|
|
adv7180_write(state, 0x0013, 0x00);
|
|
- adv7180_write(state, 0x001d, 0x40);
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -817,15 +871,81 @@ static const struct adv7180_chip_info adv7280_info = {
|
|
.select_input = adv7182_select_input,
|
|
.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 = {
|
|
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) |
|
|
.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_AIN4) |
|
|
|
|
+ BIT(ADV7182_INPUT_CVBS_AIN5) |
|
|
|
|
+ BIT(ADV7182_INPUT_CVBS_AIN6) |
|
|
BIT(ADV7182_INPUT_CVBS_AIN7) |
|
|
BIT(ADV7182_INPUT_CVBS_AIN7) |
|
|
BIT(ADV7182_INPUT_CVBS_AIN8) |
|
|
BIT(ADV7182_INPUT_CVBS_AIN8) |
|
|
BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
|
|
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_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_AIN1_AIN2) |
|
|
|
|
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
|
|
|
|
+ BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
|
|
BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
|
|
BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
|
|
.init = adv7182_init,
|
|
.init = adv7182_init,
|
|
.set_std = adv7182_set_std,
|
|
.set_std = adv7182_set_std,
|
|
@@ -904,6 +1024,13 @@ static int adv7180_probe(struct i2c_client *client,
|
|
state->client = client;
|
|
state->client = client;
|
|
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) {
|
|
|
|
+ state->csi_client = i2c_new_dummy(client->adapter,
|
|
|
|
+ ADV7180_DEFAULT_CSI_I2C_ADDR);
|
|
|
|
+ if (!state->csi_client)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
state->irq = client->irq;
|
|
state->irq = client->irq;
|
|
mutex_init(&state->mutex);
|
|
mutex_init(&state->mutex);
|
|
state->autodetect = true;
|
|
state->autodetect = true;
|
|
@@ -918,7 +1045,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_unreg_subdev;
|
|
|
|
|
|
+ goto err_unregister_csi_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;
|
|
@@ -951,7 +1078,9 @@ err_media_entity_cleanup:
|
|
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_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);
|
|
mutex_destroy(&state->mutex);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -968,7 +1097,12 @@ 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_MIPI_CSI2)
|
|
|
|
+ i2c_unregister_device(state->csi_client);
|
|
|
|
+
|
|
mutex_destroy(&state->mutex);
|
|
mutex_destroy(&state->mutex);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -976,8 +1110,12 @@ static const struct i2c_device_id adv7180_id[] = {
|
|
{ "adv7180", (kernel_ulong_t)&adv7180_info },
|
|
{ "adv7180", (kernel_ulong_t)&adv7180_info },
|
|
{ "adv7182", (kernel_ulong_t)&adv7182_info },
|
|
{ "adv7182", (kernel_ulong_t)&adv7182_info },
|
|
{ "adv7280", (kernel_ulong_t)&adv7280_info },
|
|
{ "adv7280", (kernel_ulong_t)&adv7280_info },
|
|
|
|
+ { "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
|
|
{ "adv7281", (kernel_ulong_t)&adv7281_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", (kernel_ulong_t)&adv7281_info },
|
|
|
|
+ { "adv7282-m", (kernel_ulong_t)&adv7281_m_info },
|
|
{},
|
|
{},
|
|
};
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, adv7180_id);
|
|
MODULE_DEVICE_TABLE(i2c, adv7180_id);
|