From 2c8bc91488fc57438c43b3bb19deb7fdbc1e5119 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 20 Nov 2019 17:35:09 +0000 Subject: drm/dp_mst: fix multiple frees of tx->bytes Currently tx->bytes is being freed r->num_transactions number of times because tx is not being set correctly. Fix this by setting tx to &r->transactions[i] so that the correct objects are being freed on each loop iteration. Addresses-Coverity: ("Double free") Fixes: 2f015ec6eab6 ("drm/dp_mst: Add sideband down request tracing + selftests") Signed-off-by: Colin Ian King Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20191120173509.347490-1-colin.king@canonical.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index b854a422a523..1437bc46368b 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -504,8 +504,10 @@ drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw, } if (failed) { - for (i = 0; i < r->num_transactions; i++) + for (i = 0; i < r->num_transactions; i++) { + tx = &r->transactions[i]; kfree(tx->bytes); + } return -ENOMEM; } -- cgit From f79489074c59a5fd6bc35b21ca9911237993a045 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Wed, 28 Aug 2019 20:09:44 -0400 Subject: drm/dp_mst: Clear all payload id tables downstream when initializing It seems that on certain MST hubs, namely the CableMatters USB-C 2x DP hub, using the DP_PAYLOAD_ALLOCATE_SET and DP_PAYLOAD_TABLE_UPDATE_STATUS register ranges to clear any pre-existing payload allocations on the hub isn't always enough to reset things if the source device has been reset unexpectedly. Or at least, that's the current running theory. The precise behavior appears to be that when the source device gets reset unexpectedly, the hub begins reporting an available_pbn value of 0 for all of its ports. This is a bit inconsistent with the our theory, since this seems to happen even if previously set PBN allocations should have resulted in a non-zero available_pbn value. So, it's possible that something else may be going on here. Strangely though, sending a CLEAR_PAYLOAD_ID_TABLE broadcast request when initializing the MST topology seems to bring things into working order and make available_pbn work again. Since this is a pretty safe solution, let's go ahead and implement it. Changes since v1: * Change indenting on drm_dp_send_clear_payload_id_table() prototype * Remove some braces in drm_dp_send_clear_payload_id_table() * Reorganize some variable declarations in drm_dp_send_clear_payload_id_table() * Don't forget to handle DP_CLEAR_PAYLOAD_ID_TABLE in drm_dp_sideband_parse_reply() * Move drm_dp_send_clear_payload_id_table() call into drm_dp_mst_link_probe_work(), since we can't send sideband messages while under lock in drm_dp_mst_topology_mgr_set_mst() * Change commit message Acked-by: Daniel Vetter Signed-off-by: Sean Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20190829000944.20722-1-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 63 +++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 1437bc46368b..87fc44895d83 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -63,6 +63,11 @@ static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr, static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb); + +static void +drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb); + static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *port); @@ -939,6 +944,8 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, case DP_POWER_DOWN_PHY: case DP_POWER_UP_PHY: return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg); + case DP_CLEAR_PAYLOAD_ID_TABLE: + return true; /* since there's nothing to parse */ default: DRM_ERROR("Got unknown reply 0x%02x (%s)\n", msg->req_type, drm_dp_mst_req_type_str(msg->req_type)); @@ -1037,6 +1044,15 @@ static int build_link_address(struct drm_dp_sideband_msg_tx *msg) return 0; } +static int build_clear_payload_id_table(struct drm_dp_sideband_msg_tx *msg) +{ + struct drm_dp_sideband_msg_req_body req; + + req.req_type = DP_CLEAR_PAYLOAD_ID_TABLE; + drm_dp_encode_sideband_req(&req, msg); + return 0; +} + static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num) { struct drm_dp_sideband_msg_req_body req; @@ -2166,8 +2182,12 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work) struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work); struct drm_dp_mst_branch *mstb; int ret; + bool clear_payload_id_table; mutex_lock(&mgr->lock); + clear_payload_id_table = !mgr->payload_id_table_cleared; + mgr->payload_id_table_cleared = true; + mstb = mgr->mst_primary; if (mstb) { ret = drm_dp_mst_topology_try_get_mstb(mstb); @@ -2175,10 +2195,24 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work) mstb = NULL; } mutex_unlock(&mgr->lock); - if (mstb) { - drm_dp_check_and_send_link_address(mgr, mstb); - drm_dp_mst_topology_put_mstb(mstb); + if (!mstb) + return; + + /* + * Certain branch devices seem to incorrectly report an available_pbn + * of 0 on downstream sinks, even after clearing the + * DP_PAYLOAD_ALLOCATE_* registers in + * drm_dp_mst_topology_mgr_set_mst(). Namely, the CableMatters USB-C + * 2x DP hub. Sending a CLEAR_PAYLOAD_ID_TABLE message seems to make + * things work again. + */ + if (clear_payload_id_table) { + DRM_DEBUG_KMS("Clearing payload ID table\n"); + drm_dp_send_clear_payload_id_table(mgr, mstb); } + + drm_dp_check_and_send_link_address(mgr, mstb); + drm_dp_mst_topology_put_mstb(mstb); } static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, @@ -2471,6 +2505,28 @@ out: kfree(txmsg); } +void drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb) +{ + struct drm_dp_sideband_msg_tx *txmsg; + int len, ret; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) + return; + + txmsg->dst = mstb; + len = build_clear_payload_id_table(txmsg); + + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(mstb, txmsg); + if (ret > 0 && txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) + DRM_DEBUG_KMS("clear payload table id nak received\n"); + + kfree(txmsg); +} + static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb, @@ -3062,6 +3118,7 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms mgr->payload_mask = 0; set_bit(0, &mgr->payload_mask); mgr->vcpi_mask = 0; + mgr->payload_id_table_cleared = false; } out_unlock: -- cgit From 64e62bdf04ab8529f45ed0a85122c703035dec3a Mon Sep 17 00:00:00 2001 From: Wayne Lin Date: Thu, 5 Dec 2019 17:00:43 +0800 Subject: drm/dp_mst: Remove VCPI while disabling topology mgr [Why] This patch is trying to address the issue observed when hotplug DP daisy chain monitors. e.g. src-mstb-mstb-sst -> src (unplug) mstb-mstb-sst -> src-mstb-mstb-sst (plug in again) Once unplug a DP MST capable device, driver will call drm_dp_mst_topology_mgr_set_mst() to disable MST. In this function, it cleans data of topology manager while disabling mst_state. However, it doesn't clean up the proposed_vcpis of topology manager. If proposed_vcpi is not reset, once plug in MST daisy chain monitors later, code will fail at checking port validation while trying to allocate payloads. When MST capable device is plugged in again and try to allocate payloads by calling drm_dp_update_payload_part1(), this function will iterate over all proposed virtual channels to see if any proposed VCPI's num_slots is greater than 0. If any proposed VCPI's num_slots is greater than 0 and the port which the specific virtual channel directed to is not in the topology, code then fails at the port validation. Since there are stale VCPI allocations from the previous topology enablement in proposed_vcpi[], code will fail at port validation and reurn EINVAL. [How] Clean up the data of stale proposed_vcpi[] and reset mgr->proposed_vcpis to NULL while disabling mst in drm_dp_mst_topology_mgr_set_mst(). Changes since v1: *Add on more details in commit message to describe the issue which the patch is trying to fix Signed-off-by: Wayne Lin [added cc to stable] Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20191205090043.7580-1-Wayne.Lin@amd.com Cc: # v3.17+ --- drivers/gpu/drm/drm_dp_mst_topology.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 87fc44895d83..1770754bcd4a 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -3054,6 +3054,7 @@ static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8 dp_link_count) int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state) { int ret = 0; + int i = 0; struct drm_dp_mst_branch *mstb = NULL; mutex_lock(&mgr->lock); @@ -3114,10 +3115,22 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms /* this can fail if the device is gone */ drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); ret = 0; + mutex_lock(&mgr->payload_lock); memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); mgr->payload_mask = 0; set_bit(0, &mgr->payload_mask); + for (i = 0; i < mgr->max_payloads; i++) { + struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i]; + + if (vcpi) { + vcpi->vcpi = 0; + vcpi->num_slots = 0; + } + mgr->proposed_vcpis[i] = NULL; + } mgr->vcpi_mask = 0; + mutex_unlock(&mgr->payload_lock); + mgr->payload_id_table_cleared = false; } -- cgit From dc48529fb14ee8450705c00d91f4dcc155e1c2cb Mon Sep 17 00:00:00 2001 From: David Francis Date: Wed, 21 Aug 2019 10:33:26 -0400 Subject: drm/dp_mst: Add PBN calculation for DSC modes With DSC, bpp can be fractional in multiples of 1/16. Change drm_dp_calc_pbn_mode to reflect this, adding a new parameter bool dsc. When this parameter is true, treat the bpp parameter as having units not of bits per pixel, but 1/16 of a bit per pixel v2: Don't add separate function for this v3: In the equation divide bpp by 16 as it is expected not to leave any remainder v4: Added DSC test parameters for selftest Reviewed-by: Manasi Navare Reviewed-by: Lyude Paul Reviewed-by: Harry Wentland Signed-off-by: David Francis Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index e68d23043973..2443532341d0 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4415,10 +4415,11 @@ EXPORT_SYMBOL(drm_dp_check_act_status); * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode. * @clock: dot clock for the mode * @bpp: bpp for the mode. + * @dsc: DSC mode. If true, bpp has units of 1/16 of a bit per pixel * * This uses the formula in the spec to calculate the PBN value for a mode. */ -int drm_dp_calc_pbn_mode(int clock, int bpp) +int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc) { /* * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 @@ -4429,7 +4430,16 @@ int drm_dp_calc_pbn_mode(int clock, int bpp) * peak_kbps *= (1006/1000) * peak_kbps *= (64/54) * peak_kbps *= 8 convert to bytes + * + * If the bpp is in units of 1/16, further divide by 16. Put this + * factor in the numerator rather than the denominator to avoid + * integer overflow */ + + if (dsc) + return DIV_ROUND_UP_ULL(mul_u32_u32(clock * (bpp / 16), 64 * 1006), + 8 * 54 * 1000 * 1000); + return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006), 8 * 54 * 1000 * 1000); } -- cgit From a3c2b0ffc007e5d98f5313ed951fff092535fb6d Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 6 Jun 2019 11:20:10 -0400 Subject: drm/dp_mst: Parse FEC capability on MST ports As of DP1.4, ENUM_PATH_RESOURCES returns a bit indicating if FEC can be supported up to that point in the MST network. The bit is the first byte of the ENUM_PATH_RESOURCES ack reply, bottom-most bit (refer to section 2.11.9.4 of DP standard, v1.4) That value is needed for FEC and DSC support Store it on drm_dp_mst_port Reviewed-by: Lyude Paul Reviewed-by: Harry Wentland Signed-off-by: David Francis Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 2443532341d0..77d95e2de897 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -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; @@ -2951,6 +2952,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; } } -- cgit From b1dee9a716b532566570052686144bfbba62aaf6 Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 25 Jul 2019 11:47:46 -0400 Subject: drm/dp_mst: Fill branch->num_ports This field on drm_dp_mst_branch was never filled It is initialized to zero when the port is kzallocced. When a port is added to the list, increment num_ports, and when a port is removed from the list, decrement num_ports. v2: remember to decrement on port removal v3: don't explicitly init to 0 v4: move decrement of num_ports to unlink_port function Reviewed-by: Lyude Paul Reviewed-by: Harry Wentland Signed-off-by: David Francis Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 77d95e2de897..5b3dfceab365 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -2175,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); @@ -2274,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); } -- cgit From c2bc1b6eabe65d6bf26a892d803907dca9097311 Mon Sep 17 00:00:00 2001 From: David Francis Date: Mon, 26 Aug 2019 09:50:28 -0400 Subject: drm/dp_mst: Add helpers for MST DSC and virtual DPCD aux 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 v4: - Check DSC capable MST sink connected directly to the device. - Check branch's port_parent to be set Cc: Lyude Paul Reviewed-by: Wenjing Liu Signed-off-by: David Francis Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 143 ++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5b3dfceab365..2b20caa4b7e3 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -5076,3 +5076,146 @@ 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; + 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; + + /* + * 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); -- cgit From 5b03f9d86880711441c5681244d352212460a595 Mon Sep 17 00:00:00 2001 From: Mikita Lipski Date: Fri, 20 Sep 2019 15:44:56 -0400 Subject: drm/dp_mst: Add new quirk for Synaptics MST hubs Synaptics DP1.4 hubs (BRANCH_ID 0x90CC24) do not support virtual DPCD registers, but do support DSC. The DSC caps can be read from the physical aux, like in SST DSC. These hubs have many different DEVICE_IDs. Add a new quirk to detect this case. v2: Fix error when checking return of drm_dp_read_desc Reviewed-by: Wenjing Liu Reviewed-by: Lyude Paul Signed-off-by: David Francis Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 2b20caa4b7e3..480c71a00cf2 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -5148,6 +5148,7 @@ 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; @@ -5200,6 +5201,32 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port) 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 - -- cgit From 1c6c1cb5afc77cc8afbe563937c3bd1a41172459 Mon Sep 17 00:00:00 2001 From: Mikita Lipski Date: Thu, 14 Nov 2019 16:24:29 -0500 Subject: drm/dp_mst: Manually overwrite PBN divider for calculating timeslots [why] For DSC case we cannot use topology manager's PBN divider variable. The default divider does not take FEC into account. Therefore the driver has to calculate its own divider based on the link rate and lane count its handling, as it is hw specific. [how] Pass pbn_div as an argument, which is used if its more than zero, otherwise default topology manager's pbn_div will be used. Reviewed-by: Lyude Paul Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 480c71a00cf2..90201827781d 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4093,6 +4093,7 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, * @mgr: MST topology manager for the port * @port: port to find vcpi slots for * @pbn: bandwidth required for the mode in PBN + * @pbn_div: divider for DSC mode that takes FEC into account * * Allocates VCPI slots to @port, replacing any previous VCPI allocations it * may have had. Any atomic drivers which support MST must call this function @@ -4119,7 +4120,8 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr, */ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port, int pbn) + struct drm_dp_mst_port *port, int pbn, + int pbn_div) { struct drm_dp_mst_topology_state *topology_state; struct drm_dp_vcpi_allocation *pos, *vcpi = NULL; @@ -4152,7 +4154,10 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, if (!vcpi) prev_slots = 0; - req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div); + if (pbn_div <= 0) + pbn_div = mgr->pbn_div; + + req_slots = DIV_ROUND_UP(pbn, pbn_div); DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n", port->connector->base.id, port->connector->name, -- cgit From 8afb7e6afadb36b160143794c1d2fdfbde189750 Mon Sep 17 00:00:00 2001 From: Mikita Lipski Date: Mon, 28 Oct 2019 17:33:32 -0400 Subject: drm/dp_mst: Add DSC enablement helpers to DRM Adding a helper function to be called by drivers outside of DRM to enable DSC on the MST ports. Function is called to recalculate VCPI allocation if DSC is enabled and raise the DSC flag to enable. In case of disabling DSC the flag is set to false and recalculation of VCPI slots is expected to be done in encoder's atomic_check. v2: squash separate functions into one and call it per port v3: Fix comment typos Reviewed-by: Lyude Paul Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 90201827781d..9f7e714c5182 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4789,6 +4789,67 @@ drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr, return 0; } +/** + * drm_dp_mst_atomic_enable_dsc - Set DSC Enable Flag to On/Off + * @state: Pointer to the new drm_atomic_state + * @port: Pointer to the affected MST Port + * @pbn: Newly recalculated bw required for link with DSC enabled + * @pbn_div: Divider to calculate correct number of pbn per slot + * @enable: Boolean flag to enable or disable DSC on the port + * + * This function enables DSC on the given Port + * by recalculating its vcpi from pbn provided + * and sets dsc_enable flag to keep track of which + * ports have DSC enabled + * + */ +int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state, + struct drm_dp_mst_port *port, + int pbn, int pbn_div, + bool enable) +{ + struct drm_dp_mst_topology_state *mst_state; + struct drm_dp_vcpi_allocation *pos; + bool found = false; + int vcpi = 0; + + mst_state = drm_atomic_get_mst_topology_state(state, port->mgr); + + if (IS_ERR(mst_state)) + return PTR_ERR(mst_state); + + list_for_each_entry(pos, &mst_state->vcpis, next) { + if (pos->port == port) { + found = true; + break; + } + } + + if (!found) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] Couldn't find VCPI allocation in mst state %p\n", + port, mst_state); + return -EINVAL; + } + + if (pos->dsc_enabled == enable) { + DRM_DEBUG_ATOMIC("[MST PORT:%p] DSC flag is already set to %d, returning %d VCPI slots\n", + port, enable, pos->vcpi); + vcpi = pos->vcpi; + } + + if (enable) { + vcpi = drm_dp_atomic_find_vcpi_slots(state, port->mgr, port, pbn, pbn_div); + DRM_DEBUG_ATOMIC("[MST PORT:%p] Enabling DSC flag, reallocating %d VCPI slots on the port\n", + port, vcpi); + if (vcpi < 0) + return -EINVAL; + } + + pos->dsc_enabled = enable; + + return vcpi; +} +EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc); /** * drm_dp_mst_atomic_check - Check that the new state of an MST topology in an * atomic update is valid -- cgit From cd82d82cbc0484e47918d3166f356c98f0066db8 Mon Sep 17 00:00:00 2001 From: Mikita Lipski Date: Wed, 6 Nov 2019 13:11:23 -0500 Subject: drm/dp_mst: Add branch bandwidth validation to MST atomic check [why] Adding PBN attribute to drm_dp_vcpi_allocation structure to keep track of how much bandwidth each Port requires. Adding drm_dp_mst_atomic_check_bw_limit to verify that state's bandwidth needs doesn't exceed available bandwidth. The funtion is called in drm_dp_mst_atomic_check after drm_dp_mst_atomic_check_topology_state to fully verify that the proposed topology is supported. v2: Fixing some typos and indenting v3: Return correct error enums if no bw space available Reviewed-by: Lyude Paul Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 66 +++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 9f7e714c5182..c7bea8a95797 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4125,7 +4125,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, { struct drm_dp_mst_topology_state *topology_state; struct drm_dp_vcpi_allocation *pos, *vcpi = NULL; - int prev_slots, req_slots; + int prev_slots, prev_bw, req_slots; topology_state = drm_atomic_get_mst_topology_state(state, mgr); if (IS_ERR(topology_state)) @@ -4136,6 +4136,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, if (pos->port == port) { vcpi = pos; prev_slots = vcpi->vcpi; + prev_bw = vcpi->pbn; /* * This should never happen, unless the driver tries @@ -4151,8 +4152,10 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, break; } } - if (!vcpi) + if (!vcpi) { prev_slots = 0; + prev_bw = 0; + } if (pbn_div <= 0) pbn_div = mgr->pbn_div; @@ -4162,6 +4165,9 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n", port->connector->base.id, port->connector->name, port, prev_slots, req_slots); + DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] [MST PORT:%p] PBN %d -> %d\n", + port->connector->base.id, port->connector->name, + port, prev_bw, pbn); /* Add the new allocation to the state */ if (!vcpi) { @@ -4174,6 +4180,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, list_add(&vcpi->next, &topology_state->vcpis); } vcpi->vcpi = req_slots; + vcpi->pbn = pbn; return req_slots; } @@ -4750,6 +4757,58 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj, kfree(mst_state); } +static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port, + struct drm_dp_mst_branch *branch) +{ + while (port->parent) { + if (port->parent == branch) + return true; + + if (port->parent->port_parent) + port = port->parent->port_parent; + else + break; + } + return false; +} + +static inline +int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch, + struct drm_dp_mst_topology_state *mst_state) +{ + struct drm_dp_mst_port *port; + struct drm_dp_vcpi_allocation *vcpi; + int pbn_limit = 0, pbn_used = 0; + + list_for_each_entry(port, &branch->ports, next) { + if (port->mstb) + if (drm_dp_mst_atomic_check_bw_limit(port->mstb, mst_state)) + return -ENOSPC; + + if (port->available_pbn > 0) + pbn_limit = port->available_pbn; + } + DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch has %d PBN available\n", + branch, pbn_limit); + + list_for_each_entry(vcpi, &mst_state->vcpis, next) { + if (!vcpi->pbn) + continue; + + if (drm_dp_mst_port_downstream_of_branch(vcpi->port, branch)) + pbn_used += vcpi->pbn; + } + DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch used %d PBN\n", + branch, pbn_used); + + if (pbn_used > pbn_limit) { + DRM_DEBUG_ATOMIC("[MST BRANCH:%p] No available bandwidth\n", + branch); + return -ENOSPC; + } + return 0; +} + static inline int drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_topology_state *mst_state) @@ -4881,6 +4940,9 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state) ret = drm_dp_mst_atomic_check_topology_state(mgr, mst_state); if (ret) break; + ret = drm_dp_mst_atomic_check_bw_limit(mgr->mst_primary, mst_state); + if (ret) + break; } return ret; -- cgit From 9e5b959036ff76dda397998d1185b0ec0ae58568 Mon Sep 17 00:00:00 2001 From: Mikita Lipski Date: Fri, 13 Dec 2019 10:29:48 -0500 Subject: drm/dp_mst: Rename drm_dp_mst_atomic_check_topology_state [why] drm_dp_mst_atomic_check_topology_state() should be renamed to reflect more specific type of check. Since it is verifying payload allocation limit it should be renamed into drm_dp_mst_atomic_check_vcpi_alloc_limit() Reviewed-by: Lyude Paul Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index c7bea8a95797..6a7078e538e5 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4810,8 +4810,8 @@ int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch, } static inline int -drm_dp_mst_atomic_check_topology_state(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_topology_state *mst_state) +drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state) { struct drm_dp_vcpi_allocation *vcpi; int avail_slots = 63, payload_count = 0; @@ -4937,7 +4937,7 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state) int i, ret = 0; for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) { - ret = drm_dp_mst_atomic_check_topology_state(mgr, mst_state); + ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state); if (ret) break; ret = drm_dp_mst_atomic_check_bw_limit(mgr->mst_primary, mst_state); -- cgit From 8ec046716ca8ee79a9d2699f78511c08753c2e56 Mon Sep 17 00:00:00 2001 From: Mikita Lipski Date: Sat, 16 Nov 2019 13:32:15 -0500 Subject: drm/dp_mst: Add helper to trigger modeset on affected DSC MST CRTCs [why] Whenever a connector on an MST network is changed or undergoes a modeset, the DSC configs for each stream on that topology will be recalculated. This can change their required bandwidth, requiring a full reprogramming, as though a modeset was performed, even if that stream did not change timing. [how] Adding helper to trigger modesets on MST DSC connectors by setting mode_changed flag on CRTCs in the same topology as affected connector v2: use drm_dp_mst_dsc_aux_for_port function to verify if the port is DSC capable v3: - added _must_check attribute - removed topology manager check - fix typos and indentations Reviewed-by: Lyude Paul Signed-off-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 6a7078e538e5..7d174ba78eba 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4848,6 +4848,67 @@ drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr, return 0; } +/** + * drm_dp_mst_add_affected_dsc_crtcs + * @state: Pointer to the new struct drm_dp_mst_topology_state + * @port: Port pointer of connector with new state + * + * Whenever there is a change in mst topology + * DSC configuration would have to be recalculated + * therefore we need to trigger modeset on all affected + * CRTCs in that topology + * + * See also: + * drm_dp_mst_atomic_enable_dsc() + */ +int drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr) +{ + struct drm_dp_mst_topology_state *mst_state; + struct drm_dp_vcpi_allocation *pos; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + + mst_state = drm_atomic_get_mst_topology_state(state, mgr); + + if (IS_ERR(mst_state)) + return -EINVAL; + + list_for_each_entry(pos, &mst_state->vcpis, next) { + + connector = pos->port->connector; + + if (!connector) + return -EINVAL; + + conn_state = drm_atomic_get_connector_state(state, connector); + + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + + crtc = conn_state->crtc; + + if (WARN_ON(!crtc)) + return -EINVAL; + + if (!drm_dp_mst_dsc_aux_for_port(pos->port)) + continue; + + crtc_state = drm_atomic_get_crtc_state(mst_state->base.state, crtc); + + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + DRM_DEBUG_ATOMIC("[MST MGR:%p] Setting mode_changed flag on CRTC %p\n", + mgr, crtc); + + crtc_state->mode_changed = true; + } + return 0; +} +EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs); + /** * drm_dp_mst_atomic_enable_dsc - Set DSC Enable Flag to On/Off * @state: Pointer to the new drm_atomic_state -- cgit From c908b1c4bb56c27fa6fbb48e2d3dbebdad366706 Mon Sep 17 00:00:00 2001 From: "David (Dingchen) Zhang" Date: Fri, 6 Dec 2019 17:56:37 -0500 Subject: drm: add dp helper to initialize remote aux channel. [why] We need to minimally initialize the remote aux channel, e.g. the crc work struct of remote aux to dump the sink's DPRX CRCs in MST setup. [how] Add helper that only initializes the crc work struct of the remote aux, hooke crc work queue to 'drm_dp_aux_crc_work'. Then call this helper in DP MST port initialization. This, plus David Francis' patch [1], fix the issue of MST remote aux DPCD CRCs read. [1] https://patchwork.kernel.org/patch/11217941/ Cc: Leo Li Cc: Harry Wentland Signed-off-by: David (Dingchen) Zhang Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 7d174ba78eba..fa0656697c72 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -2200,6 +2200,9 @@ drm_dp_mst_add_port(struct drm_device *dev, port->aux.dev = dev->dev; port->aux.is_remote = true; + /* initialize the MST downstream port's AUX crc work queue */ + drm_dp_remote_aux_init(&port->aux); + /* * Make sure the memory allocation for our parent branch stays * around until our own memory allocation is released -- cgit From 9edb435aed968a2c792f6d847f524587697edf37 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 8 Jan 2020 22:21:30 -0500 Subject: drm/dp_mst: fix documentation of drm_dp_mst_add_affected_dsc_crtcs the parameter is the mst manager, not the port. Reviewed-by: Mikita Lipski Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index fa0656697c72..5d3c1d379277 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4854,7 +4854,7 @@ drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr, /** * drm_dp_mst_add_affected_dsc_crtcs * @state: Pointer to the new struct drm_dp_mst_topology_state - * @port: Port pointer of connector with new state + * @mgr: MST topology manager * * Whenever there is a change in mst topology * DSC configuration would have to be recalculated -- cgit From 7b19914383fc008a6b51871f18da72cf9aa43cae Mon Sep 17 00:00:00 2001 From: José Roberto de Souza Date: Thu, 16 Jan 2020 17:58:34 -0800 Subject: drm/mst: Don't do atomic checks over disabled managers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a main MST port is disconnected drivers should call drm_dp_mst_topology_mgr_set_mst() disabling the MST manager, this function will set manager mst_primary to NULL and it will cause the crash bellow on the next atomic check when trying to access mst_primary->port. As there is no use in running checks over managers that are not active this patch will skip it. [ 305.616450] [drm:drm_dp_mst_atomic_check] [MST PORT:00000000cc2049e9] releases all VCPI slots [ 305.625085] [drm:drm_dp_mst_atomic_check] [MST PORT:00000000020ab43e] releases all VCPI slots [ 305.633729] [drm:drm_dp_mst_atomic_check] [MST MGR:00000000cdd467d4] mst state 00000000b67672eb VCPI avail=63 used=0 [ 305.644405] BUG: kernel NULL pointer dereference, address: 0000000000000030 [ 305.651448] #PF: supervisor read access in kernel mode [ 305.656640] #PF: error_code(0x0000) - not-present page [ 305.661807] PGD 0 P4D 0 [ 305.664396] Oops: 0000 [#1] PREEMPT SMP NOPTI [ 305.668789] CPU: 3 PID: 183 Comm: kworker/3:2 Not tainted 5.5.0-rc6+ #1404 [ 305.675703] Hardware name: Intel Corporation Ice Lake Client Platform/IceLake U DDR4 SODIMM PD RVP TLC, BIOS ICLSFWR1.R00.3201.A00.1905140358 05/14/2019 [ 305.689425] Workqueue: events drm_dp_delayed_destroy_work [ 305.694874] RIP: 0010:drm_dp_mst_atomic_check+0x138/0x2c0 [ 305.700306] Code: 00 00 00 41 29 d9 41 89 d8 4c 89 fa 4c 89 f1 48 c7 c6 b0 b1 34 82 bf 10 00 00 00 45 31 ed e8 3f 99 02 00 4d 8b bf 80 04 00 00 <49> 8b 47 30 49 8d 5f 30 4c 8d 60 e8 48 39 c3 74 35 49 8b 7c 24 28 [ 305.719169] RSP: 0018:ffffc90001687b58 EFLAGS: 00010246 [ 305.724434] RAX: 0000000000000000 RBX: 000000000000003f RCX: 0000000000000000 [ 305.731611] RDX: 0000000000000000 RSI: ffff88849fba8cb8 RDI: 00000000ffffffff [ 305.738785] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000001 [ 305.745962] R10: ffffc900016879a0 R11: ffffc900016879a5 R12: 0000000000000000 [ 305.753139] R13: 0000000000000000 R14: ffff8884905c9bc0 R15: 0000000000000000 [ 305.760315] FS: 0000000000000000(0000) GS:ffff88849fb80000(0000) knlGS:0000000000000000 [ 305.768452] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 305.774263] CR2: 0000000000000030 CR3: 0000000005610006 CR4: 0000000000760ee0 [ 305.781441] PKRU: 55555554 [ 305.784228] Call Trace: [ 305.786739] intel_atomic_check+0xb2e/0x2560 [i915] [ 305.791678] ? printk+0x53/0x6a [ 305.794856] ? drm_atomic_check_only+0x3e/0x810 [ 305.799417] ? __drm_dbg+0x82/0x90 [ 305.802848] drm_atomic_check_only+0x56a/0x810 [ 305.807322] drm_atomic_commit+0xe/0x50 [ 305.811185] drm_client_modeset_commit_atomic+0x1e2/0x250 [ 305.816619] drm_client_modeset_commit_force+0x4d/0x180 [ 305.821921] drm_fb_helper_restore_fbdev_mode_unlocked+0x46/0xa0 [ 305.827963] drm_fb_helper_set_par+0x2b/0x40 [ 305.832265] drm_fb_helper_hotplug_event.part.0+0xb2/0xd0 [ 305.837755] drm_kms_helper_hotplug_event+0x21/0x30 [ 305.842694] process_one_work+0x25b/0x5b0 [ 305.846735] worker_thread+0x4b/0x3b0 [ 305.850439] kthread+0x100/0x140 [ 305.853690] ? process_one_work+0x5b0/0x5b0 [ 305.857901] ? kthread_park+0x80/0x80 [ 305.861588] ret_from_fork+0x24/0x50 [ 305.865202] Modules linked in: snd_hda_codec_hdmi snd_hda_codec_realtek snd_hda_codec_generic i915 btusb btrtl btbcm btintel bluetooth prime_numbers snd_hda_intel snd_intel_dspcfg snd_hda_codec e1000e snd_hwdep snd_hda_core thunderbolt mei_hdcp mei_me asix cdc_ether x86_pkg_temp_thermal r8152 mei coretemp usbnet snd_pcm mii crct10dif_pclmul ptp crc32_pclmul ecdh_generic ghash_clmulni_intel pps_core ecc i2c_i801 intel_lpss_pci [ 305.903096] CR2: 0000000000000030 [ 305.906431] ---[ end trace 70ee364eed801cb0 ]--- [ 305.940816] RIP: 0010:drm_dp_mst_atomic_check+0x138/0x2c0 [ 305.946261] Code: 00 00 00 41 29 d9 41 89 d8 4c 89 fa 4c 89 f1 48 c7 c6 b0 b1 34 82 bf 10 00 00 00 45 31 ed e8 3f 99 02 00 4d 8b bf 80 04 00 00 <49> 8b 47 30 49 8d 5f 30 4c 8d 60 e8 48 39 c3 74 35 49 8b 7c 24 28 [ 305.965125] RSP: 0018:ffffc90001687b58 EFLAGS: 00010246 [ 305.970382] RAX: 0000000000000000 RBX: 000000000000003f RCX: 0000000000000000 [ 305.977571] RDX: 0000000000000000 RSI: ffff88849fba8cb8 RDI: 00000000ffffffff [ 305.984747] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000001 [ 305.991921] R10: ffffc900016879a0 R11: ffffc900016879a5 R12: 0000000000000000 [ 305.999099] R13: 0000000000000000 R14: ffff8884905c9bc0 R15: 0000000000000000 [ 306.006271] FS: 0000000000000000(0000) GS:ffff88849fb80000(0000) knlGS:0000000000000000 [ 306.014407] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 306.020185] CR2: 0000000000000030 CR3: 000000048b3aa003 CR4: 0000000000760ee0 [ 306.027404] PKRU: 55555554 [ 306.030127] BUG: sleeping function called from invalid context at include/linux/percpu-rwsem.h:38 [ 306.039049] in_atomic(): 0, irqs_disabled(): 1, non_block: 0, pid: 183, name: kworker/3:2 [ 306.047272] INFO: lockdep is turned off. [ 306.051217] irq event stamp: 77505 [ 306.054647] hardirqs last enabled at (77505): [] _raw_spin_unlock_irqrestore+0x47/0x60 [ 306.064270] hardirqs last disabled at (77504): [] _raw_spin_lock_irqsave+0xf/0x50 [ 306.073404] softirqs last enabled at (77402): [] __do_softirq+0x389/0x47f [ 306.081885] softirqs last disabled at (77395): [] irq_exit+0xa9/0xc0 [ 306.089859] CPU: 3 PID: 183 Comm: kworker/3:2 Tainted: G D 5.5.0-rc6+ #1404 [ 306.098167] Hardware name: Intel Corporation Ice Lake Client Platform/IceLake U DDR4 SODIMM PD RVP TLC, BIOS ICLSFWR1.R00.3201.A00.1905140358 05/14/2019 [ 306.111882] Workqueue: events drm_dp_delayed_destroy_work [ 306.117314] Call Trace: [ 306.119780] dump_stack+0x71/0xa0 [ 306.123135] ___might_sleep.cold+0xf7/0x10b [ 306.127399] exit_signals+0x2b/0x360 [ 306.131014] do_exit+0xa7/0xc70 [ 306.134189] ? kthread+0x100/0x140 [ 306.137615] rewind_stack_do_exit+0x17/0x20 Fixes: cd82d82cbc04 ("drm/dp_mst: Add branch bandwidth validation to MST atomic check") Cc: Mikita Lipski Cc: Alex Deucher Cc: Lyude Paul Acked-by: Mikita Lipski Reviewed-by: Lyude Paul Signed-off-by: José Roberto de Souza Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 5d3c1d379277..021c5a98db09 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -5001,6 +5001,9 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state) int i, ret = 0; for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) { + if (!mgr->mst_state) + continue; + ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state); if (ret) break; -- cgit