From: Ville Syrjälä <ville.syrj...@linux.intel.com>

Try to deal with duplicate child devices for the same DDI port
by attempting to initialize each in turn. The first on to succeed
will be the one we use.

TODO: Still very rough. Should probably just initialize DDIs
      strictly in child device order, and just filter out the
      ones that are bogus (eg. via device_info->port_mask etc.)?

Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
---
 drivers/gpu/drm/i915/display/intel_bios.c     | 67 ++++++++++++++-----
 drivers/gpu/drm/i915/display/intel_bios.h     |  6 ++
 drivers/gpu/drm/i915/display/intel_ddi.c      | 29 ++++----
 .../gpu/drm/i915/display/intel_display_core.h |  2 -
 4 files changed, 72 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_bios.c 
b/drivers/gpu/drm/i915/display/intel_bios.c
index ac9fc07e5ccf..73b1f18bc8c9 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -2610,6 +2610,31 @@ intel_bios_encoder_is_lspcon(const struct 
intel_bios_encoder_data *devdata)
        return devdata && HAS_LSPCON(devdata->i915) && devdata->child.lspcon;
 }
 
+bool
+intel_bios_for_each_child(struct drm_i915_private *i915,
+                         enum port port,
+                         bool (*func)(struct drm_i915_private *i915,
+                                      struct intel_bios_encoder_data *devdata,
+                                      enum port port))
+{
+       struct intel_bios_encoder_data *devdata;
+       bool found = false;
+
+       list_for_each_entry(devdata, &i915->display.vbt.display_devices, node) {
+               const struct child_device_config *child = &devdata->child;
+
+               if (dvo_port_to_port(i915, child->dvo_port) != port)
+                       continue;
+
+               found = true;
+
+               if (func(i915, devdata, port))
+                       break;
+       }
+
+       return found;
+}
+
 /* This is an index in the HDMI/DVI DDI buffer translation table, or -1 */
 int intel_bios_encoder_hdmi_level_shift(const struct intel_bios_encoder_data 
*devdata)
 {
@@ -2656,13 +2681,19 @@ static bool is_port_valid(struct drm_i915_private 
*i915, enum port port)
        return true;
 }
 
-static void print_ddi_port(const struct intel_bios_encoder_data *devdata,
-                          enum port port)
+static void print_ddi_port(const struct intel_bios_encoder_data *devdata)
 {
        struct drm_i915_private *i915 = devdata->i915;
        const struct child_device_config *child = &devdata->child;
        bool is_dvi, is_hdmi, is_dp, is_edp, is_dsi, is_crt, 
supports_typec_usb, supports_tbt;
        int dp_boost_level, dp_max_link_rate, hdmi_boost_level, 
hdmi_level_shift, max_tmds_clock;
+       enum port port;
+
+       port = dvo_port_to_port(i915, child->dvo_port);
+       if (port == PORT_NONE && DISPLAY_VER(i915) >= 11)
+               port = dsi_dvo_port_to_port(i915, child->dvo_port);
+       if (port == PORT_NONE)
+               return;
 
        is_dvi = intel_bios_encoder_supports_dvi(devdata);
        is_dp = intel_bios_encoder_supports_dp(devdata);
@@ -2741,16 +2772,7 @@ static void parse_ddi_port(struct 
intel_bios_encoder_data *devdata)
                return;
        }
 
-       if (i915->display.vbt.ports[port]) {
-               drm_dbg_kms(&i915->drm,
-                           "More than one child device for port %c in VBT, 
using the first.\n",
-                           port_name(port));
-               return;
-       }
-
        sanitize_device_type(devdata, port);
-
-       i915->display.vbt.ports[port] = devdata;
 }
 
 void intel_bios_encoder_sanitize(struct intel_bios_encoder_data *devdata,
@@ -2774,7 +2796,6 @@ static bool has_ddi_port_info(struct drm_i915_private 
*i915)
 static void parse_ddi_ports(struct drm_i915_private *i915)
 {
        struct intel_bios_encoder_data *devdata;
-       enum port port;
 
        if (!has_ddi_port_info(i915))
                return;
@@ -2782,10 +2803,8 @@ static void parse_ddi_ports(struct drm_i915_private 
*i915)
        list_for_each_entry(devdata, &i915->display.vbt.display_devices, node)
                parse_ddi_port(devdata);
 
-       for_each_port(port) {
-               if (i915->display.vbt.ports[port])
-                       print_ddi_port(i915->display.vbt.ports[port], port);
-       }
+       list_for_each_entry(devdata, &i915->display.vbt.display_devices, node)
+               print_ddi_port(devdata);
 }
 
 static void
@@ -3727,5 +3746,19 @@ bool intel_bios_encoder_hpd_invert(const struct 
intel_bios_encoder_data *devdata
 struct intel_bios_encoder_data *
 intel_bios_encoder_data_lookup(struct drm_i915_private *i915, enum port port)
 {
-       return i915->display.vbt.ports[port];
+       struct intel_bios_encoder_data *devdata;
+
+       list_for_each_entry(devdata, &i915->display.vbt.display_devices, node) {
+               const struct child_device_config *child = &devdata->child;
+               enum port p;
+
+               p = dvo_port_to_port(i915, child->dvo_port);
+               if (p == PORT_NONE && DISPLAY_VER(i915) >= 11)
+                       p = dsi_dvo_port_to_port(i915, child->dvo_port);
+
+               if (p == port)
+                       return devdata;
+       }
+
+       return NULL;
 }
diff --git a/drivers/gpu/drm/i915/display/intel_bios.h 
b/drivers/gpu/drm/i915/display/intel_bios.h
index 7f72e35eec3d..c3b79793ec18 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.h
+++ b/drivers/gpu/drm/i915/display/intel_bios.h
@@ -280,4 +280,10 @@ int intel_bios_encoder_hdmi_boost_level(const struct 
intel_bios_encoder_data *de
 int intel_bios_encoder_hdmi_level_shift(const struct intel_bios_encoder_data 
*devdata);
 int intel_bios_encoder_hdmi_ddc_pin(const struct intel_bios_encoder_data 
*devdata);
 
+bool
+intel_bios_for_each_child(struct drm_i915_private *i915,
+                         enum port port,
+                         bool (*func)(struct drm_i915_private *i915,
+                                      struct intel_bios_encoder_data *devdata, 
enum port port));
+
 #endif /* _INTEL_BIOS_H_ */
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c 
b/drivers/gpu/drm/i915/display/intel_ddi.c
index 2864592fe6b9..b05eca899ace 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -4274,11 +4274,12 @@ static void intel_ddi_encoder_shutdown(struct 
intel_encoder *encoder)
 #define port_tc_name(port) ((port) - PORT_TC1 + '1')
 #define tc_port_name(tc_port) ((tc_port) - TC_PORT_1 + '1')
 
-void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
+static bool _intel_ddi_init(struct drm_i915_private *dev_priv,
+                           struct intel_bios_encoder_data *devdata,
+                           enum port port)
 {
        struct intel_digital_port *dig_port;
        struct intel_encoder *encoder;
-       struct intel_bios_encoder_data *devdata;
        bool init_hdmi, init_dp;
        enum phy phy = intel_port_to_phy(dev_priv, port);
 
@@ -4291,16 +4292,9 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, 
enum port port)
        if (intel_hti_uses_phy(dev_priv, phy)) {
                drm_dbg_kms(&dev_priv->drm, "PORT %c / PHY %c reserved by 
HTI\n",
                            port_name(port), phy_name(phy));
-               return;
+               return false;
        }
 
-       devdata = intel_bios_encoder_data_lookup(dev_priv, port);
-       if (!devdata) {
-               drm_dbg_kms(&dev_priv->drm,
-                           "VBT says port %c is not present\n",
-                           port_name(port));
-               return;
-       }
        intel_bios_encoder_sanitize(devdata, port);
 
        init_hdmi = intel_bios_encoder_supports_dvi(devdata) ||
@@ -4323,7 +4317,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, 
enum port port)
                drm_dbg_kms(&dev_priv->drm,
                            "VBT says port %c is not DVI/HDMI/DP compatible, 
respect it\n",
                            port_name(port));
-               return;
+               return false;
        }
 
        if (intel_phy_is_snps(dev_priv, phy) &&
@@ -4335,7 +4329,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, 
enum port port)
 
        dig_port = kzalloc(sizeof(*dig_port), GFP_KERNEL);
        if (!dig_port)
-               return;
+               return false;
 
        encoder = &dig_port->base;
        encoder->devdata = devdata;
@@ -4560,9 +4554,18 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, 
enum port port)
 
        intel_infoframe_init(dig_port);
 
-       return;
+       return true;
 
 err:
        drm_encoder_cleanup(&encoder->base);
        kfree(dig_port);
+       return false;
+}
+
+void intel_ddi_init(struct drm_i915_private *i915, enum port port)
+{
+       if (!intel_bios_for_each_child(i915, port, _intel_ddi_init))
+               drm_dbg_kms(&i915->drm,
+                           "VBT says port %c is not present\n",
+                           port_name(port));
 }
diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h 
b/drivers/gpu/drm/i915/display/intel_display_core.h
index 25d778fb7d15..b63c5db76a97 100644
--- a/drivers/gpu/drm/i915/display/intel_display_core.h
+++ b/drivers/gpu/drm/i915/display/intel_display_core.h
@@ -34,7 +34,6 @@ struct i915_audio_component;
 struct i915_hdcp_comp_master;
 struct intel_atomic_state;
 struct intel_audio_funcs;
-struct intel_bios_encoder_data;
 struct intel_cdclk_funcs;
 struct intel_cdclk_vals;
 struct intel_color_funcs;
@@ -206,7 +205,6 @@ struct intel_vbt_data {
        struct list_head display_devices;
        struct list_head bdb_blocks;
 
-       struct intel_bios_encoder_data *ports[I915_MAX_PORTS]; /* Non-NULL if 
port present. */
        struct sdvo_device_mapping {
                u8 initialized;
                u8 dvo_port;
-- 
2.39.1

Reply via email to