Commit 794aca0e authored by Jani Nikula's avatar Jani Nikula

drm/edid: use struct drm_edid for override/firmware EDID

There's a lot going on here, but the main thing is switching the
firmware EDID loader to use struct drm_edid. Unfortunately, it's
difficult to reasonably split to smaller pieces.

Convert the EDID loader to struct drm_edid. There's a functional change
in validation; it no longer tries to fix errors or filter invalid
blocks. It's stricter in this sense. Hopefully this will not be an
issue.

As a by-product, this change also allows HF-EEODB extended EDIDs to be
passed via override/firmware EDID.
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/e64267c28eca483e83c802bc06ddd149bdcdfc66.1666614699.git.jani.nikula@intel.com
parent a05992d5
...@@ -2202,25 +2202,20 @@ static void connector_bad_edid(struct drm_connector *connector, ...@@ -2202,25 +2202,20 @@ static void connector_bad_edid(struct drm_connector *connector,
} }
/* Get override or firmware EDID */ /* Get override or firmware EDID */
static struct edid *drm_get_override_edid(struct drm_connector *connector, static const struct drm_edid *drm_edid_override_get(struct drm_connector *connector)
size_t *alloc_size)
{ {
struct edid *override = NULL; const struct drm_edid *override = NULL;
mutex_lock(&connector->edid_override_mutex); mutex_lock(&connector->edid_override_mutex);
if (connector->edid_override) if (connector->edid_override)
override = drm_edid_duplicate(connector->edid_override->edid); override = drm_edid_dup(connector->edid_override);
mutex_unlock(&connector->edid_override_mutex); mutex_unlock(&connector->edid_override_mutex);
if (!override) if (!override)
override = drm_edid_load_firmware(connector); override = drm_edid_load_firmware(connector);
/* FIXME: Get alloc size from deeper down the stack */
if (!IS_ERR_OR_NULL(override) && alloc_size)
*alloc_size = edid_size(override);
return IS_ERR(override) ? NULL : override; return IS_ERR(override) ? NULL : override;
} }
...@@ -2296,14 +2291,14 @@ int drm_edid_override_reset(struct drm_connector *connector) ...@@ -2296,14 +2291,14 @@ int drm_edid_override_reset(struct drm_connector *connector)
*/ */
int drm_edid_override_connector_update(struct drm_connector *connector) int drm_edid_override_connector_update(struct drm_connector *connector)
{ {
struct edid *override; const struct drm_edid *override;
int num_modes = 0; int num_modes = 0;
override = drm_get_override_edid(connector, NULL); override = drm_edid_override_get(connector);
if (override) { if (override) {
drm_connector_update_edid_property(connector, override); num_modes = drm_edid_connector_update(connector, override);
num_modes = drm_add_edid_modes(connector, override);
kfree(override); drm_edid_free(override);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n", DRM_DEBUG_KMS("[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n",
connector->base.id, connector->name, num_modes); connector->base.id, connector->name, num_modes);
...@@ -2354,12 +2349,19 @@ static struct edid *_drm_do_get_edid(struct drm_connector *connector, ...@@ -2354,12 +2349,19 @@ static struct edid *_drm_do_get_edid(struct drm_connector *connector,
{ {
enum edid_block_status status; enum edid_block_status status;
int i, num_blocks, invalid_blocks = 0; int i, num_blocks, invalid_blocks = 0;
const struct drm_edid *override;
struct edid *edid, *new; struct edid *edid, *new;
size_t alloc_size = EDID_LENGTH; size_t alloc_size = EDID_LENGTH;
edid = drm_get_override_edid(connector, &alloc_size); override = drm_edid_override_get(connector);
if (edid) if (override) {
alloc_size = override->size;
edid = kmemdup(override->edid, alloc_size, GFP_KERNEL);
drm_edid_free(override);
if (!edid)
return NULL;
goto ok; goto ok;
}
edid = kmalloc(alloc_size, GFP_KERNEL); edid = kmalloc(alloc_size, GFP_KERNEL);
if (!edid) if (!edid)
......
...@@ -159,22 +159,12 @@ static const u8 generic_edid[GENERIC_EDIDS][128] = { ...@@ -159,22 +159,12 @@ static const u8 generic_edid[GENERIC_EDIDS][128] = {
}, },
}; };
static int edid_size(const u8 *edid, int data_size) static const struct drm_edid *edid_load(struct drm_connector *connector, const char *name)
{
if (data_size < EDID_LENGTH)
return 0;
return (edid[0x7e] + 1) * EDID_LENGTH;
}
static void *edid_load(struct drm_connector *connector, const char *name)
{ {
const struct firmware *fw = NULL; const struct firmware *fw = NULL;
const u8 *fwdata; const u8 *fwdata;
u8 *edid; const struct drm_edid *drm_edid;
int fwsize, builtin; int fwsize, builtin;
int i, valid_extensions = 0;
bool print_bad_edid = !connector->bad_edid_counter || drm_debug_enabled(DRM_UT_KMS);
builtin = match_string(generic_edid_name, GENERIC_EDIDS, name); builtin = match_string(generic_edid_name, GENERIC_EDIDS, name);
if (builtin >= 0) { if (builtin >= 0) {
...@@ -203,69 +193,26 @@ static void *edid_load(struct drm_connector *connector, const char *name) ...@@ -203,69 +193,26 @@ static void *edid_load(struct drm_connector *connector, const char *name)
fwsize = fw->size; fwsize = fw->size;
} }
if (edid_size(fwdata, fwsize) != fwsize) { drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded %s firmware EDID \"%s\"\n",
DRM_ERROR("Size of EDID firmware \"%s\" is invalid " connector->base.id, connector->name,
"(expected %d, got %d\n", name, builtin >= 0 ? "built-in" : "external", name);
edid_size(fwdata, fwsize), (int)fwsize);
edid = ERR_PTR(-EINVAL);
goto out;
}
edid = kmemdup(fwdata, fwsize, GFP_KERNEL);
if (edid == NULL) {
edid = ERR_PTR(-ENOMEM);
goto out;
}
if (!drm_edid_block_valid(edid, 0, print_bad_edid,
&connector->edid_corrupt)) {
connector->bad_edid_counter++;
DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
name);
kfree(edid);
edid = ERR_PTR(-EINVAL);
goto out;
}
for (i = 1; i <= edid[0x7e]; i++) {
if (i != valid_extensions + 1)
memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
edid + i * EDID_LENGTH, EDID_LENGTH);
if (drm_edid_block_valid(edid + i * EDID_LENGTH, i,
print_bad_edid,
NULL))
valid_extensions++;
}
if (valid_extensions != edid[0x7e]) {
u8 *new_edid;
edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions; drm_edid = drm_edid_alloc(fwdata, fwsize);
DRM_INFO("Found %d valid extensions instead of %d in EDID data " if (!drm_edid_valid(drm_edid)) {
"\"%s\" for connector \"%s\"\n", valid_extensions, drm_err(connector->dev, "Invalid firmware EDID \"%s\"\n", name);
edid[0x7e], name, connector->name); drm_edid_free(drm_edid);
edid[0x7e] = valid_extensions; drm_edid = ERR_PTR(-EINVAL);
new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
GFP_KERNEL);
if (new_edid)
edid = new_edid;
} }
DRM_INFO("Got %s EDID base block and %d extension%s from "
"\"%s\" for connector \"%s\"\n", (builtin >= 0) ? "built-in" :
"external", valid_extensions, valid_extensions == 1 ? "" : "s",
name, connector->name);
out:
release_firmware(fw); release_firmware(fw);
return edid;
return drm_edid;
} }
struct edid *drm_edid_load_firmware(struct drm_connector *connector) const struct drm_edid *drm_edid_load_firmware(struct drm_connector *connector)
{ {
char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL; char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
struct edid *edid; const struct drm_edid *drm_edid;
if (edid_firmware[0] == '\0') if (edid_firmware[0] == '\0')
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
...@@ -308,8 +255,9 @@ struct edid *drm_edid_load_firmware(struct drm_connector *connector) ...@@ -308,8 +255,9 @@ struct edid *drm_edid_load_firmware(struct drm_connector *connector)
if (*last == '\n') if (*last == '\n')
*last = '\0'; *last = '\0';
edid = edid_load(connector, edidname); drm_edid = edid_load(connector, edidname);
kfree(fwstr); kfree(fwstr);
return edid; return drm_edid;
} }
...@@ -388,11 +388,11 @@ int drm_av_sync_delay(struct drm_connector *connector, ...@@ -388,11 +388,11 @@ int drm_av_sync_delay(struct drm_connector *connector,
const struct drm_display_mode *mode); const struct drm_display_mode *mode);
#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
struct edid *drm_edid_load_firmware(struct drm_connector *connector); const struct drm_edid *drm_edid_load_firmware(struct drm_connector *connector);
int __drm_set_edid_firmware_path(const char *path); int __drm_set_edid_firmware_path(const char *path);
int __drm_get_edid_firmware_path(char *buf, size_t bufsize); int __drm_get_edid_firmware_path(char *buf, size_t bufsize);
#else #else
static inline struct edid * static inline const struct drm_edid *
drm_edid_load_firmware(struct drm_connector *connector) drm_edid_load_firmware(struct drm_connector *connector)
{ {
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment