Commit 2deaf1c2 authored by Jani Nikula's avatar Jani Nikula
Browse files

drm/edid: abstract an EDID block read helper



We have an abstraction for the EDID base block read, yet duplicating the
retries and error handling for extension block reads. Introduce a more
generic EDID block read helper.

Switch to the helper piecemeal, starting with drm_edid_get_panel_id(),
which doesn't need or have access to the connector anyway.

The subtle change is switching from drm_edid_block_valid() to
edid_block_check(). We also status print once, not for every attempt.

Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
Reviewed-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/47560f7530e4a7b32b56cb9038178244fe30a4af.1649685475.git.jani.nikula@intel.com
parent 89fb7536
Loading
Loading
Loading
Loading
+51 −7
Original line number Diff line number Diff line
@@ -1670,6 +1670,7 @@ EXPORT_SYMBOL(drm_edid_are_equal);

enum edid_block_status {
	EDID_BLOCK_OK = 0,
	EDID_BLOCK_READ_FAIL,
	EDID_BLOCK_NULL,
	EDID_BLOCK_ZERO,
	EDID_BLOCK_HEADER_CORRUPT,
@@ -1736,6 +1737,9 @@ static void edid_block_status_print(enum edid_block_status status,
	switch (status) {
	case EDID_BLOCK_OK:
		break;
	case EDID_BLOCK_READ_FAIL:
		pr_debug("EDID block %d read failed\n", block_num);
		break;
	case EDID_BLOCK_NULL:
		pr_debug("EDID block %d pointer is NULL\n", block_num);
		break;
@@ -2039,6 +2043,39 @@ EXPORT_SYMBOL(drm_add_override_edid_modes);

typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len);

static enum edid_block_status edid_block_read(void *block, unsigned int block_num,
					      read_block_fn read_block,
					      void *context)
{
	enum edid_block_status status;
	bool is_base_block = block_num == 0;
	int try;

	for (try = 0; try < 4; try++) {
		if (read_block(context, block, block_num, EDID_LENGTH))
			return EDID_BLOCK_READ_FAIL;

		status = edid_block_check(block, is_base_block);
		if (status == EDID_BLOCK_HEADER_REPAIR) {
			edid_header_fix(block);

			/* Retry with fixed header, update status if that worked. */
			status = edid_block_check(block, is_base_block);
			if (status == EDID_BLOCK_OK)
				status = EDID_BLOCK_HEADER_FIXED;
		}

		if (edid_block_status_valid(status, edid_block_tag(block)))
			break;

		/* Fail early for unrepairable base block all zeros. */
		if (try == 0 && is_base_block && status == EDID_BLOCK_ZERO)
			break;
	}

	return status;
}

static struct edid *drm_do_get_edid_base_block(struct drm_connector *connector,
					       read_block_fn read_block,
					       void *context)
@@ -2237,20 +2274,27 @@ static u32 edid_extract_panel_id(const struct edid *edid)

u32 drm_edid_get_panel_id(struct i2c_adapter *adapter)
{
	const struct edid *edid;
	u32 panel_id;

	edid = drm_do_get_edid_base_block(NULL, drm_do_probe_ddc_edid, adapter);
	enum edid_block_status status;
	void *base_block;
	u32 panel_id = 0;

	/*
	 * There are no manufacturer IDs of 0, so if there is a problem reading
	 * the EDID then we'll just return 0.
	 */
	if (!edid)

	base_block = kmalloc(EDID_LENGTH, GFP_KERNEL);
	if (!base_block)
		return 0;

	panel_id = edid_extract_panel_id(edid);
	kfree(edid);
	status = edid_block_read(base_block, 0, drm_do_probe_ddc_edid, adapter);

	edid_block_status_print(status, base_block, 0);

	if (edid_block_status_valid(status, edid_block_tag(base_block)))
		panel_id = edid_extract_panel_id(base_block);

	kfree(base_block);

	return panel_id;
}