On Wed, 27 Jul 2022 09:41:52 +0200, Matthieu CHARETTE wrote: > > Loading an EDID using drm.edid_firmware parameter makes resume to fail > after firmware cache is being cleaned. This is because edid_load() use a > temporary device to request the firmware. This cause the EDID firmware > not to be cached from suspend. And, requesting the EDID firmware return > an error during resume. > So the request_firmware() call should use a permanent device for each > connector. Also, we should cache the EDID even if no monitor is > connected, in case it's plugged while suspended. > > Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2061 > Signed-off-by: Matthieu CHARETTE <matthieu.charette@xxxxxxxxx> Can we simply cache the already loaded EDID bytes instead? Something like below (totally untested). thanks, Takashi -- 8< -- diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 1c48d162c77e..b9d2803b518b 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -286,6 +286,7 @@ int drm_connector_init(struct drm_device *dev, connector->status = connector_status_unknown; connector->display_info.panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; + connector->firmware_edid = NULL; drm_connector_get_cmdline_mode(connector); @@ -485,6 +486,7 @@ void drm_connector_cleanup(struct drm_connector *connector) ida_simple_remove(&dev->mode_config.connector_ida, connector->index); + kfree(connector->firmware_edid); kfree(connector->display_info.bus_formats); drm_mode_object_unregister(dev, &connector->base); kfree(connector->name); diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 37d8ba3ddb46..a38fe4e00e4a 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -253,6 +253,13 @@ static void *edid_load(struct drm_connector *connector, const char *name, edid = new_edid; } + connector->firmware_edid = drm_edid_duplicate((struct edid *)edid); + if (!connector->firmware_edid) { + kfree(edid); + edid = ERR_PTR(-ENOMEM); + goto out; + } + 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", @@ -269,6 +276,12 @@ struct edid *drm_load_edid_firmware(struct drm_connector *connector) char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL; struct edid *edid; + /* already loaded? */ + if (connector->firmware_edid) { + edid = drm_edid_duplicate(connector->firmware_edid); + return edid ? edid : ERR_PTR(-ENOMEM); + } + if (edid_firmware[0] == '\0') return ERR_PTR(-ENOENT); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 3ac4bf87f257..b5d0c87327a3 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1528,6 +1528,8 @@ struct drm_connector { enum drm_connector_force force; /** @override_edid: has the EDID been overwritten through debugfs for testing? */ bool override_edid; + /** @firmware_edid: the cached firmware EDID bytes */ + struct edid *firmware_edid; /** @epoch_counter: used to detect any other changes in connector, besides status */ u64 epoch_counter;