diff mbox series

[v2,11/14] drm/amd/display: Validate DSC caps on MST endpoints

Message ID 20190820191203.25807-12-David.Francis@amd.com (mailing list archive)
State New, archived
Headers show
Series Display Stream Compression (DSC) for AMD Navi | expand

Commit Message

Francis, David Aug. 20, 2019, 7:12 p.m. UTC
The first step in MST DSC is checking MST endpoints
to see how DSC can be enabled

Case 1: DP-to-DP peer device
if the branch immediately upstream has
 - PDT = DP_PEER_DEVICE_DP_MST_BRANCHING (2)
 - DPCD rev. >= DP 1.4
 - Exactly one input and one output
 - The output has PDT = DP_PEER_DEVICE_SST_SINK (3)

In this case, DSC could be possible either on the endpoint
or the peer device. Prefer the endpoint, which is possible if
 - The endpoint has DP_DSC_DECOMPRESSION_IS_SUPPORTED bit set
 - The endpoint has DP_FEC_CAPABLE bit set
 - The peer device has DSC_PASSTHROUGH_CAPABILITY bit set (from DP v2.0)

Otherwise, use the peer device

Case 2: DP-to-HDMI peer device
If the output port has
 - PDT = DP_PEER_DEVICE_DP_LEGACY_CONV (4)
 - DPCD rev. >= DP 1.4
 - LDPS = true
 - MCS = false

In this case, DSC can only be attempted on the peer device
(the output port)

Case 3: Virtual DP Sink (Internal Display Panel)
If the output port has
 - DPCD rev. >= DP 1.4
 - port_num >= 8

In this case, DSC can only be attempted on the peer device
(the output port)

Case 4: Synaptix Workaround
If the output has
 - link DPCD rev. >= DP 1.4
 - link branch_dev_id = 0x90CC24 (Synaptix)
 - There is exactly one branch device between the link and output

In this case, DSC can be attempted, but only using the *link*
aux device's caps. This is a quirk.

Test for these cases as modes are enumerated for an MST endpoint.
We cannot do this during link attach because the dc_sink object
will not have been created yet

If no DSC is attempted, zero the DSC caps

Cc: Wenjing Liu <Wenjing.Liu@amd.com>
Cc: Nikola Cornij <Nikola.Cornij@amd.com>
Signed-off-by: David Francis <David.Francis@amd.com>
---
 .../display/amdgpu_dm/amdgpu_dm_mst_types.c   | 123 +++++++++++++++++-
 .../display/amdgpu_dm/amdgpu_dm_mst_types.h   |   3 +
 2 files changed, 125 insertions(+), 1 deletion(-)

Comments

Lyude Paul Aug. 20, 2019, 8:34 p.m. UTC | #1
Hi, I was looking through patchwork and just noticed this series. I've been
working on trying to rework a lot of the drm MST helpers (currently on hold
for a short bit while I fix some other unrelated issues at work...), and one
of the things we're currently missing with our helpers are helpers for
handling checking the PBN of downstream sinks (which could be added to
drm_dp_mst_atomic_check()). As well, it looks like you could also add a DRM
helper for checking for DSC/FEC support on mstbs. Thoughts?

On Tue, 2019-08-20 at 15:12 -0400, David Francis wrote:
> The first step in MST DSC is checking MST endpoints
> to see how DSC can be enabled
> 
> Case 1: DP-to-DP peer device
> if the branch immediately upstream has
>  - PDT = DP_PEER_DEVICE_DP_MST_BRANCHING (2)
>  - DPCD rev. >= DP 1.4
>  - Exactly one input and one output
>  - The output has PDT = DP_PEER_DEVICE_SST_SINK (3)
> 
> In this case, DSC could be possible either on the endpoint
> or the peer device. Prefer the endpoint, which is possible if
>  - The endpoint has DP_DSC_DECOMPRESSION_IS_SUPPORTED bit set
>  - The endpoint has DP_FEC_CAPABLE bit set
>  - The peer device has DSC_PASSTHROUGH_CAPABILITY bit set (from DP v2.0)
> 
> Otherwise, use the peer device
> 
> Case 2: DP-to-HDMI peer device
> If the output port has
>  - PDT = DP_PEER_DEVICE_DP_LEGACY_CONV (4)
>  - DPCD rev. >= DP 1.4
>  - LDPS = true
>  - MCS = false
> 
> In this case, DSC can only be attempted on the peer device
> (the output port)
> 
> Case 3: Virtual DP Sink (Internal Display Panel)
> If the output port has
>  - DPCD rev. >= DP 1.4
>  - port_num >= 8
> 
> In this case, DSC can only be attempted on the peer device
> (the output port)
> 
> Case 4: Synaptix Workaround
> If the output has
>  - link DPCD rev. >= DP 1.4
>  - link branch_dev_id = 0x90CC24 (Synaptix)
>  - There is exactly one branch device between the link and output
> 
> In this case, DSC can be attempted, but only using the *link*
> aux device's caps. This is a quirk.
> 
> Test for these cases as modes are enumerated for an MST endpoint.
> We cannot do this during link attach because the dc_sink object
> will not have been created yet
> 
> If no DSC is attempted, zero the DSC caps
> 
> Cc: Wenjing Liu <Wenjing.Liu@amd.com>
> Cc: Nikola Cornij <Nikola.Cornij@amd.com>
> Signed-off-by: David Francis <David.Francis@amd.com>
> ---
>  .../display/amdgpu_dm/amdgpu_dm_mst_types.c   | 123 +++++++++++++++++-
>  .../display/amdgpu_dm/amdgpu_dm_mst_types.h   |   3 +
>  2 files changed, 125 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> index 16218a202b59..58571844f6d5 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> @@ -25,6 +25,7 @@
>  
>  #include <linux/version.h>
>  #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_dp_mst_helper.h>
>  #include "dm_services.h"
>  #include "amdgpu.h"
>  #include "amdgpu_dm.h"
> @@ -189,6 +190,120 @@ static const struct drm_connector_funcs
> dm_dp_mst_connector_funcs = {
>  	.early_unregister = amdgpu_dm_mst_connector_early_unregister,
>  };
>  
> +bool is_virtual_dpcd(struct drm_dp_mst_port *port)
> +{
> +	struct drm_dp_mst_port *downstream_port;
> +
> +	if (!port)
> +		return false;
> +
> +	if (port->port_num >= 8 &&
> +		port->dpcd_rev >= DP_DPCD_REV_14) {
> +		/* Virtual DP Sink (Internal Display Panel) */
> +		return true;
> +	} else if (port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV &&
> +			!port->mcs &&
> +			port->ldps &&
> +			port->dpcd_rev >= DP_DPCD_REV_14) {
> +		/* DP-to-HDMI Protocol Converter */
> +		return true;
> +	} else if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
> +			port->mstb &&
> +			port->dpcd_rev >= DP_DPCD_REV_14) {
> +		/* DP-to-DP */
> +		if (port->mstb->num_ports == 2) {
> +			list_for_each_entry(downstream_port, &port->mstb-
> >ports, next) {
> +				if (!downstream_port->input &&
> +						downstream_port->pdt ==
> DP_PEER_DEVICE_SST_SINK)
> +					return true;
> +			}
> +		}
> +	}
> +	return false;
> +}
> +
> +bool synaptix_workaround(struct amdgpu_dm_connector *aconnector)
> +{
> +	struct drm_dp_mst_port *port = aconnector->port;
> +	struct dc_link *link = aconnector->dc_sink->link;
> +	u8 down_stream_port_data;
> +
> +	if (port->mgr->mst_primary == port->parent &&
> +			link->dpcd_caps.branch_dev_id == 0x90CC24 &&
> +			link->dpcd_caps.dpcd_rev.raw >= DP_DPCD_REV_14) {
> +		drm_dp_mst_dpcd_read(&port->aux, DP_DOWNSTREAMPORT_PRESENT,
> &down_stream_port_data, 1);
> +		if ((down_stream_port_data & 7) != 3)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
> +static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector
> *aconnector)
> +{
> +	u8 upstream_dsc_caps[16] = { 0 };
> +	u8 endpoint_dsc_caps[16] = { 0 };
> +	u8 endpoint_fec_caps = 0;
> +	struct dc_sink *dc_sink = aconnector->dc_sink;
> +	struct drm_dp_mst_port *output_port = aconnector->port;
> +	struct drm_dp_mst_port *immediate_upstream_port;
> +	struct drm_dp_mst_port *fec_port;
> +
> +	if (aconnector->port && aconnector->port->parent)
> +		immediate_upstream_port = aconnector->port->parent-
> >port_parent;
> +	else
> +		immediate_upstream_port = NULL;
> +
> +	fec_port = immediate_upstream_port;
> +	while (fec_port) {
> +		if (!fec_port->fec_capable)
> +			return false;
> +
> +		fec_port = fec_port->parent->port_parent;
> +	}
> +
> +	if (immediate_upstream_port)
> +		drm_dp_mst_dpcd_read(&immediate_upstream_port->aux,
> DP_DSC_SUPPORT, upstream_dsc_caps, 16);
> +	drm_dp_mst_dpcd_read(&output_port->aux, DP_DSC_SUPPORT,
> endpoint_dsc_caps, 16);
> +	drm_dp_mst_dpcd_read(&output_port->aux, DP_FEC_CAPABILITY,
> &endpoint_fec_caps, 1);
> +
> +	if (is_virtual_dpcd(immediate_upstream_port)
> +			&& (upstream_dsc_caps[0] & 0x2) /* DSC passthrough
> capability */
> +			&& (endpoint_fec_caps & DP_FEC_CAPABLE)
> +			&& (endpoint_dsc_caps[0] &
> DP_DSC_DECOMPRESSION_IS_SUPPORTED)) {
> +		/* Enpoint decompression with DP-to-DP peer device */
> +		if (!dc_dsc_parse_dsc_dpcd(endpoint_dsc_caps, NULL, &dc_sink-
> >sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = false;
> +	} else if (is_virtual_dpcd(immediate_upstream_port)) {
> +		/* Virtual DPCD decompression with DP-to-DP peer device */
> +		if (!dc_dsc_parse_dsc_dpcd(upstream_dsc_caps, NULL, &dc_sink-
> >sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
> +	} else if (is_virtual_dpcd(output_port)) {
> +		/* Virtual DPCD decompression with DP-to-HDMI or Virtual DP
> Sink */
> +		if (!dc_dsc_parse_dsc_dpcd(endpoint_dsc_caps, NULL, &dc_sink-
> >sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
> +	} else if (synaptix_workaround(aconnector)) {
> +		/* Synaptix workaround */
> +		aconnector = dc_sink->link->priv;
> +		drm_dp_dpcd_read(&aconnector->dm_dp_aux.aux, DP_DSC_SUPPORT,
> upstream_dsc_caps, 16);
> +		if (!dc_dsc_parse_dsc_dpcd(upstream_dsc_caps, NULL, &dc_sink-
> >sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
> +	} else {
> +		return false;
> +	}
> +
> +	return true;
> +}
> +#endif
> +
>  static int dm_dp_mst_get_modes(struct drm_connector *connector)
>  {
>  	struct amdgpu_dm_connector *aconnector =
> to_amdgpu_dm_connector(connector);
> @@ -231,10 +346,16 @@ static int dm_dp_mst_get_modes(struct drm_connector
> *connector)
>  		/* dc_link_add_remote_sink returns a new reference */
>  		aconnector->dc_sink = dc_sink;
>  
> -		if (aconnector->dc_sink)
> +		if (aconnector->dc_sink) {
>  			amdgpu_dm_update_freesync_caps(
>  					connector, aconnector->edid);
>  
> +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
> +			if (!validate_dsc_caps_on_connector(aconnector))
> +				memset(&aconnector->dc_sink->sink_dsc_caps,
> +				       0, sizeof(aconnector->dc_sink-
> >sink_dsc_caps));
> +#endif
> +		}
>  	}
>  
>  	drm_connector_update_edid_property(
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
> index 2da851b40042..8de3d8c30f8d 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
> @@ -32,4 +32,7 @@ struct amdgpu_dm_connector;
>  void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
>  				       struct amdgpu_dm_connector
> *aconnector);
>  
> +bool is_virtual_dpcd(struct drm_dp_mst_port *port);
> +bool synaptix_workaround(struct amdgpu_dm_connector *aconnector);
> +
>  #endif
Kazlauskas, Nicholas Aug. 21, 2019, 6:14 p.m. UTC | #2
On 8/20/19 3:12 PM, David Francis wrote:
> The first step in MST DSC is checking MST endpoints
> to see how DSC can be enabled
> 
> Case 1: DP-to-DP peer device
> if the branch immediately upstream has
>   - PDT = DP_PEER_DEVICE_DP_MST_BRANCHING (2)
>   - DPCD rev. >= DP 1.4
>   - Exactly one input and one output
>   - The output has PDT = DP_PEER_DEVICE_SST_SINK (3)
> 
> In this case, DSC could be possible either on the endpoint
> or the peer device. Prefer the endpoint, which is possible if
>   - The endpoint has DP_DSC_DECOMPRESSION_IS_SUPPORTED bit set
>   - The endpoint has DP_FEC_CAPABLE bit set
>   - The peer device has DSC_PASSTHROUGH_CAPABILITY bit set (from DP v2.0)
> 
> Otherwise, use the peer device
> 
> Case 2: DP-to-HDMI peer device
> If the output port has
>   - PDT = DP_PEER_DEVICE_DP_LEGACY_CONV (4)
>   - DPCD rev. >= DP 1.4
>   - LDPS = true
>   - MCS = false
> 
> In this case, DSC can only be attempted on the peer device
> (the output port)
> 
> Case 3: Virtual DP Sink (Internal Display Panel)
> If the output port has
>   - DPCD rev. >= DP 1.4
>   - port_num >= 8
> 
> In this case, DSC can only be attempted on the peer device
> (the output port)
> 
> Case 4: Synaptix Workaround
> If the output has
>   - link DPCD rev. >= DP 1.4
>   - link branch_dev_id = 0x90CC24 (Synaptix)
>   - There is exactly one branch device between the link and output
> 
> In this case, DSC can be attempted, but only using the *link*
> aux device's caps. This is a quirk.
> 
> Test for these cases as modes are enumerated for an MST endpoint.
> We cannot do this during link attach because the dc_sink object
> will not have been created yet
> 
> If no DSC is attempted, zero the DSC caps
> 
> Cc: Wenjing Liu <Wenjing.Liu@amd.com>
> Cc: Nikola Cornij <Nikola.Cornij@amd.com>
> Signed-off-by: David Francis <David.Francis@amd.com>

Questions and comments below...

> ---
>   .../display/amdgpu_dm/amdgpu_dm_mst_types.c   | 123 +++++++++++++++++-
>   .../display/amdgpu_dm/amdgpu_dm_mst_types.h   |   3 +
>   2 files changed, 125 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> index 16218a202b59..58571844f6d5 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
> @@ -25,6 +25,7 @@
>   
>   #include <linux/version.h>
>   #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_dp_mst_helper.h>
>   #include "dm_services.h"
>   #include "amdgpu.h"
>   #include "amdgpu_dm.h"
> @@ -189,6 +190,120 @@ static const struct drm_connector_funcs dm_dp_mst_connector_funcs = {
>   	.early_unregister = amdgpu_dm_mst_connector_early_unregister,
>   };
>   
> +bool is_virtual_dpcd(struct drm_dp_mst_port *port)
> +{
> +	struct drm_dp_mst_port *downstream_port;
> +
> +	if (!port)
> +		return false;
> +
> +	if (port->port_num >= 8 &&
> +		port->dpcd_rev >= DP_DPCD_REV_14) {

All these if statements should be aligned if possible. That's just 
formatting nitpicks though.

> +		/* Virtual DP Sink (Internal Display Panel) */
> +		return true;
> +	} else if (port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV &&
> +			!port->mcs &&
> +			port->ldps &&
> +			port->dpcd_rev >= DP_DPCD_REV_14) {
> +		/* DP-to-HDMI Protocol Converter */
> +		return true;
> +	} else if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
> +			port->mstb &&
> +			port->dpcd_rev >= DP_DPCD_REV_14) {
> +		/* DP-to-DP */
> +		if (port->mstb->num_ports == 2) {

Can probably merge this if condition into the else if above. Will help 
cut down on line length below.

> +			list_for_each_entry(downstream_port, &port->mstb->ports, next) {
> +				if (!downstream_port->input &&
> +						downstream_port->pdt == DP_PEER_DEVICE_SST_SINK)
> +					return true;
> +			}
> +		}
> +	}
> +	return false;
> +} > +
> +bool synaptix_workaround(struct amdgpu_dm_connector *aconnector)

This probably needs a better name. This isn't applying a workaround for 
a specific device but returning true if it is a specific device.

> +{
> +	struct drm_dp_mst_port *port = aconnector->port;
> +	struct dc_link *link = aconnector->dc_sink->link;
> +	u8 down_stream_port_data;
> +
> +	if (port->mgr->mst_primary == port->parent &&
> +			link->dpcd_caps.branch_dev_id == 0x90CC24 &&
> +			link->dpcd_caps.dpcd_rev.raw >= DP_DPCD_REV_14) {
> +		drm_dp_mst_dpcd_read(&port->aux, DP_DOWNSTREAMPORT_PRESENT, &down_stream_port_data, 1);
> +		if ((down_stream_port_data & 7) != 3)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
> +static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector)
> +{
> +	u8 upstream_dsc_caps[16] = { 0 };
> +	u8 endpoint_dsc_caps[16] = { 0 };
> +	u8 endpoint_fec_caps = 0;
> +	struct dc_sink *dc_sink = aconnector->dc_sink;
> +	struct drm_dp_mst_port *output_port = aconnector->port;
> +	struct drm_dp_mst_port *immediate_upstream_port;
> +	struct drm_dp_mst_port *fec_port;
> +
> +	if (aconnector->port && aconnector->port->parent)
> +		immediate_upstream_port = aconnector->port->parent->port_parent;
> +	else
> +		immediate_upstream_port = NULL;
> +
> +	fec_port = immediate_upstream_port;
> +	while (fec_port) {
> +		if (!fec_port->fec_capable)
> +			return false;
> +
> +		fec_port = fec_port->parent->port_parent;
> +	}
> +
> +	if (immediate_upstream_port)
> +		drm_dp_mst_dpcd_read(&immediate_upstream_port->aux, DP_DSC_SUPPORT, upstream_dsc_caps, 16);
> +	drm_dp_mst_dpcd_read(&output_port->aux, DP_DSC_SUPPORT, endpoint_dsc_caps, 16);
> +	drm_dp_mst_dpcd_read(&output_port->aux, DP_FEC_CAPABILITY, &endpoint_fec_caps, 1);
> +
> +	if (is_virtual_dpcd(immediate_upstream_port)
> +			&& (upstream_dsc_caps[0] & 0x2) /* DSC passthrough capability */
> +			&& (endpoint_fec_caps & DP_FEC_CAPABLE)
> +			&& (endpoint_dsc_caps[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED)) {
> +		/* Enpoint decompression with DP-to-DP peer device */
> +		if (!dc_dsc_parse_dsc_dpcd(endpoint_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = false;
> +	} else if (is_virtual_dpcd(immediate_upstream_port)) {
> +		/* Virtual DPCD decompression with DP-to-DP peer device */
> +		if (!dc_dsc_parse_dsc_dpcd(upstream_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
> +	} else if (is_virtual_dpcd(output_port)) {
> +		/* Virtual DPCD decompression with DP-to-HDMI or Virtual DP Sink */
> +		if (!dc_dsc_parse_dsc_dpcd(endpoint_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
> +	} else if (synaptix_workaround(aconnector)) {
> +		/* Synaptix workaround */
> +		aconnector = dc_sink->link->priv;
> +		drm_dp_dpcd_read(&aconnector->dm_dp_aux.aux, DP_DSC_SUPPORT, upstream_dsc_caps, 16);
> +		if (!dc_dsc_parse_dsc_dpcd(upstream_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
> +			return false;
> +
> +		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
> +	} else {
> +		return false;
> +	}
> +
> +	return true;
> +}
> +#endif

Should this be common code instead as a dp mst helper for determining 
caps? Other than setting the dc sink stuff the rest looks like it could 
be common but I'm not overly familiar with the architecture.

> +
>   static int dm_dp_mst_get_modes(struct drm_connector *connector)
>   {
>   	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
> @@ -231,10 +346,16 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
>   		/* dc_link_add_remote_sink returns a new reference */
>   		aconnector->dc_sink = dc_sink;
>   
> -		if (aconnector->dc_sink)
> +		if (aconnector->dc_sink) {
>   			amdgpu_dm_update_freesync_caps(
>   					connector, aconnector->edid);
>   
> +#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
> +			if (!validate_dsc_caps_on_connector(aconnector))
> +				memset(&aconnector->dc_sink->sink_dsc_caps,
> +				       0, sizeof(aconnector->dc_sink->sink_dsc_caps));
> +#endif
> +		}
>   	}
>   
>   	drm_connector_update_edid_property(
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
> index 2da851b40042..8de3d8c30f8d 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
> @@ -32,4 +32,7 @@ struct amdgpu_dm_connector;
>   void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
>   				       struct amdgpu_dm_connector *aconnector);
>   
> +bool is_virtual_dpcd(struct drm_dp_mst_port *port);
> +bool synaptix_workaround(struct amdgpu_dm_connector *aconnector);

These shouldn't be defined in the header if they're only going to be 
used here, they should just be static.

> +
>   #endif
> 

Nicholas Kazlauskas
diff mbox series

Patch

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 16218a202b59..58571844f6d5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -25,6 +25,7 @@ 
 
 #include <linux/version.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_dp_mst_helper.h>
 #include "dm_services.h"
 #include "amdgpu.h"
 #include "amdgpu_dm.h"
@@ -189,6 +190,120 @@  static const struct drm_connector_funcs dm_dp_mst_connector_funcs = {
 	.early_unregister = amdgpu_dm_mst_connector_early_unregister,
 };
 
+bool is_virtual_dpcd(struct drm_dp_mst_port *port)
+{
+	struct drm_dp_mst_port *downstream_port;
+
+	if (!port)
+		return false;
+
+	if (port->port_num >= 8 &&
+		port->dpcd_rev >= DP_DPCD_REV_14) {
+		/* Virtual DP Sink (Internal Display Panel) */
+		return true;
+	} else if (port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV &&
+			!port->mcs &&
+			port->ldps &&
+			port->dpcd_rev >= DP_DPCD_REV_14) {
+		/* DP-to-HDMI Protocol Converter */
+		return true;
+	} else if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
+			port->mstb &&
+			port->dpcd_rev >= DP_DPCD_REV_14) {
+		/* DP-to-DP */
+		if (port->mstb->num_ports == 2) {
+			list_for_each_entry(downstream_port, &port->mstb->ports, next) {
+				if (!downstream_port->input &&
+						downstream_port->pdt == DP_PEER_DEVICE_SST_SINK)
+					return true;
+			}
+		}
+	}
+	return false;
+}
+
+bool synaptix_workaround(struct amdgpu_dm_connector *aconnector)
+{
+	struct drm_dp_mst_port *port = aconnector->port;
+	struct dc_link *link = aconnector->dc_sink->link;
+	u8 down_stream_port_data;
+
+	if (port->mgr->mst_primary == port->parent &&
+			link->dpcd_caps.branch_dev_id == 0x90CC24 &&
+			link->dpcd_caps.dpcd_rev.raw >= DP_DPCD_REV_14) {
+		drm_dp_mst_dpcd_read(&port->aux, DP_DOWNSTREAMPORT_PRESENT, &down_stream_port_data, 1);
+		if ((down_stream_port_data & 7) != 3)
+			return true;
+	}
+	return false;
+}
+
+#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
+static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnector)
+{
+	u8 upstream_dsc_caps[16] = { 0 };
+	u8 endpoint_dsc_caps[16] = { 0 };
+	u8 endpoint_fec_caps = 0;
+	struct dc_sink *dc_sink = aconnector->dc_sink;
+	struct drm_dp_mst_port *output_port = aconnector->port;
+	struct drm_dp_mst_port *immediate_upstream_port;
+	struct drm_dp_mst_port *fec_port;
+
+	if (aconnector->port && aconnector->port->parent)
+		immediate_upstream_port = aconnector->port->parent->port_parent;
+	else
+		immediate_upstream_port = NULL;
+
+	fec_port = immediate_upstream_port;
+	while (fec_port) {
+		if (!fec_port->fec_capable)
+			return false;
+
+		fec_port = fec_port->parent->port_parent;
+	}
+
+	if (immediate_upstream_port)
+		drm_dp_mst_dpcd_read(&immediate_upstream_port->aux, DP_DSC_SUPPORT, upstream_dsc_caps, 16);
+	drm_dp_mst_dpcd_read(&output_port->aux, DP_DSC_SUPPORT, endpoint_dsc_caps, 16);
+	drm_dp_mst_dpcd_read(&output_port->aux, DP_FEC_CAPABILITY, &endpoint_fec_caps, 1);
+
+	if (is_virtual_dpcd(immediate_upstream_port)
+			&& (upstream_dsc_caps[0] & 0x2) /* DSC passthrough capability */
+			&& (endpoint_fec_caps & DP_FEC_CAPABLE)
+			&& (endpoint_dsc_caps[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED)) {
+		/* Enpoint decompression with DP-to-DP peer device */
+		if (!dc_dsc_parse_dsc_dpcd(endpoint_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
+			return false;
+
+		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = false;
+	} else if (is_virtual_dpcd(immediate_upstream_port)) {
+		/* Virtual DPCD decompression with DP-to-DP peer device */
+		if (!dc_dsc_parse_dsc_dpcd(upstream_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
+			return false;
+
+		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
+	} else if (is_virtual_dpcd(output_port)) {
+		/* Virtual DPCD decompression with DP-to-HDMI or Virtual DP Sink */
+		if (!dc_dsc_parse_dsc_dpcd(endpoint_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
+			return false;
+
+		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
+	} else if (synaptix_workaround(aconnector)) {
+		/* Synaptix workaround */
+		aconnector = dc_sink->link->priv;
+		drm_dp_dpcd_read(&aconnector->dm_dp_aux.aux, DP_DSC_SUPPORT, upstream_dsc_caps, 16);
+		if (!dc_dsc_parse_dsc_dpcd(upstream_dsc_caps, NULL, &dc_sink->sink_dsc_caps.dsc_dec_caps))
+			return false;
+
+		dc_sink->sink_dsc_caps.is_virtual_dpcd_dsc = true;
+	} else {
+		return false;
+	}
+
+	return true;
+}
+#endif
+
 static int dm_dp_mst_get_modes(struct drm_connector *connector)
 {
 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
@@ -231,10 +346,16 @@  static int dm_dp_mst_get_modes(struct drm_connector *connector)
 		/* dc_link_add_remote_sink returns a new reference */
 		aconnector->dc_sink = dc_sink;
 
-		if (aconnector->dc_sink)
+		if (aconnector->dc_sink) {
 			amdgpu_dm_update_freesync_caps(
 					connector, aconnector->edid);
 
+#ifdef CONFIG_DRM_AMD_DC_DSC_SUPPORT
+			if (!validate_dsc_caps_on_connector(aconnector))
+				memset(&aconnector->dc_sink->sink_dsc_caps,
+				       0, sizeof(aconnector->dc_sink->sink_dsc_caps));
+#endif
+		}
 	}
 
 	drm_connector_update_edid_property(
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
index 2da851b40042..8de3d8c30f8d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
@@ -32,4 +32,7 @@  struct amdgpu_dm_connector;
 void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
 				       struct amdgpu_dm_connector *aconnector);
 
+bool is_virtual_dpcd(struct drm_dp_mst_port *port);
+bool synaptix_workaround(struct amdgpu_dm_connector *aconnector);
+
 #endif