From 21a729d00776799c553b1cf45cb798c852ead660 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Thu, 13 Feb 2020 16:15:18 -0500 Subject: drm/mst: Separate sideband packet header parsing from message building In preparation of per-branch device down message handling, separate header parsing from message building. This will allow us to peek at figure out which branch device the message is from before starting to parse the message contents. Reviewed-by: Lyude Paul Reviewed-by: Wayne Lin [seanpaul s/drm_dp_sideband_msg_build/drm_dp_sideband_msg_set_header/] Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200213211523.156998-2-sean@poorly.run --- drivers/gpu/drm/drm_dp_mst_topology.c | 102 +++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 45 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 4b255e25e4a1..551f242761d5 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -687,51 +687,45 @@ static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body * raw->cur_len = idx; } -/* this adds a chunk of msg to the builder to get the final msg */ -static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg, - u8 *replybuf, u8 replybuflen, bool hdr) +static int drm_dp_sideband_msg_set_header(struct drm_dp_sideband_msg_rx *msg, + struct drm_dp_sideband_msg_hdr *hdr, + u8 hdrlen) { - int ret; - u8 crc4; + /* + * ignore out-of-order messages or messages that are part of a + * failed transaction + */ + if (!hdr->somt && !msg->have_somt) + return false; - if (hdr) { - u8 hdrlen; - struct drm_dp_sideband_msg_hdr recv_hdr; - ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen); - if (ret == false) { - print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false); - return false; - } + /* get length contained in this portion */ + msg->curchunk_idx = 0; + msg->curchunk_len = hdr->msg_len; + msg->curchunk_hdrlen = hdrlen; - /* - * ignore out-of-order messages or messages that are part of a - * failed transaction - */ - if (!recv_hdr.somt && !msg->have_somt) - return false; + /* we have already gotten an somt - don't bother parsing */ + if (hdr->somt && msg->have_somt) + return false; - /* get length contained in this portion */ - msg->curchunk_len = recv_hdr.msg_len; - msg->curchunk_hdrlen = hdrlen; + if (hdr->somt) { + memcpy(&msg->initial_hdr, hdr, + sizeof(struct drm_dp_sideband_msg_hdr)); + msg->have_somt = true; + } + if (hdr->eomt) + msg->have_eomt = true; - /* we have already gotten an somt - don't bother parsing */ - if (recv_hdr.somt && msg->have_somt) - return false; + return true; +} - if (recv_hdr.somt) { - memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr)); - msg->have_somt = true; - } - if (recv_hdr.eomt) - msg->have_eomt = true; +/* this adds a chunk of msg to the builder to get the final msg */ +static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg, + u8 *replybuf, u8 replybuflen) +{ + u8 crc4; - /* copy the bytes for the remainder of this header chunk */ - msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen)); - memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx); - } else { - memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); - msg->curchunk_idx += replybuflen; - } + memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen); + msg->curchunk_idx += replybuflen; if (msg->curchunk_idx >= msg->curchunk_len) { /* do CRC */ @@ -3708,25 +3702,43 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) u8 replyblock[32]; int replylen, curreply; int ret; + u8 hdrlen; + struct drm_dp_sideband_msg_hdr hdr; struct drm_dp_sideband_msg_rx *msg; - int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; + int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : + DP_SIDEBAND_MSG_DOWN_REP_BASE; + msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv; len = min(mgr->max_dpcd_transaction_bytes, 16); - ret = drm_dp_dpcd_read(mgr->aux, basereg, - replyblock, len); + ret = drm_dp_dpcd_read(mgr->aux, basereg, replyblock, len); if (ret != len) { DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret); return false; } - ret = drm_dp_sideband_msg_build(msg, replyblock, len, true); + + ret = drm_dp_decode_sideband_msg_hdr(&hdr, replyblock, len, &hdrlen); + if (ret == false) { + print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, + 1, replyblock, len, false); + DRM_DEBUG_KMS("ERROR: failed header\n"); + return false; + } + + if (!drm_dp_sideband_msg_set_header(msg, &hdr, hdrlen)) { + DRM_DEBUG_KMS("sideband msg set header failed %d\n", + replyblock[0]); + return false; + } + + replylen = min(msg->curchunk_len, (u8)(len - hdrlen)); + ret = drm_dp_sideband_append_payload(msg, replyblock + hdrlen, replylen); if (!ret) { DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]); return false; } - replylen = msg->curchunk_len + msg->curchunk_hdrlen; - replylen -= len; + replylen = msg->curchunk_len + msg->curchunk_hdrlen - len; curreply = len; while (replylen > 0) { len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16); @@ -3738,7 +3750,7 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) return false; } - ret = drm_dp_sideband_msg_build(msg, replyblock, len, false); + ret = drm_dp_sideband_append_payload(msg, replyblock, len); if (!ret) { DRM_DEBUG_KMS("failed to build sideband msg\n"); return false; -- cgit From fbc821c4a506a960e85f3e97e32cfab63d43f7d0 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Thu, 13 Feb 2020 16:15:19 -0500 Subject: drm/mst: Support simultaneous down replies Currently we have one down reply message servicing the mst manager, so we need to serialize all tx msgs to ensure we only have one message in flight at a time. For obvious reasons this is suboptimal (but less suboptimal than the free-for-all we had before serialization). This patch removes the single down_rep_recv message from manager and adds 2 replies in the branch structure. The 2 replies mirrors the tx_slots which we use to rate-limit outgoing messages and correspond to seqno in the packet headers. Cc: Wayne Lin Reviewed-by: Lyude Paul Reviewed-by: Wayne Lin Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200213211523.156998-3-sean@poorly.run --- drivers/gpu/drm/drm_dp_mst_topology.c | 80 +++++++++++++++++++++-------------- include/drm/drm_dp_mst_helper.h | 59 +++++++++++++------------- 2 files changed, 78 insertions(+), 61 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 551f242761d5..236a4beb7bd6 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -3696,7 +3696,8 @@ out_fail: } EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume); -static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) +static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, + struct drm_dp_mst_branch **mstb, int *seqno) { int len; u8 replyblock[32]; @@ -3708,7 +3709,8 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; - msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv; + *mstb = NULL; + *seqno = -1; len = min(mgr->max_dpcd_transaction_bytes, 16); ret = drm_dp_dpcd_read(mgr->aux, basereg, replyblock, len); @@ -3725,6 +3727,21 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) return false; } + *seqno = hdr.seqno; + + if (up) { + msg = &mgr->up_req_recv; + } else { + /* Caller is responsible for giving back this reference */ + *mstb = drm_dp_get_mst_branch_device(mgr, hdr.lct, hdr.rad); + if (!*mstb) { + DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", + hdr.lct); + return false; + } + msg = &(*mstb)->down_rep_recv[hdr.seqno]; + } + if (!drm_dp_sideband_msg_set_header(msg, &hdr, hdrlen)) { DRM_DEBUG_KMS("sideband msg set header failed %d\n", replyblock[0]); @@ -3765,53 +3782,52 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) { struct drm_dp_sideband_msg_tx *txmsg; - struct drm_dp_mst_branch *mstb; - struct drm_dp_sideband_msg_hdr *hdr = &mgr->down_rep_recv.initial_hdr; - int slot = -1; + struct drm_dp_mst_branch *mstb = NULL; + struct drm_dp_sideband_msg_rx *msg = NULL; + int seqno = -1; - if (!drm_dp_get_one_sb_msg(mgr, false)) - goto clear_down_rep_recv; + if (!drm_dp_get_one_sb_msg(mgr, false, &mstb, &seqno)) + goto out_clear_reply; - if (!mgr->down_rep_recv.have_eomt) - return 0; + msg = &mstb->down_rep_recv[seqno]; - mstb = drm_dp_get_mst_branch_device(mgr, hdr->lct, hdr->rad); - if (!mstb) { - DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", - hdr->lct); - goto clear_down_rep_recv; - } + /* Multi-packet message transmission, don't clear the reply */ + if (!msg->have_eomt) + goto out; /* find the message */ - slot = hdr->seqno; mutex_lock(&mgr->qlock); - txmsg = mstb->tx_slots[slot]; + txmsg = mstb->tx_slots[seqno]; /* remove from slots */ mutex_unlock(&mgr->qlock); if (!txmsg) { + struct drm_dp_sideband_msg_hdr *hdr; + hdr = &msg->initial_hdr; DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n", mstb, hdr->seqno, hdr->lct, hdr->rad[0], - mgr->down_rep_recv.msg[0]); - goto no_msg; + msg->msg[0]); + goto out_clear_reply; } - drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply); + drm_dp_sideband_parse_reply(msg, &txmsg->reply); - if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) + if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK) { DRM_DEBUG_KMS("Got NAK reply: req 0x%02x (%s), reason 0x%02x (%s), nak data 0x%02x\n", txmsg->reply.req_type, drm_dp_mst_req_type_str(txmsg->reply.req_type), txmsg->reply.u.nak.reason, drm_dp_mst_nak_reason_str(txmsg->reply.u.nak.reason), txmsg->reply.u.nak.nak_data); + goto out_clear_reply; + } - memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx)); drm_dp_mst_topology_put_mstb(mstb); mutex_lock(&mgr->qlock); txmsg->state = DRM_DP_SIDEBAND_TX_RX; - mstb->tx_slots[slot] = NULL; + mstb->tx_slots[seqno] = NULL; mgr->is_waiting_for_dwn_reply = false; mutex_unlock(&mgr->qlock); @@ -3819,13 +3835,15 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) return 0; -no_msg: - drm_dp_mst_topology_put_mstb(mstb); -clear_down_rep_recv: +out_clear_reply: mutex_lock(&mgr->qlock); mgr->is_waiting_for_dwn_reply = false; mutex_unlock(&mgr->qlock); - memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); + if (msg) + memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx)); +out: + if (mstb) + drm_dp_mst_topology_put_mstb(mstb); return 0; } @@ -3901,11 +3919,10 @@ static void drm_dp_mst_up_req_work(struct work_struct *work) static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) { - struct drm_dp_sideband_msg_hdr *hdr = &mgr->up_req_recv.initial_hdr; struct drm_dp_pending_up_req *up_req; - bool seqno; + int seqno; - if (!drm_dp_get_one_sb_msg(mgr, true)) + if (!drm_dp_get_one_sb_msg(mgr, true, NULL, &seqno)) goto out; if (!mgr->up_req_recv.have_eomt) @@ -3918,7 +3935,6 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) } INIT_LIST_HEAD(&up_req->next); - seqno = hdr->seqno; drm_dp_sideband_parse_req(&mgr->up_req_recv, &up_req->msg); if (up_req->msg.req_type != DP_CONNECTION_STATUS_NOTIFY && @@ -3952,7 +3968,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) res_stat->available_pbn); } - up_req->hdr = *hdr; + up_req->hdr = mgr->up_req_recv.initial_hdr; mutex_lock(&mgr->up_req_lock); list_add_tail(&up_req->next, &mgr->up_req_list); mutex_unlock(&mgr->up_req_lock); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index 9a1e8ba4f839..cff135070535 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -160,6 +160,31 @@ struct drm_dp_mst_port { bool fec_capable; }; +/* sideband msg header - not bit struct */ +struct drm_dp_sideband_msg_hdr { + u8 lct; + u8 lcr; + u8 rad[8]; + bool broadcast; + bool path_msg; + u8 msg_len; + bool somt; + bool eomt; + bool seqno; +}; + +struct drm_dp_sideband_msg_rx { + u8 chunk[48]; + u8 msg[256]; + u8 curchunk_len; + u8 curchunk_idx; /* chunk we are parsing now */ + u8 curchunk_hdrlen; + u8 curlen; /* total length of the msg */ + bool have_somt; + bool have_eomt; + struct drm_dp_sideband_msg_hdr initial_hdr; +}; + /** * struct drm_dp_mst_branch - MST branch device. * @rad: Relative Address to talk to this branch device. @@ -232,24 +257,16 @@ struct drm_dp_mst_branch { int last_seqno; bool link_address_sent; + /** + * @down_rep_recv: Message receiver state for down replies. + */ + struct drm_dp_sideband_msg_rx down_rep_recv[2]; + /* global unique identifier to identify branch devices */ u8 guid[16]; }; -/* sideband msg header - not bit struct */ -struct drm_dp_sideband_msg_hdr { - u8 lct; - u8 lcr; - u8 rad[8]; - bool broadcast; - bool path_msg; - u8 msg_len; - bool somt; - bool eomt; - bool seqno; -}; - struct drm_dp_nak_reply { u8 guid[16]; u8 reason; @@ -306,18 +323,6 @@ struct drm_dp_remote_i2c_write_ack_reply { }; -struct drm_dp_sideband_msg_rx { - u8 chunk[48]; - u8 msg[256]; - u8 curchunk_len; - u8 curchunk_idx; /* chunk we are parsing now */ - u8 curchunk_hdrlen; - u8 curlen; /* total length of the msg */ - bool have_somt; - bool have_eomt; - struct drm_dp_sideband_msg_hdr initial_hdr; -}; - #define DRM_DP_MAX_SDP_STREAMS 16 struct drm_dp_allocate_payload { u8 port_number; @@ -555,10 +560,6 @@ struct drm_dp_mst_topology_mgr { */ int conn_base_id; - /** - * @down_rep_recv: Message receiver state for down replies. - */ - struct drm_dp_sideband_msg_rx down_rep_recv; /** * @up_req_recv: Message receiver state for up requests. */ -- cgit From 6bb0942e8f46863a745489cce27efe5be2a3885e Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Thu, 13 Feb 2020 16:15:20 -0500 Subject: drm/dp_mst: Remove single tx msg restriction. Now that we can support multiple simultaneous replies, remove the restrictions placed on sending new tx msgs. This patch essentially just reverts commit 5a64967a2f3b ("drm/dp_mst: Have DP_Tx send one msg at a time") now that the problem is solved in a different way. Cc: Wayne Lin Reviewed-by: Lyude Paul Reviewed-by: Wayne Lin Signed-off-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200213211523.156998-4-sean@poorly.run --- drivers/gpu/drm/drm_dp_mst_topology.c | 14 ++------------ include/drm/drm_dp_mst_helper.h | 5 ----- 2 files changed, 2 insertions(+), 17 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 236a4beb7bd6..840c6370fb39 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1205,8 +1205,6 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, txmsg->state == DRM_DP_SIDEBAND_TX_SENT) { mstb->tx_slots[txmsg->seqno] = NULL; } - mgr->is_waiting_for_dwn_reply = false; - } out: if (unlikely(ret == -EIO) && drm_debug_enabled(DRM_UT_DP)) { @@ -1216,7 +1214,6 @@ out: } mutex_unlock(&mgr->qlock); - drm_dp_mst_kick_tx(mgr); return ret; } @@ -2796,11 +2793,9 @@ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr) ret = process_single_tx_qlock(mgr, txmsg, false); if (ret == 1) { /* txmsg is sent it should be in the slots now */ - mgr->is_waiting_for_dwn_reply = true; list_del(&txmsg->next); } else if (ret) { DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); - mgr->is_waiting_for_dwn_reply = false; list_del(&txmsg->next); if (txmsg->seqno != -1) txmsg->dst->tx_slots[txmsg->seqno] = NULL; @@ -2840,8 +2835,7 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr, drm_dp_mst_dump_sideband_msg_tx(&p, txmsg); } - if (list_is_singular(&mgr->tx_msg_downq) && - !mgr->is_waiting_for_dwn_reply) + if (list_is_singular(&mgr->tx_msg_downq)) process_single_down_tx_qlock(mgr); mutex_unlock(&mgr->qlock); } @@ -3828,7 +3822,6 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) mutex_lock(&mgr->qlock); txmsg->state = DRM_DP_SIDEBAND_TX_RX; mstb->tx_slots[seqno] = NULL; - mgr->is_waiting_for_dwn_reply = false; mutex_unlock(&mgr->qlock); wake_up_all(&mgr->tx_waitq); @@ -3836,9 +3829,6 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) return 0; out_clear_reply: - mutex_lock(&mgr->qlock); - mgr->is_waiting_for_dwn_reply = false; - mutex_unlock(&mgr->qlock); if (msg) memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx)); out: @@ -4696,7 +4686,7 @@ static void drm_dp_tx_work(struct work_struct *work) struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work); mutex_lock(&mgr->qlock); - if (!list_empty(&mgr->tx_msg_downq) && !mgr->is_waiting_for_dwn_reply) + if (!list_empty(&mgr->tx_msg_downq)) process_single_down_tx_qlock(mgr); mutex_unlock(&mgr->qlock); } diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index cff135070535..bf5e65d2303e 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -590,11 +590,6 @@ struct drm_dp_mst_topology_mgr { */ bool payload_id_table_cleared : 1; - /** - * @is_waiting_for_dwn_reply: whether we're waiting for a down reply. - */ - bool is_waiting_for_dwn_reply : 1; - /** * @mst_primary: Pointer to the primary/first branch device. */ -- cgit From bdf7e3b782efbc6ca8fd4d6cd663f14a00cafdad Mon Sep 17 00:00:00 2001 From: Maya Rashish Date: Sat, 21 Mar 2020 22:29:59 +0000 Subject: drm/dp_mst: make build_clear_payload_id_table return void Nothing uses the always-0 return value. Signed-off-by: Maya Rashish Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200321222959.GA1053@SDF.ORG --- drivers/gpu/drm/drm_dp_mst_topology.c | 3 +-- 1 file changed, 1 insertion(+), 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 840c6370fb39..17a7a744fb14 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -1054,13 +1054,12 @@ static void build_link_address(struct drm_dp_sideband_msg_tx *msg) drm_dp_encode_sideband_req(&req, msg); } -static int build_clear_payload_id_table(struct drm_dp_sideband_msg_tx *msg) +static void 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, -- cgit From 72dc0f5159138b61762a500d0fde9bbc1af82884 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 31 Mar 2020 16:57:37 -0400 Subject: drm/dp_mst: Remove drm_dp_mst_topology_cbs.destroy_connector Now that we've removed the last user of this callback, get rid of it and drm_dp_destroy_connector(). Signed-off-by: Lyude Paul Cc: Pankaj Bharadiya Link: https://patchwork.freedesktop.org/patch/msgid/20200331205740.135525-5-lyude@redhat.com Reviewed-by: Alex Deucher --- drivers/gpu/drm/drm_dp_mst_topology.c | 16 +++------------- include/drm/drm_dp_mst_helper.h | 2 -- 2 files changed, 3 insertions(+), 15 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 17a7a744fb14..3331de086580 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4690,23 +4690,13 @@ static void drm_dp_tx_work(struct work_struct *work) mutex_unlock(&mgr->qlock); } -static inline void drm_dp_destroy_connector(struct drm_dp_mst_port *port) +static inline void +drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port) { - if (!port->connector) - return; - - if (port->mgr->cbs->destroy_connector) { - port->mgr->cbs->destroy_connector(port->mgr, port->connector); - } else { + if (port->connector) { drm_connector_unregister(port->connector); drm_connector_put(port->connector); } -} - -static inline void -drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port) -{ - drm_dp_destroy_connector(port); drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs); drm_dp_mst_put_port_malloc(port); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index e1f212b2505a..e36e53de269e 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -488,8 +488,6 @@ struct drm_dp_mst_topology_mgr; struct drm_dp_mst_topology_cbs { /* create a connector for a port */ struct drm_connector *(*add_connector)(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, const char *path); - void (*destroy_connector)(struct drm_dp_mst_topology_mgr *mgr, - struct drm_connector *connector); }; #define DP_MAX_PAYLOAD (sizeof(unsigned long) * 8) -- cgit From cbfb1b74438fdab9ab34a24bb3a206033d807dc0 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 6 Apr 2020 15:33:52 -0400 Subject: drm/dp_mst: Fix NULL deref in drm_dp_get_one_sb_msg() While we don't need this function to store an mstb anywhere for UP requests since we process them asynchronously, we do need to make sure that we don't try to write to **mstb for UP requests otherwise we'll cause a NULL pointer deref: RIP: 0010:drm_dp_get_one_sb_msg+0x4b/0x460 [drm_kms_helper] Call Trace: ? vprintk_emit+0x16a/0x230 ? drm_dp_mst_hpd_irq+0x133/0x1010 [drm_kms_helper] drm_dp_mst_hpd_irq+0x133/0x1010 [drm_kms_helper] ? __drm_dbg+0x87/0x90 [drm] ? intel_dp_hpd_pulse+0x24b/0x400 [i915] intel_dp_hpd_pulse+0x24b/0x400 [i915] i915_digport_work_func+0xd6/0x160 [i915] process_one_work+0x1a9/0x370 worker_thread+0x4d/0x3a0 kthread+0xf9/0x130 ? process_one_work+0x370/0x370 ? kthread_park+0x90/0x90 ret_from_fork+0x35/0x40 So, fix this. Signed-off-by: Lyude Paul Fixes: fbc821c4a506 ("drm/mst: Support simultaneous down replies") Cc: Wayne Lin Cc: Lyude Paul Cc: Wayne Lin Cc: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200406193352.1245985-1-lyude@redhat.com Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_mst_topology.c | 3 ++- 1 file changed, 2 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 3331de086580..6994076cbdc7 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -3702,7 +3702,8 @@ static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up, int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE; - *mstb = NULL; + if (!up) + *mstb = NULL; *seqno = -1; len = min(mgr->max_dpcd_transaction_bytes, 16); -- cgit From 61272e47c18cd05c0c5e5a4a2bee736bf37d765d Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 3 Apr 2020 16:03:25 -0400 Subject: drm/dp_mst: Don't drop NAKs for down responses It looks like that when we introduced the ability to handle multiple down requests at once, we accidentally started dropping NAK replies - causing sideband messages which got NAK'd to seemingly timeout and cause all sorts of weirdness. So, fix this by making sure we don't return from drm_dp_mst_handle_down_rep() early, but instead treat NAKs like any other message. Signed-off-by: Lyude Paul Fixes: fbc821c4a506 ("drm/mst: Support simultaneous down replies") Cc: Wayne Lin Cc: Wayne Lin Cc: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200403200325.885628-1-lyude@redhat.com Reviewed-by: Sean Paul --- drivers/gpu/drm/drm_dp_mst_topology.c | 1 - 1 file changed, 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 6994076cbdc7..80067ea5c50e 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -3813,7 +3813,6 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) txmsg->reply.u.nak.reason, drm_dp_mst_nak_reason_str(txmsg->reply.u.nak.reason), txmsg->reply.u.nak.nak_data); - goto out_clear_reply; } memset(msg, 0, sizeof(struct drm_dp_sideband_msg_rx)); -- cgit From 20c22ad3295766b9a7f6da29b3620f570993c2f3 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 6 Apr 2020 16:06:42 -0400 Subject: drm/dp_mst: Remove drm_dp_mst_has_audio() Drive-by fix I noticed the other day - drm_dp_mst_has_audio() only ever made sense back when we still had to validate ports before accessing them in order to (attempt to) avoid NULL dereferences. Since we have proper reference counting that guarantees we always can safely access the MST port, there's no use in keeping this function around as all it does is validate the port pointer before checking the audio status. Note - drm_dp_mst_port->has_audio is technically protected by drm_device->mode_config.connection_mutex, since it's only ever updated from drm_dp_mst_get_edid(). Additionally, we change the declaration for port in struct intel_connector to be properly typed, so we can directly access it. Changes since v1: * Change type of intel_connector->port in a separate patch - Sean Paul Cc: "Lee, Shawn C" Reviewed-by: Sean Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200406200646.1263435-2-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 21 --------------------- .../gpu/drm/i915/display/intel_display_debugfs.c | 10 ++-------- drivers/gpu/drm/i915/display/intel_dp_mst.c | 3 +-- include/drm/drm_dp_mst_helper.h | 2 -- 4 files changed, 3 insertions(+), 33 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 80067ea5c50e..3842336f63a5 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4062,27 +4062,6 @@ out: } EXPORT_SYMBOL(drm_dp_mst_detect_port); -/** - * drm_dp_mst_port_has_audio() - Check whether port has audio capability or not - * @mgr: manager for this port - * @port: unverified pointer to a port. - * - * This returns whether the port supports audio or not. - */ -bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port) -{ - bool ret = false; - - port = drm_dp_mst_topology_get_port_validated(mgr, port); - if (!port) - return ret; - ret = port->has_audio; - drm_dp_mst_topology_put_port(port); - return ret; -} -EXPORT_SYMBOL(drm_dp_mst_port_has_audio); - /** * drm_dp_mst_get_edid() - get EDID for an MST port * @connector: toplevel connector to get EDID for diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 424f4e52f783..9f736420d83f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -631,15 +631,9 @@ static void intel_dp_info(struct seq_file *m, } static void intel_dp_mst_info(struct seq_file *m, - struct intel_connector *intel_connector) + struct intel_connector *intel_connector) { - struct intel_encoder *intel_encoder = intel_attached_encoder(intel_connector); - struct intel_dp_mst_encoder *intel_mst = - enc_to_mst(intel_encoder); - struct intel_digital_port *intel_dig_port = intel_mst->primary; - struct intel_dp *intel_dp = &intel_dig_port->dp; - bool has_audio = drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, - intel_connector->port); + bool has_audio = intel_connector->port->has_audio; seq_printf(m, "\taudio support: %s\n", yesno(has_audio)); } diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 44f3fd251ca1..35debce71366 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -158,8 +158,7 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, pipe_config->has_pch_encoder = false; if (intel_conn_state->force_audio == HDMI_AUDIO_AUTO) - pipe_config->has_audio = - drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, port); + pipe_config->has_audio = connector->port->has_audio; else pipe_config->has_audio = intel_conn_state->force_audio == HDMI_AUDIO_ON; diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index e36e53de269e..e689f8d25869 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -732,8 +732,6 @@ drm_dp_mst_detect_port(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); -bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_port *port); struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port); -- cgit From 17e03aa8cc16e3d43848fb459f0435372f1c5b9b Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 3 Apr 2020 14:11:46 -0400 Subject: drm/dp_mst: Improve kdocs for drm_dp_check_act_status() No functional changes. Signed-off-by: Lyude Paul Cc: Sean Paul Reviewed-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200406221253.1307209-2-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 8 ++++++-- 1 file changed, 6 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 3842336f63a5..c6caa0d604ae 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4440,10 +4440,14 @@ fail: /** - * drm_dp_check_act_status() - Check ACT handled status. + * drm_dp_check_act_status() - Polls for ACT handled status. * @mgr: manager to use * - * Check the payload status bits in the DPCD for ACT handled completion. + * Tries waiting for the MST hub to finish updating it's payload table by + * polling for the ACT handled bit. + * + * Returns: + * 0 if the ACT was handled in time, negative error code on failure. */ int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) { -- cgit From a5cb5fa6c3a5c2cf492db667b8670ee7b044b79f Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 3 Apr 2020 14:08:32 -0400 Subject: drm/dp_mst: Reformat drm_dp_check_act_status() a bit Just add a bit more line wrapping, get rid of some extraneous whitespace, remove an unneeded goto label, and move around some variable declarations. No functional changes here. Signed-off-by: Lyude Paul [this isn't a fix, but it's needed for the fix that comes after this] Fixes: ad7f8a1f9ced ("drm/helper: add Displayport multi-stream helper (v0.6)") Cc: Sean Paul Cc: # v3.17+ Reviewed-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200406221253.1307209-3-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 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 c6caa0d604ae..e7a5bd3e6015 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4451,33 +4451,31 @@ fail: */ int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) { + int count = 0, ret; u8 status; - int ret; - int count = 0; do { - ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); - + ret = drm_dp_dpcd_readb(mgr->aux, + DP_PAYLOAD_TABLE_UPDATE_STATUS, + &status); if (ret < 0) { - DRM_DEBUG_KMS("failed to read payload table status %d\n", ret); - goto fail; + DRM_DEBUG_KMS("failed to read payload table status %d\n", + ret); + return ret; } if (status & DP_PAYLOAD_ACT_HANDLED) break; count++; udelay(100); - } while (count < 30); if (!(status & DP_PAYLOAD_ACT_HANDLED)) { - DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count); - ret = -EINVAL; - goto fail; + DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", + status, count); + return -EINVAL; } return 0; -fail: - return ret; } EXPORT_SYMBOL(drm_dp_check_act_status); -- cgit From 873a95e0d59ac06901ae261dda0b7165ffd002b8 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 3 Apr 2020 15:47:15 -0400 Subject: drm/dp_mst: Increase ACT retry timeout to 3s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we only poll for an ACT up to 30 times, with a busy-wait delay of 100µs between each attempt - giving us a timeout of 2900µs. While this might seem sensible, it would appear that in certain scenarios it can take dramatically longer then that for us to receive an ACT. On one of the EVGA MST hubs that I have available, I observed said hub sometimes taking longer then a second before signalling the ACT. These delays mostly seem to occur when previous sideband messages we've sent are NAKd by the hub, however it wouldn't be particularly surprising if it's possible to reproduce times like this simply by introducing branch devices with large LCTs since payload allocations have to take effect on every downstream device up to the payload's target. So, instead of just retrying 30 times we poll for the ACT for up to 3ms, and additionally use usleep_range() to avoid a very long and rude busy-wait. Note that the previous retry count of 30 appears to have been arbitrarily chosen, as I can't find any mention of a recommended timeout or retry count for ACTs in the DisplayPort 2.0 specification. This also goes for the range we were previously using for udelay(), although I suspect that was just copied from the recommended delay for link training on SST devices. Changes since v1: * Use readx_poll_timeout() instead of open-coding timeout loop - Sean Paul Changes since v2: * Increase poll interval to 200us - Sean Paul * Print status in hex when we timeout waiting for ACT - Sean Paul Signed-off-by: Lyude Paul Fixes: ad7f8a1f9ced ("drm/helper: add Displayport multi-stream helper (v0.6)") Cc: Sean Paul Cc: # v3.17+ Reviewed-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200406221253.1307209-4-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 57 +++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 23 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 e7a5bd3e6015..8942ab98ab64 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -27,6 +27,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS) #include @@ -4438,43 +4439,53 @@ fail: return ret; } +static int do_get_act_status(struct drm_dp_aux *aux) +{ + int ret; + u8 status; + + ret = drm_dp_dpcd_readb(aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status); + if (ret < 0) + return ret; + + return status; +} /** * drm_dp_check_act_status() - Polls for ACT handled status. * @mgr: manager to use * * Tries waiting for the MST hub to finish updating it's payload table by - * polling for the ACT handled bit. + * polling for the ACT handled bit for up to 3 seconds (yes-some hubs really + * take that long). * * Returns: * 0 if the ACT was handled in time, negative error code on failure. */ int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) { - int count = 0, ret; - u8 status; - - do { - ret = drm_dp_dpcd_readb(mgr->aux, - DP_PAYLOAD_TABLE_UPDATE_STATUS, - &status); - if (ret < 0) { - DRM_DEBUG_KMS("failed to read payload table status %d\n", - ret); - return ret; - } - - if (status & DP_PAYLOAD_ACT_HANDLED) - break; - count++; - udelay(100); - } while (count < 30); - - if (!(status & DP_PAYLOAD_ACT_HANDLED)) { - DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", - status, count); + /* + * There doesn't seem to be any recommended retry count or timeout in + * the MST specification. Since some hubs have been observed to take + * over 1 second to update their payload allocations under certain + * conditions, we use a rather large timeout value. + */ + const int timeout_ms = 3000; + int ret, status; + + ret = readx_poll_timeout(do_get_act_status, mgr->aux, status, + status & DP_PAYLOAD_ACT_HANDLED || status < 0, + 200, timeout_ms * USEC_PER_MSEC); + if (ret < 0 && status >= 0) { + DRM_DEBUG_KMS("Failed to get ACT after %dms, last status: %02x\n", + timeout_ms, status); return -EINVAL; + } else if (status < 0) { + DRM_DEBUG_KMS("Failed to read payload table status: %d\n", + status); + return status; } + return 0; } EXPORT_SYMBOL(drm_dp_check_act_status); -- cgit From 4d1b58d5eafd6acbc728e41263a7de1b5e8b781a Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 3 Apr 2020 15:58:29 -0400 Subject: drm/dp_mst: Print errors on ACT timeouts Although it's not unexpected for drm_dp_check_act_status() to fail due to DPCD read failures (as the hub may have just been unplugged suddenly), timeouts are a bit more worrying as they either mean we need a longer timeout value, or we aren't setting up payload allocations properly. So, let's start printing errors on timeouts. Signed-off-by: Lyude Paul Cc: Sean Paul Reviewed-by: Sean Paul Link: https://patchwork.freedesktop.org/patch/msgid/20200406221253.1307209-5-lyude@redhat.com --- drivers/gpu/drm/drm_dp_mst_topology.c | 8 ++++++-- 1 file changed, 6 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 8942ab98ab64..6529b479645c 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -4477,10 +4477,14 @@ int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr) status & DP_PAYLOAD_ACT_HANDLED || status < 0, 200, timeout_ms * USEC_PER_MSEC); if (ret < 0 && status >= 0) { - DRM_DEBUG_KMS("Failed to get ACT after %dms, last status: %02x\n", - timeout_ms, status); + DRM_ERROR("Failed to get ACT after %dms, last status: %02x\n", + timeout_ms, status); return -EINVAL; } else if (status < 0) { + /* + * Failure here isn't unexpected - the hub may have + * just been unplugged + */ DRM_DEBUG_KMS("Failed to read payload table status: %d\n", status); return status; -- cgit