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 |
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
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 --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
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(-)