Skip to content

Commit

Permalink
media: adv7842: support 1 block EDIDs, fix clearing EDID
Browse files Browse the repository at this point in the history
Add support for EDIDs consisting of one EDID block.

Related to this, improve CEC physical address handling.

Clearing the EDID caused a bug since v4l2_calc_aspect_ratio() was
called with a NULL pointer.

Signed-off-by: Hans Verkuil <[email protected]>
Signed-off-by: Mauro Carvalho Chehab <[email protected]>
  • Loading branch information
Hans Verkuil authored and mchehab committed Apr 9, 2021
1 parent e0a4205 commit 3e057b8
Showing 1 changed file with 43 additions and 28 deletions.
71 changes: 43 additions & 28 deletions drivers/media/i2c/adv7842.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,12 @@ struct adv7842_state {
v4l2_std_id norm;
struct {
u8 edid[256];
u32 blocks;
u32 present;
} hdmi_edid;
struct {
u8 edid[256];
u32 blocks;
u32 present;
} vga_edid;
struct v4l2_fract aspect_ratio;
Expand Down Expand Up @@ -711,7 +713,8 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct adv7842_state *state = to_state(sd);
const u8 *val = state->vga_edid.edid;
const u8 *edid = state->vga_edid.edid;
u32 blocks = state->vga_edid.blocks;
int err = 0;
int i;

Expand All @@ -726,10 +729,10 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
/* edid segment pointer '1' for VGA port */
rep_write_and_or(sd, 0x77, 0xef, 0x10);

for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX)
for (i = 0; !err && i < blocks * 128; i += I2C_SMBUS_BLOCK_MAX)
err = i2c_smbus_write_i2c_block_data(state->i2c_edid, i,
I2C_SMBUS_BLOCK_MAX,
val + i);
edid + i);
if (err)
return err;

Expand Down Expand Up @@ -759,8 +762,9 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct adv7842_state *state = to_state(sd);
const u8 *edid = state->hdmi_edid.edid;
u32 blocks = state->hdmi_edid.blocks;
int spa_loc;
u16 pa;
u16 pa, parent_pa;
int err = 0;
int i;

Expand All @@ -778,33 +782,35 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
return 0;
}

pa = v4l2_get_edid_phys_addr(edid, 256, &spa_loc);
err = v4l2_phys_addr_validate(pa, &pa, NULL);
pa = v4l2_get_edid_phys_addr(edid, blocks * 128, &spa_loc);
err = v4l2_phys_addr_validate(pa, &parent_pa, NULL);
if (err)
return err;

/*
* Return an error if no location of the source physical address
* was found.
*/
if (spa_loc == 0)
return -EINVAL;
if (!spa_loc) {
/*
* There is no SPA, so just set spa_loc to 128 and pa to whatever
* data is there.
*/
spa_loc = 128;
pa = (edid[spa_loc] << 8) | edid[spa_loc + 1];
}

/* edid segment pointer '0' for HDMI ports */
rep_write_and_or(sd, 0x77, 0xef, 0x00);

for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX)
for (i = 0; !err && i < blocks * 128; i += I2C_SMBUS_BLOCK_MAX)
err = i2c_smbus_write_i2c_block_data(state->i2c_edid, i,
I2C_SMBUS_BLOCK_MAX, edid + i);
if (err)
return err;

if (port == ADV7842_EDID_PORT_A) {
rep_write(sd, 0x72, edid[spa_loc]);
rep_write(sd, 0x73, edid[spa_loc + 1]);
rep_write(sd, 0x72, pa >> 8);
rep_write(sd, 0x73, pa & 0xff);
} else {
rep_write(sd, 0x74, edid[spa_loc]);
rep_write(sd, 0x75, edid[spa_loc + 1]);
rep_write(sd, 0x74, pa >> 8);
rep_write(sd, 0x75, pa & 0xff);
}
rep_write(sd, 0x76, spa_loc & 0xff);
rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40);
Expand All @@ -824,7 +830,7 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
(port == ADV7842_EDID_PORT_A) ? 'A' : 'B');
return -EIO;
}
cec_s_phys_addr(state->cec_adap, pa, false);
cec_s_phys_addr(state->cec_adap, parent_pa, false);

/* enable hotplug after 200 ms */
schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
Expand Down Expand Up @@ -2443,37 +2449,42 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
{
struct adv7842_state *state = to_state(sd);
u32 blocks = 0;
u8 *data = NULL;

memset(edid->reserved, 0, sizeof(edid->reserved));

switch (edid->pad) {
case ADV7842_EDID_PORT_A:
case ADV7842_EDID_PORT_B:
if (state->hdmi_edid.present & (0x04 << edid->pad))
if (state->hdmi_edid.present & (0x04 << edid->pad)) {
data = state->hdmi_edid.edid;
blocks = state->hdmi_edid.blocks;
}
break;
case ADV7842_EDID_PORT_VGA:
if (state->vga_edid.present)
if (state->vga_edid.present) {
data = state->vga_edid.edid;
blocks = state->vga_edid.blocks;
}
break;
default:
return -EINVAL;
}

if (edid->start_block == 0 && edid->blocks == 0) {
edid->blocks = data ? 2 : 0;
edid->blocks = blocks;
return 0;
}

if (!data)
return -ENODATA;

if (edid->start_block >= 2)
if (edid->start_block >= blocks)
return -EINVAL;

if (edid->start_block + edid->blocks > 2)
edid->blocks = 2 - edid->start_block;
if (edid->start_block + edid->blocks > blocks)
edid->blocks = blocks - edid->start_block;

memcpy(edid->edid, data + edid->start_block * 128, edid->blocks * 128);

Expand All @@ -2497,26 +2508,30 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e)
}

/* todo, per edid */
state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15],
e->edid[0x16]);
if (e->blocks)
state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15],
e->edid[0x16]);

switch (e->pad) {
case ADV7842_EDID_PORT_VGA:
memset(&state->vga_edid.edid, 0, 256);
state->vga_edid.blocks = e->blocks;
state->vga_edid.present = e->blocks ? 0x1 : 0x0;
memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks);
if (e->blocks)
memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks);
err = edid_write_vga_segment(sd);
break;
case ADV7842_EDID_PORT_A:
case ADV7842_EDID_PORT_B:
memset(&state->hdmi_edid.edid, 0, 256);
state->hdmi_edid.blocks = e->blocks;
if (e->blocks) {
state->hdmi_edid.present |= 0x04 << e->pad;
memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);
} else {
state->hdmi_edid.present &= ~(0x04 << e->pad);
adv7842_s_detect_tx_5v_ctrl(sd);
}
memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks);
err = edid_write_hdmi_segment(sd, e->pad);
break;
default:
Expand Down

0 comments on commit 3e057b8

Please sign in to comment.