drm/dp/mst: process broadcast messages correctly
authorMykola Lysenko <Mykola.Lysenko@amd.com>
Fri, 18 Dec 2015 22:14:42 +0000 (17:14 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 4 Jan 2016 17:03:39 +0000 (12:03 -0500)
In case broadcast message received in UP request,
RAD cannot be used to identify message originator.
Message should be parsed, originator should be found
by GUID from parsed message.

Also reply with broadcast in case broadcast message
received (for now it is always broadcast)

Acked-by: Dave Airlie <airlied@gmail.com>
Signed-off-by: Mykola Lysenko <Mykola.Lysenko@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
drivers/gpu/drm/drm_dp_mst_topology.c

index c5a942b15d637ee1cb206bb232620188cc0697a0..7710de6e8a55f9640f0548b8464ab892f93746a4 100644 (file)
@@ -1215,6 +1215,50 @@ out:
        return mstb;
 }
 
+static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
+       struct drm_dp_mst_branch *mstb,
+       uint8_t *guid)
+{
+       struct drm_dp_mst_branch *found_mstb;
+       struct drm_dp_mst_port *port;
+
+       list_for_each_entry(port, &mstb->ports, next) {
+               if (!port->mstb)
+                       continue;
+
+               if (port->guid_valid && memcmp(port->guid, guid, 16) == 0)
+                       return port->mstb;
+
+               found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
+
+               if (found_mstb)
+                       return found_mstb;
+       }
+
+       return NULL;
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device_by_guid(
+       struct drm_dp_mst_topology_mgr *mgr,
+       uint8_t *guid)
+{
+       struct drm_dp_mst_branch *mstb;
+
+       /* find the port by iterating down */
+       mutex_lock(&mgr->lock);
+
+       if (mgr->guid_valid && memcmp(mgr->guid, guid, 16) == 0)
+               mstb = mgr->mst_primary;
+       else
+               mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid);
+
+       if (mstb)
+               kref_get(&mstb->kref);
+
+       mutex_unlock(&mgr->lock);
+       return mstb;
+}
+
 static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
                                               struct drm_dp_mst_branch *mstb)
 {
@@ -1325,6 +1369,7 @@ static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
                                  struct drm_dp_sideband_msg_tx *txmsg)
 {
        struct drm_dp_mst_branch *mstb = txmsg->dst;
+       u8 req_type;
 
        /* both msg slots are full */
        if (txmsg->seqno == -1) {
@@ -1341,7 +1386,13 @@ static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
                        txmsg->seqno = 1;
                mstb->tx_slots[txmsg->seqno] = txmsg;
        }
-       hdr->broadcast = 0;
+
+       req_type = txmsg->msg[0] & 0x7f;
+       if (req_type == DP_CONNECTION_STATUS_NOTIFY ||
+               req_type == DP_RESOURCE_STATUS_NOTIFY)
+               hdr->broadcast = 1;
+       else
+               hdr->broadcast = 0;
        hdr->path_msg = txmsg->path_msg;
        hdr->lct = mstb->lct;
        hdr->lcr = mstb->lct - 1;
@@ -2157,28 +2208,50 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
 
        if (mgr->up_req_recv.have_eomt) {
                struct drm_dp_sideband_msg_req_body msg;
-               struct drm_dp_mst_branch *mstb;
+               struct drm_dp_mst_branch *mstb = NULL;
                bool seqno;
-               mstb = drm_dp_get_mst_branch_device(mgr,
-                                                   mgr->up_req_recv.initial_hdr.lct,
-                                                   mgr->up_req_recv.initial_hdr.rad);
-               if (!mstb) {
-                       DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
-                       memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
-                       return 0;
+
+               if (!mgr->up_req_recv.initial_hdr.broadcast) {
+                       mstb = drm_dp_get_mst_branch_device(mgr,
+                                                           mgr->up_req_recv.initial_hdr.lct,
+                                                           mgr->up_req_recv.initial_hdr.rad);
+                       if (!mstb) {
+                               DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+                               memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+                               return 0;
+                       }
                }
 
                seqno = mgr->up_req_recv.initial_hdr.seqno;
                drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
 
                if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
-                       drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+                       drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
+
+                       if (!mstb)
+                               mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.conn_stat.guid);
+
+                       if (!mstb) {
+                               DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+                               memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+                               return 0;
+                       }
+
                        drm_dp_update_port(mstb, &msg.u.conn_stat);
                        DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
                        (*mgr->cbs->hotplug)(mgr);
 
                } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
-                       drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+                       drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
+                       if (!mstb)
+                               mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.resource_stat.guid);
+
+                       if (!mstb) {
+                               DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+                               memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+                               return 0;
+                       }
+
                        DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
                }