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