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

Jani Nikula jani.nikula at intel.com
Thu Mar 31 15:54:34 UTC 2022


On Thu, 31 Mar 2022, Ville Syrjälä <ville.syrjala at linux.intel.com> wrote:
> 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?

Okay.

>
>> +{
>> +	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?

Indeed, thanks!

> 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.

Yes.

BR,
Jani.

>
>> +			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

-- 
Jani Nikula, Intel Open Source Graphics Center


More information about the dri-devel mailing list