Add a replacement for drm_get_edid that handles override and firmware
EDID at the low level, and handles EDID property update, ELD update, and
adds modes. This allows for a more transparent EDID override, and makes
it possible to unify the drivers better. It also allows reusing the EDID
cached in the property, and only probing the DDC.

Signed-off-by: Jani Nikula <jani.nikula at intel.com>
---
 drivers/gpu/drm/drm_connector.c    | 60 ++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_probe_helper.c |  7 +++++
 include/drm/drm_connector.h        |  6 ++++
 3 files changed, 73 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 3115db2ae6b1..b9636c98dbf3 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -1086,6 +1086,66 @@ int drm_mode_connector_update_edid_property(struct 
drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_mode_connector_update_edid_property);

+/**
+ * drm_mode_connector_get_edid - do it all replacement for drm_get_edid()
+ * @connector: connector to probe
+ * @adapter: I2C adapter to use for DDC
+ * @edid_out: if non-NULL, get EDID here, must be kfree'd by caller
+ * @no_cache: false to allow using cached EDID, true to force read
+ *
+ * drm_get_edid() on steroids. Handle debugfs override edid, firmware edid, and
+ * caching at the low level. Add modes, update ELD and EDID property. Using 
this
+ * prevents override/firmware edid usage at the higher level.
+ *
+ * @return: Number of modes from drm_add_edid_modes().
+ */
+int drm_mode_connector_get_edid(struct drm_connector *connector,
+                               struct i2c_adapter *adapter,
+                               struct edid **edid_out,
+                               bool no_cache)
+{
+       struct drm_property_blob *prop = connector->edid_blob_ptr;
+       struct edid *edid = NULL;
+       int count = 0;
+
+       /*
+        * If a driver uses this function on a connector, override/firmware edid
+        * will only be used at this level.
+        */
+       connector->low_level_override = true;
+
+       if ((connector->override_edid || !no_cache) && prop && prop->data)
+               edid = drm_edid_duplicate((struct edid *) prop->data);
+
+       if (!edid) {
+               edid = drm_load_edid_firmware(connector);
+               if (IS_ERR(edid))
+                       edid = NULL;
+       }
+
+       if (edid) {
+               /* Require successful probe for override/cached/firmware EDID */
+               if (drm_probe_ddc(adapter))
+                       drm_get_displayid(connector, edid);
+               else
+                       edid = NULL;
+       } else {
+               edid = drm_get_edid(connector, adapter);
+       }
+
+       count = drm_add_edid_modes(connector, edid);
+       drm_edid_to_eld(connector, edid);
+       drm_mode_connector_update_edid_property(connector, edid);
+
+       if (edid_out)
+               *edid_out = edid;
+       else
+               kfree(edid);
+
+       return count;
+}
+EXPORT_SYMBOL(drm_mode_connector_get_edid);
+
 int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
                                    struct drm_property *property,
                                    uint64_t value)
diff --git a/drivers/gpu/drm/drm_probe_helper.c 
b/drivers/gpu/drm/drm_probe_helper.c
index 431349673846..20a175a775d6 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -167,6 +167,13 @@ static bool bypass_edid(struct drm_connector *connector, 
int *count)
 {
        struct edid *edid;

+       /*
+        * If a driver uses drm_connector_get_edid() on a connector,
+        * override/firmware edid will only be used at that level.
+        */
+       if (connector->low_level_override)
+               return false;
+
        if (connector->override_edid) {
                edid = (struct edid *) connector->edid_blob_ptr->data;

diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 6e352a0b5c81..0c85ddc422de 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -25,6 +25,7 @@

 #include <linux/list.h>
 #include <linux/ctype.h>
+#include <linux/i2c.h>
 #include <drm/drm_mode_object.h>

 #include <uapi/drm/drm_mode.h>
@@ -727,6 +728,7 @@ struct drm_connector {
        /* forced on connector */
        struct drm_cmdline_mode cmdline_mode;
        enum drm_connector_force force;
+       bool low_level_override;
        bool override_edid;

 #define DRM_CONNECTOR_MAX_ENCODER 3
@@ -837,6 +839,10 @@ int drm_mode_connector_set_path_property(struct 
drm_connector *connector,
 int drm_mode_connector_set_tile_property(struct drm_connector *connector);
 int drm_mode_connector_update_edid_property(struct drm_connector *connector,
                                            const struct edid *edid);
+int drm_mode_connector_get_edid(struct drm_connector *connector,
+                               struct i2c_adapter *adapter,
+                               struct edid **edid_out,
+                               bool no_cache);

 /**
  * struct drm_tile_group - Tile group metadata
-- 
2.1.4

Reply via email to