]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/gpu/drm/drm_dp_mst_topology.c
drm/dp_mst: Add new quirk for Synaptics MST hubs
[linux.git] / drivers / gpu / drm / drm_dp_mst_topology.c
index 2443532341d00209182e361b264da00aac8b3418..480c71a00cf2e2913d1ff2b38b44bbfe508064b6 100644 (file)
@@ -853,6 +853,7 @@ static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband
 {
        int idx = 1;
        repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
+       repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1;
        idx++;
        if (idx > raw->curlen)
                goto fail_len;
@@ -2174,6 +2175,7 @@ drm_dp_mst_topology_unlink_port(struct drm_dp_mst_topology_mgr *mgr,
                                struct drm_dp_mst_port *port)
 {
        mutex_lock(&mgr->lock);
+       port->parent->num_ports--;
        list_del(&port->next);
        mutex_unlock(&mgr->lock);
        drm_dp_mst_topology_put_port(port);
@@ -2273,6 +2275,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
                mutex_lock(&mgr->lock);
                drm_dp_mst_topology_get_port(port);
                list_add(&port->next, &mstb->ports);
+               mstb->num_ports++;
                mutex_unlock(&mgr->lock);
        }
 
@@ -2951,6 +2954,7 @@ drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
                                      path_res->avail_payload_bw_number);
                        port->available_pbn =
                                path_res->avail_payload_bw_number;
+                       port->fec_capable = path_res->fec_capable;
                }
        }
 
@@ -5072,3 +5076,173 @@ static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
 {
        i2c_del_adapter(&aux->ddc);
 }
+
+/**
+ * drm_dp_mst_is_virtual_dpcd() - Is the given port a virtual DP Peer Device
+ * @port: The port to check
+ *
+ * A single physical MST hub object can be represented in the topology
+ * by multiple branches, with virtual ports between those branches.
+ *
+ * As of DP1.4, An MST hub with internal (virtual) ports must expose
+ * certain DPCD registers over those ports. See sections 2.6.1.1.1
+ * and 2.6.1.1.2 of Display Port specification v1.4 for details.
+ *
+ * May acquire mgr->lock
+ *
+ * Returns:
+ * true if the port is a virtual DP peer device, false otherwise
+ */
+static bool drm_dp_mst_is_virtual_dpcd(struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_port *downstream_port;
+
+       if (!port || port->dpcd_rev < DP_DPCD_REV_14)
+               return false;
+
+       /* Virtual DP Sink (Internal Display Panel) */
+       if (port->port_num >= 8)
+               return true;
+
+       /* DP-to-HDMI Protocol Converter */
+       if (port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV &&
+           !port->mcs &&
+           port->ldps)
+               return true;
+
+       /* DP-to-DP */
+       mutex_lock(&port->mgr->lock);
+       if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
+           port->mstb &&
+           port->mstb->num_ports == 2) {
+               list_for_each_entry(downstream_port, &port->mstb->ports, next) {
+                       if (downstream_port->pdt == DP_PEER_DEVICE_SST_SINK &&
+                           !downstream_port->input) {
+                               mutex_unlock(&port->mgr->lock);
+                               return true;
+                       }
+               }
+       }
+       mutex_unlock(&port->mgr->lock);
+
+       return false;
+}
+
+/**
+ * drm_dp_mst_dsc_aux_for_port() - Find the correct aux for DSC
+ * @port: The port to check. A leaf of the MST tree with an attached display.
+ *
+ * Depending on the situation, DSC may be enabled via the endpoint aux,
+ * the immediately upstream aux, or the connector's physical aux.
+ *
+ * This is both the correct aux to read DSC_CAPABILITY and the
+ * correct aux to write DSC_ENABLED.
+ *
+ * This operation can be expensive (up to four aux reads), so
+ * the caller should cache the return.
+ *
+ * Returns:
+ * NULL if DSC cannot be enabled on this port, otherwise the aux device
+ */
+struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
+{
+       struct drm_dp_mst_port *immediate_upstream_port;
+       struct drm_dp_mst_port *fec_port;
+       struct drm_dp_desc desc = { 0 };
+       u8 endpoint_fec;
+       u8 endpoint_dsc;
+
+       if (!port)
+               return NULL;
+
+       if (port->parent->port_parent)
+               immediate_upstream_port = port->parent->port_parent;
+       else
+               immediate_upstream_port = NULL;
+
+       fec_port = immediate_upstream_port;
+       while (fec_port) {
+               /*
+                * Each physical link (i.e. not a virtual port) between the
+                * output and the primary device must support FEC
+                */
+               if (!drm_dp_mst_is_virtual_dpcd(fec_port) &&
+                   !fec_port->fec_capable)
+                       return NULL;
+
+               fec_port = fec_port->parent->port_parent;
+       }
+
+       /* DP-to-DP peer device */
+       if (drm_dp_mst_is_virtual_dpcd(immediate_upstream_port)) {
+               u8 upstream_dsc;
+
+               if (drm_dp_dpcd_read(&port->aux,
+                                    DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
+                       return NULL;
+               if (drm_dp_dpcd_read(&port->aux,
+                                    DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
+                       return NULL;
+               if (drm_dp_dpcd_read(&immediate_upstream_port->aux,
+                                    DP_DSC_SUPPORT, &upstream_dsc, 1) != 1)
+                       return NULL;
+
+               /* Enpoint decompression with DP-to-DP peer device */
+               if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
+                   (endpoint_fec & DP_FEC_CAPABLE) &&
+                   (upstream_dsc & 0x2) /* DSC passthrough */)
+                       return &port->aux;
+
+               /* Virtual DPCD decompression with DP-to-DP peer device */
+               return &immediate_upstream_port->aux;
+       }
+
+       /* Virtual DPCD decompression with DP-to-HDMI or Virtual DP Sink */
+       if (drm_dp_mst_is_virtual_dpcd(port))
+               return &port->aux;
+
+       /*
+        * Synaptics quirk
+        * Applies to ports for which:
+        * - Physical aux has Synaptics OUI
+        * - DPv1.4 or higher
+        * - Port is on primary branch device
+        * - Not a VGA adapter (DP_DWN_STRM_PORT_TYPE_ANALOG)
+        */
+       if (drm_dp_read_desc(port->mgr->aux, &desc, true))
+               return NULL;
+
+       if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
+           port->mgr->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14 &&
+           port->parent == port->mgr->mst_primary) {
+               u8 downstreamport;
+
+               if (drm_dp_dpcd_read(&port->aux, DP_DOWNSTREAMPORT_PRESENT,
+                                    &downstreamport, 1) < 0)
+                       return NULL;
+
+               if ((downstreamport & DP_DWN_STRM_PORT_PRESENT) &&
+                  ((downstreamport & DP_DWN_STRM_PORT_TYPE_MASK)
+                    != DP_DWN_STRM_PORT_TYPE_ANALOG))
+                       return port->mgr->aux;
+       }
+
+       /*
+        * The check below verifies if the MST sink
+        * connected to the GPU is capable of DSC -
+        * therefore the endpoint needs to be
+        * both DSC and FEC capable.
+        */
+       if (drm_dp_dpcd_read(&port->aux,
+          DP_DSC_SUPPORT, &endpoint_dsc, 1) != 1)
+               return NULL;
+       if (drm_dp_dpcd_read(&port->aux,
+          DP_FEC_CAPABILITY, &endpoint_fec, 1) != 1)
+               return NULL;
+       if ((endpoint_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED) &&
+          (endpoint_fec & DP_FEC_CAPABLE))
+               return &port->aux;
+
+       return NULL;
+}
+EXPORT_SYMBOL(drm_dp_mst_dsc_aux_for_port);