Message ID | 8c8b8ad55ea714ef5c7f48ff5cd9b889dcead76b.1568833906.git.mikita.lipski@amd.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | DSC MST support for AMDGPU | expand |
Reviewed-by: Lyude Paul <lyude@redhat.com> On Wed, 2019-09-18 at 16:26 -0400, mikita.lipski@amd.com wrote: > From: David Francis <David.Francis@amd.com> > > Add drm_dp_mst_dsc_aux_for_port. To enable DSC, the DSC_ENABLED > register might have to be written on the leaf port's DPCD, > its parent's DPCD, or the MST manager's DPCD. This function > finds the correct aux for the job. > > As part of this, add drm_dp_mst_is_virtual_dpcd. Virtual DPCD > is a DP feature new in DP v1.4, which exposes certain DPCD > registers on virtual ports. > > v2: Remember to unlock mutex on all paths > v3: Refactor to match coding style and increase brevity > > Cc: Lyude Paul <lyude@redhat.com> > Cc: Jani Nikula <jani.nikula@linux.intel.com> > Cc: Harry Wentland <harry.wentland@amd.com> > Reviewed-by: Wenjing Liu <Wenjing.Liu@amd.com> > Signed-off-by: David Francis <David.Francis@amd.com> > --- > drivers/gpu/drm/drm_dp_mst_topology.c | 127 ++++++++++++++++++++++++++ > include/drm/drm_dp_mst_helper.h | 2 + > 2 files changed, 129 insertions(+) > > diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c > b/drivers/gpu/drm/drm_dp_mst_topology.c > index ae2f986d76a2..dd2ca065cc92 100644 > --- a/drivers/gpu/drm/drm_dp_mst_topology.c > +++ b/drivers/gpu/drm/drm_dp_mst_topology.c > @@ -4147,3 +4147,130 @@ 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; > + > + if (!port) > + return NULL; > + > + if (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; > + u8 endpoint_dsc; > + u8 endpoint_fec; > + > + if (drm_dp_dpcd_read(&port->aux, > + DP_DSC_SUPPORT, &endpoint_dsc, 1) < 0) > + return NULL; > + if (drm_dp_dpcd_read(&port->aux, > + DP_FEC_CAPABILITY, &endpoint_fec, 1) < 0) > + return NULL; > + if (drm_dp_dpcd_read(&immediate_upstream_port->aux, > + DP_DSC_SUPPORT, &upstream_dsc, 1) < 0) > + 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; > + > + return NULL; > +} > +EXPORT_SYMBOL(drm_dp_mst_dsc_aux_for_port); > diff --git a/include/drm/drm_dp_mst_helper.h > b/include/drm/drm_dp_mst_helper.h > index f113ae04fa88..4cf738545dfb 100644 > --- a/include/drm/drm_dp_mst_helper.h > +++ b/include/drm/drm_dp_mst_helper.h > @@ -673,6 +673,8 @@ int __must_check drm_dp_mst_atomic_check(struct > drm_atomic_state *state); > void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port); > void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port); > > +struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port > *port); > + > extern const struct drm_private_state_funcs > drm_dp_mst_topology_state_funcs; > > /**
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index ae2f986d76a2..dd2ca065cc92 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4147,3 +4147,130 @@ 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; + + if (!port) + return NULL; + + if (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; + u8 endpoint_dsc; + u8 endpoint_fec; + + if (drm_dp_dpcd_read(&port->aux, + DP_DSC_SUPPORT, &endpoint_dsc, 1) < 0) + return NULL; + if (drm_dp_dpcd_read(&port->aux, + DP_FEC_CAPABILITY, &endpoint_fec, 1) < 0) + return NULL; + if (drm_dp_dpcd_read(&immediate_upstream_port->aux, + DP_DSC_SUPPORT, &upstream_dsc, 1) < 0) + 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; + + return NULL; +} +EXPORT_SYMBOL(drm_dp_mst_dsc_aux_for_port); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index f113ae04fa88..4cf738545dfb 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -673,6 +673,8 @@ int __must_check drm_dp_mst_atomic_check(struct drm_atomic_state *state); void drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port); void drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port); +struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port); + extern const struct drm_private_state_funcs drm_dp_mst_topology_state_funcs; /**