[PATCH 07/12] drm/edid: split drm_edid_block_valid() to check and act parts

Ville Syrjälä ville.syrjala at linux.intel.com
Thu Mar 31 14:54:27 UTC 2022


On Tue, Mar 29, 2022 at 09:42:14PM +0300, Jani Nikula wrote:
> Add edid_block_check() that only checks the EDID block validity, without
> any actions. Turns out it's simple and crystal clear.
> 
> Rewrite drm_edid_block_valid() around it, keeping all the functionality
> fairly closely the same, warts and all. Turns out it's incredibly
> complicated for a function you'd expect to be simple, with all the
> fixing and printing and special casing. (Maybe we'll want to simplify it
> in the future.)
> 
> Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
> Signed-off-by: Jani Nikula <jani.nikula at intel.com>
> ---
>  drivers/gpu/drm/drm_edid.c | 150 ++++++++++++++++++++++---------------
>  1 file changed, 88 insertions(+), 62 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 481643751d10..04eb6949c9c8 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -1668,10 +1668,55 @@ bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2)
>  }
>  EXPORT_SYMBOL(drm_edid_are_equal);
>  
> +enum edid_block_status {
> +	EDID_BLOCK_OK = 0,
> +	EDID_BLOCK_NULL,
> +	EDID_BLOCK_HEADER_CORRUPT,
> +	EDID_BLOCK_HEADER_REPAIR,
> +	EDID_BLOCK_HEADER_FIXED,
> +	EDID_BLOCK_CHECKSUM,
> +	EDID_BLOCK_VERSION,
> +};
> +
> +static enum edid_block_status edid_block_check(const void *_block, bool base)

s/base/is_base_block/ or something?

> +{
> +	const struct edid *block = _block;
> +
> +	if (!block)
> +		return EDID_BLOCK_NULL;
> +
> +	if (base) {
> +		int score = drm_edid_header_is_valid(block);
> +
> +		if (score < clamp(edid_fixup, 6, 8))

That should clamp to 0-8?

Might be nicer to just define a .set() op for the modparam
and check it there, but that's clearly material for a separate patch.

> +			return EDID_BLOCK_HEADER_CORRUPT;
> +
> +		if (score < 8)
> +			return EDID_BLOCK_HEADER_REPAIR;
> +	}
> +
> +	if (edid_block_compute_checksum(block) != edid_block_get_checksum(block))
> +		return EDID_BLOCK_CHECKSUM;
> +
> +	if (base) {
> +		if (block->version != 1)
> +			return EDID_BLOCK_VERSION;
> +	}
> +
> +	return EDID_BLOCK_OK;
> +}
> +
> +static bool edid_block_status_valid(enum edid_block_status status, int tag)
> +{
> +	return status == EDID_BLOCK_OK ||
> +		status == EDID_BLOCK_HEADER_FIXED ||
> +		(status == EDID_BLOCK_CHECKSUM && tag == CEA_EXT);
> +}
> +
>  /**
>   * drm_edid_block_valid - Sanity check the EDID block (base or extension)
>   * @raw_edid: pointer to raw EDID block
> - * @block: type of block to validate (0 for base, extension otherwise)
> + * @block_num: type of block to validate (0 for base, extension otherwise)
>   * @print_bad_edid: if true, dump bad EDID blocks to the console
>   * @edid_corrupt: if true, the header or checksum is invalid
>   *
> @@ -1680,88 +1725,69 @@ EXPORT_SYMBOL(drm_edid_are_equal);
>   *
>   * Return: True if the block is valid, false otherwise.
>   */
> -bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid,
> +bool drm_edid_block_valid(u8 *_block, int block_num, bool print_bad_edid,
>  			  bool *edid_corrupt)
>  {
> -	u8 csum;
> -	struct edid *edid = (struct edid *)raw_edid;
> +	struct edid *block = (struct edid *)_block;
> +	enum edid_block_status status;
> +	bool base = block_num == 0;
> +	bool valid;
>  
> -	if (WARN_ON(!raw_edid))
> +	if (WARN_ON(!block))
>  		return false;
>  
> -	if (edid_fixup > 8 || edid_fixup < 0)
> -		edid_fixup = 6;
> -
> -	if (block == 0) {
> -		int score = drm_edid_header_is_valid(raw_edid);
> +	status = edid_block_check(block, base);
> +	if (status == EDID_BLOCK_HEADER_REPAIR) {
> +		DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
> +		edid_header_fix(block);
>  
> -		if (score == 8) {
> -			if (edid_corrupt)
> -				*edid_corrupt = false;
> -		} else if (score >= edid_fixup) {
> -			/* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6
> -			 * The corrupt flag needs to be set here otherwise, the
> -			 * fix-up code here will correct the problem, the
> -			 * checksum is correct and the test fails
> -			 */
> -			if (edid_corrupt)
> -				*edid_corrupt = true;
> -			DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
> -			edid_header_fix(raw_edid);
> -		} else {
> -			if (edid_corrupt)
> -				*edid_corrupt = true;
> -			goto bad;
> -		}
> +		/* Retry with fixed header, update status if that worked. */
> +		status = edid_block_check(block, base);
> +		if (status == EDID_BLOCK_OK)
> +			status = EDID_BLOCK_HEADER_FIXED;
>  	}
>  
> -	csum = edid_block_compute_checksum(raw_edid);
> -	if (csum != edid_block_get_checksum(raw_edid)) {
> -		if (edid_corrupt)
> +	if (edid_corrupt) {
> +		/*
> +		 * Unknown major version isn't corrupt but we can't use it. Only
> +		 * the base block can reset edid_corrupt to false.
> +		 */
> +		if (base && (status == EDID_BLOCK_OK || status == EDID_BLOCK_VERSION))
> +			*edid_corrupt = false;
> +		else if (status != EDID_BLOCK_OK)
>  			*edid_corrupt = true;
> -
> -		/* allow CEA to slide through, switches mangle this */
> -		if (edid_block_tag(raw_edid) == CEA_EXT) {
> -			DRM_DEBUG("EDID checksum is invalid, remainder is %d\n", csum);
> -			DRM_DEBUG("Assuming a KVM switch modified the CEA block but left the original checksum\n");
> -		} else {
> -			if (print_bad_edid)
> -				DRM_NOTE("EDID checksum is invalid, remainder is %d\n", csum);
> -
> -			goto bad;
> -		}
>  	}
>  
> -	/* per-block-type checks */
> -	switch (edid_block_tag(raw_edid)) {
> -	case 0: /* base */
> -		if (edid->version != 1) {
> -			DRM_NOTE("EDID has major version %d, instead of 1\n", edid->version);
> -			goto bad;
> +	/* Determine whether we can use this block with this status. */
> +	valid = edid_block_status_valid(status, edid_block_tag(block));
> +
> +	/* Some fairly random status printouts. */
> +	if (status == EDID_BLOCK_CHECKSUM) {
> +		if (valid) {
> +			DRM_DEBUG("EDID block checksum is invalid, remainder is %d\n",
> +				  edid_block_compute_checksum(block));
> +			DRM_DEBUG("Assuming a KVM switch modified the block but left the original checksum\n");
> +		} else if (print_bad_edid) {
> +			DRM_NOTE("EDID block checksum is invalid, remainder is %d\n",
> +				 edid_block_compute_checksum(block));
>  		}
> -
> -		if (edid->revision > 4)
> -			DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");

This debug message seems to disappear. Intentional?

> -		break;
> -
> -	default:
> -		break;
> +	} else if (status == EDID_BLOCK_VERSION) {
> +		DRM_NOTE("EDID has major version %d, instead of 1\n",
> +			 block->version);
>  	}
>  
> -	return true;
> -
> -bad:
> -	if (print_bad_edid) {
> -		if (edid_is_zero(raw_edid, EDID_LENGTH)) {
> +	if (!valid && print_bad_edid) {
> +		if (edid_is_zero(block, EDID_LENGTH)) {
>  			pr_notice("EDID block is all zeroes\n");
>  		} else {
>  			pr_notice("Raw EDID:\n");
>  			print_hex_dump(KERN_NOTICE,
>  				       " \t", DUMP_PREFIX_NONE, 16, 1,
> -				       raw_edid, EDID_LENGTH, false);
> +				       block, EDID_LENGTH, false);
>  		}
>  	}
> -	return false;
> +
> +	return valid;
>  }
>  EXPORT_SYMBOL(drm_edid_block_valid);
>  
> -- 
> 2.30.2

-- 
Ville Syrjälä
Intel


More information about the dri-devel mailing list