summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJani Nikula <jani.nikula@intel.com>2019-06-10 05:30:54 -0400
committerJani Nikula <jani.nikula@intel.com>2019-06-12 06:26:25 -0400
commit48eaeb7664c76139438724d520a1ea4a84a3ed92 (patch)
treeda87b367a6ee0b16ec5b3c62d1de32be5db84244
parent56a2b7f2a39a8d4b16a628e113decde3d7400879 (diff)
drm: add fallback override/firmware EDID modes workaround
We've moved the override and firmware EDID (simply "override EDID" from now on) handling to the low level drm_do_get_edid() function in order to transparently use the override throughout the stack. The idea is that you get the override EDID via the ->get_modes() hook. Unfortunately, there are scenarios where the DDC probe in drm_get_edid() called via ->get_modes() fails, although the preceding ->detect() succeeds. In the case reported by Paul Wise, the ->detect() hook, intel_crt_detect(), relies on hotplug detect, bypassing the DDC. In the case reported by Ilpo Järvinen, there is no ->detect() hook, which is interpreted as connected. The subsequent DDC probe reached via ->get_modes() fails, and we don't even look at the override EDID, resulting in no modes being added. Because drm_get_edid() is used via ->detect() all over the place, we can't trivially remove the DDC probe, as it leads to override EDID effectively meaning connector forcing. The goal is that connector forcing and override EDID remain orthogonal. Generally, the underlying problem here is the conflation of ->detect() and ->get_modes() via drm_get_edid(). The former should just detect, and the latter should just get the modes, typically via reading the EDID. As long as drm_get_edid() is used in ->detect(), it needs to retain the DDC probe. Or such users need to have a separate DDC probe step first. The EDID caching between ->detect() and ->get_modes() done by some drivers is a further complication that prevents us from making drm_do_get_edid() adapt to the two cases. Work around the regression by falling back to a separate attempt at getting the override EDID at drm_helper_probe_single_connector_modes() level. With a working DDC and override EDID, it'll never be called; the override EDID will come via ->get_modes(). There will still be a failing DDC probe attempt in the cases that require the fallback. v2: - Call drm_connector_update_edid_property (Paul) - Update commit message about EDID caching (Daniel) Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=107583 Reported-by: Paul Wise <pabs3@bonedaddy.net> Cc: Paul Wise <pabs3@bonedaddy.net> References: http://mid.mail-archive.com/alpine.DEB.2.20.1905262211270.24390@whs-18.cs.helsinki.fi Reported-by: Ilpo Järvinen <ilpo.jarvinen@cs.helsinki.fi> Cc: Ilpo Järvinen <ilpo.jarvinen@cs.helsinki.fi> Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch> References: 15f080f08d48 ("drm/edid: respect connector force for drm_get_edid ddc probe") Fixes: 53fd40a90f3c ("drm: handle override and firmware EDID at drm_do_get_edid() level") Cc: <stable@vger.kernel.org> # v4.15+ 56a2b7f2a39a drm/edid: abstract override/firmware EDID retrieval Cc: <stable@vger.kernel.org> # v4.15+ Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Harish Chegondi <harish.chegondi@intel.com> Tested-by: Paul Wise <pabs3@bonedaddy.net> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Jani Nikula <jani.nikula@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190610093054.28445-1-jani.nikula@intel.com
-rw-r--r--drivers/gpu/drm/drm_edid.c30
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c7
-rw-r--r--include/drm/drm_edid.h1
3 files changed, 38 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index c1952b6e5747..e804ac5dec02 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1585,6 +1585,36 @@ static struct edid *drm_get_override_edid(struct drm_connector *connector)
1585} 1585}
1586 1586
1587/** 1587/**
1588 * drm_add_override_edid_modes - add modes from override/firmware EDID
1589 * @connector: connector we're probing
1590 *
1591 * Add modes from the override/firmware EDID, if available. Only to be used from
1592 * drm_helper_probe_single_connector_modes() as a fallback for when DDC probe
1593 * failed during drm_get_edid() and caused the override/firmware EDID to be
1594 * skipped.
1595 *
1596 * Return: The number of modes added or 0 if we couldn't find any.
1597 */
1598int drm_add_override_edid_modes(struct drm_connector *connector)
1599{
1600 struct edid *override;
1601 int num_modes = 0;
1602
1603 override = drm_get_override_edid(connector);
1604 if (override) {
1605 drm_connector_update_edid_property(connector, override);
1606 num_modes = drm_add_edid_modes(connector, override);
1607 kfree(override);
1608
1609 DRM_DEBUG_KMS("[CONNECTOR:%d:%s] adding %d modes via fallback override/firmware EDID\n",
1610 connector->base.id, connector->name, num_modes);
1611 }
1612
1613 return num_modes;
1614}
1615EXPORT_SYMBOL(drm_add_override_edid_modes);
1616
1617/**
1588 * drm_do_get_edid - get EDID data using a custom EDID block read function 1618 * drm_do_get_edid - get EDID data using a custom EDID block read function
1589 * @connector: connector we're probing 1619 * @connector: connector we're probing
1590 * @get_edid_block: EDID block read function 1620 * @get_edid_block: EDID block read function
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 6fd08e04b323..dd427c7ff967 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -479,6 +479,13 @@ retry:
479 479
480 count = (*connector_funcs->get_modes)(connector); 480 count = (*connector_funcs->get_modes)(connector);
481 481
482 /*
483 * Fallback for when DDC probe failed in drm_get_edid() and thus skipped
484 * override/firmware EDID.
485 */
486 if (count == 0 && connector->status == connector_status_connected)
487 count = drm_add_override_edid_modes(connector);
488
482 if (count == 0 && connector->status == connector_status_connected) 489 if (count == 0 && connector->status == connector_status_connected)
483 count = drm_add_modes_noedid(connector, 1024, 768); 490 count = drm_add_modes_noedid(connector, 1024, 768);
484 count += drm_helper_probe_add_cmdline_mode(connector); 491 count += drm_helper_probe_add_cmdline_mode(connector);
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 9d3b5b93102c..c9ca0be54d9a 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -471,6 +471,7 @@ struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
471 struct i2c_adapter *adapter); 471 struct i2c_adapter *adapter);
472struct edid *drm_edid_duplicate(const struct edid *edid); 472struct edid *drm_edid_duplicate(const struct edid *edid);
473int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); 473int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
474int drm_add_override_edid_modes(struct drm_connector *connector);
474 475
475u8 drm_match_cea_mode(const struct drm_display_mode *to_match); 476u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
476enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code); 477enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code);