diff options
-rw-r--r-- | drivers/media/i2c/adv7604.c | 89 |
1 files changed, 61 insertions, 28 deletions
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 68b2b7a10d5a..10418574a89c 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -73,6 +73,8 @@ MODULE_LICENSE("GPL"); #define ADV76XX_MAX_ADDRS (3) +#define ADV76XX_MAX_EDID_BLOCKS 4 + enum adv76xx_type { ADV7604, ADV7611, @@ -108,6 +110,11 @@ struct adv76xx_chip_info { unsigned int edid_enable_reg; unsigned int edid_status_reg; + unsigned int edid_segment_reg; + unsigned int edid_segment_mask; + unsigned int edid_spa_loc_reg; + unsigned int edid_spa_loc_msb_mask; + unsigned int edid_spa_port_b_reg; unsigned int lcf_reg; unsigned int cable_det_mask; @@ -176,7 +183,7 @@ struct adv76xx_state { const struct adv76xx_format_info *format; struct { - u8 edid[256]; + u8 edid[ADV76XX_MAX_EDID_BLOCKS * 128]; u32 present; unsigned blocks; } edid; @@ -2334,15 +2341,25 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) __func__, edid->pad, state->edid.present); return 0; } - if (edid->blocks > 2) { - edid->blocks = 2; + if (edid->blocks > ADV76XX_MAX_EDID_BLOCKS) { + edid->blocks = ADV76XX_MAX_EDID_BLOCKS; return -E2BIG; } + pa = v4l2_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); err = v4l2_phys_addr_validate(pa, &parent_pa, NULL); if (err) return err; + 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->edid[spa_loc] << 8) | edid->edid[spa_loc + 1]; + } + v4l2_dbg(2, debug, sd, "%s: write EDID pad %d, edid.present = 0x%x\n", __func__, edid->pad, state->edid.present); @@ -2351,59 +2368,63 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) adv76xx_set_hpd(state, 0); rep_write_clr_set(sd, info->edid_enable_reg, 0x0f, 0x00); - /* - * Return an error if no location of the source physical address - * was found. - */ - if (edid->blocks > 1 && spa_loc == 0) - return -EINVAL; - switch (edid->pad) { case ADV76XX_PAD_HDMI_PORT_A: state->spa_port_a[0] = pa >> 8; state->spa_port_a[1] = pa & 0xff; break; case ADV7604_PAD_HDMI_PORT_B: - rep_write(sd, 0x70, pa >> 8); - rep_write(sd, 0x71, pa & 0xff); + rep_write(sd, info->edid_spa_port_b_reg, pa >> 8); + rep_write(sd, info->edid_spa_port_b_reg + 1, pa & 0xff); break; case ADV7604_PAD_HDMI_PORT_C: - rep_write(sd, 0x72, pa >> 8); - rep_write(sd, 0x73, pa & 0xff); + rep_write(sd, info->edid_spa_port_b_reg + 2, pa >> 8); + rep_write(sd, info->edid_spa_port_b_reg + 3, pa & 0xff); break; case ADV7604_PAD_HDMI_PORT_D: - rep_write(sd, 0x74, pa >> 8); - rep_write(sd, 0x75, pa & 0xff); + rep_write(sd, info->edid_spa_port_b_reg + 4, pa >> 8); + rep_write(sd, info->edid_spa_port_b_reg + 5, pa & 0xff); break; default: return -EINVAL; } - if (info->type == ADV7604) { - rep_write(sd, 0x76, spa_loc & 0xff); - rep_write_clr_set(sd, 0x77, 0x40, (spa_loc & 0x100) >> 2); - } else { - /* ADV7612 Software Manual Rev. A, p. 15 */ - rep_write(sd, 0x70, spa_loc & 0xff); - rep_write_clr_set(sd, 0x71, 0x01, (spa_loc & 0x100) >> 8); - } + if (info->edid_spa_loc_reg) { + u8 mask = info->edid_spa_loc_msb_mask; - if (spa_loc) { - edid->edid[spa_loc] = state->spa_port_a[0]; - edid->edid[spa_loc + 1] = state->spa_port_a[1]; + rep_write(sd, info->edid_spa_loc_reg, spa_loc & 0xff); + rep_write_clr_set(sd, info->edid_spa_loc_reg + 1, + mask, (spa_loc & 0x100) ? mask : 0); } + edid->edid[spa_loc] = state->spa_port_a[0]; + edid->edid[spa_loc + 1] = state->spa_port_a[1]; + memcpy(state->edid.edid, edid->edid, 128 * edid->blocks); state->edid.blocks = edid->blocks; state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15], edid->edid[0x16]); state->edid.present |= 1 << edid->pad; - err = edid_write_block(sd, 128 * edid->blocks, state->edid.edid); + rep_write_clr_set(sd, info->edid_segment_reg, + info->edid_segment_mask, 0); + err = edid_write_block(sd, 128 * min(edid->blocks, 2U), state->edid.edid); if (err < 0) { v4l2_err(sd, "error %d writing edid pad %d\n", err, edid->pad); return err; } + if (edid->blocks > 2) { + rep_write_clr_set(sd, info->edid_segment_reg, + info->edid_segment_mask, + info->edid_segment_mask); + err = edid_write_block(sd, 128 * (edid->blocks - 2), + state->edid.edid + 256); + if (err < 0) { + v4l2_err(sd, "error %d writing edid pad %d\n", + err, edid->pad); + return err; + } + } /* adv76xx calculates the checksums and enables I2C access to internal EDID RAM from DDC port. */ @@ -3008,6 +3029,11 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .num_dv_ports = 4, .edid_enable_reg = 0x77, .edid_status_reg = 0x7d, + .edid_segment_reg = 0x77, + .edid_segment_mask = 0x10, + .edid_spa_loc_reg = 0x76, + .edid_spa_loc_msb_mask = 0x40, + .edid_spa_port_b_reg = 0x70, .lcf_reg = 0xb3, .tdms_lock_mask = 0xe0, .cable_det_mask = 0x1e, @@ -3058,6 +3084,8 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .num_dv_ports = 1, .edid_enable_reg = 0x74, .edid_status_reg = 0x76, + .edid_segment_reg = 0x7a, + .edid_segment_mask = 0x01, .lcf_reg = 0xa3, .tdms_lock_mask = 0x43, .cable_det_mask = 0x01, @@ -3102,6 +3130,11 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .num_dv_ports = 1, /* normally 2 */ .edid_enable_reg = 0x74, .edid_status_reg = 0x76, + .edid_segment_reg = 0x7a, + .edid_segment_mask = 0x01, + .edid_spa_loc_reg = 0x70, + .edid_spa_loc_msb_mask = 0x01, + .edid_spa_port_b_reg = 0x52, .lcf_reg = 0xa3, .tdms_lock_mask = 0x43, .cable_det_mask = 0x01, |