linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv2] adv7604: support EDIDs up to 4 blocks
@ 2021-03-25 10:39 Hans Verkuil
  2021-03-26  0:37 ` Niklas Söderlund
  0 siblings, 1 reply; 2+ messages in thread
From: Hans Verkuil @ 2021-03-25 10:39 UTC (permalink / raw)
  To: Linux Media Mailing List; +Cc: Niklas Söderlund

While the adv7604/11/12 hardware supported EDIDs up to 4 blocks, the
driver didn't. This patch adds support for this. It also improves support
for EDIDs that do not have a Source Physical Address: in that case the
spa location is set to the first byte of the second block, and the
'physical address' is just the two bytes at that location. This is per
the suggestion in the adv76xx documentation for such EDIDs.

Tested with an adv7604 and adv7612.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
Change since v2' remove ADV7604 type test, was spurious code that should
have been removed. Thanks to Niklas for catching that!
---
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 18184297e2f0..7547afc85eb1 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;
@@ -2327,15 +2334,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);

@@ -2344,59 +2361,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. */
@@ -2989,6 +3010,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,
@@ -3039,6 +3065,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,
@@ -3083,6 +3111,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,

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCHv2] adv7604: support EDIDs up to 4 blocks
  2021-03-25 10:39 [PATCHv2] adv7604: support EDIDs up to 4 blocks Hans Verkuil
@ 2021-03-26  0:37 ` Niklas Söderlund
  0 siblings, 0 replies; 2+ messages in thread
From: Niklas Söderlund @ 2021-03-26  0:37 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Linux Media Mailing List

Hi Hans,

Thanks for your work.

On 2021-03-25 11:39:37 +0100, Hans Verkuil wrote:
> While the adv7604/11/12 hardware supported EDIDs up to 4 blocks, the
> driver didn't. This patch adds support for this. It also improves support
> for EDIDs that do not have a Source Physical Address: in that case the
> spa location is set to the first byte of the second block, and the
> 'physical address' is just the two bytes at that location. This is per
> the suggestion in the adv76xx documentation for such EDIDs.
> 
> Tested with an adv7604 and adv7612.
> 
> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>

This one works as expected,

Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

> ---
> Change since v2' remove ADV7604 type test, was spurious code that should
> have been removed. Thanks to Niklas for catching that!
> ---
> diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
> index 18184297e2f0..7547afc85eb1 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;
> @@ -2327,15 +2334,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);
> 
> @@ -2344,59 +2361,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. */
> @@ -2989,6 +3010,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,
> @@ -3039,6 +3065,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,
> @@ -3083,6 +3111,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,

-- 
Regards,
Niklas Söderlund

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-03-26  0:38 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-25 10:39 [PATCHv2] adv7604: support EDIDs up to 4 blocks Hans Verkuil
2021-03-26  0:37 ` Niklas Söderlund

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).