summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2015-10-07 14:37:08 +1000
committerDave Airlie <airlied@redhat.com>2015-10-14 12:40:07 +1000
commit15b73ea121055ea749e90b69f1ac6ffae5e5f9e2 (patch)
treeea89569db2f41b60d9e1e12a83422a498ad42221
parent82aff6b72e2d0e03bb7e9fea99617711d265a95b (diff)
drm/dp/mst: handle connection status notify better.drm-mst-fixes
If we get a CSN message from a DP1.2 downstream device, the mst layer would attempt to send msgs to the device from the irq, so could never receive replies. Set a flag on the port and use the work queue to update values the port requires. MST spec is crappy here, initial port info has guid/dpcd_rev but CSN doesn't have it, so we manually probe it here. Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c60
-rw-r--r--include/drm/drm_dp_mst_helper.h2
2 files changed, 48 insertions, 14 deletions
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 2f171e66483d..a35a3f8275c7 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -49,6 +49,9 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
int id,
struct drm_dp_payload *payload);
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port,
+ int offset, int size, u8 *bytes);
static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port,
int offset, int size, u8 *bytes);
@@ -427,6 +430,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx
if (idx > raw->curlen)
goto fail_len;
+ idx++;
memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
return true;
fail_len:
@@ -1152,16 +1156,17 @@ static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
if (old_ddps != port->ddps) {
if (port->ddps) {
- drm_dp_check_port_guid(mstb, port);
+ port->needs_update = true;
dowork = true;
} else {
port->guid_valid = false;
port->available_pbn = 0;
+ if (!port->input)
+ drm_dp_port_teardown_pdt(port, old_pdt);
+ port->pdt = 0;
}
}
if (old_pdt != port->pdt && !port->input) {
- drm_dp_port_teardown_pdt(port, old_pdt);
-
if (drm_dp_port_setup_pdt(port))
dowork = true;
}
@@ -1219,6 +1224,20 @@ static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *m
if (!port->ddps)
continue;
+ if (port->needs_update) {
+ u8 dpcd_rev = 0;
+ int ret;
+ ret = drm_dp_send_dpcd_read(mgr, port,
+ 0, 1, &dpcd_rev);
+ port->dpcd_rev = dpcd_rev;
+
+ if (port->dpcd_rev >= 0x12) {
+ drm_dp_send_dpcd_read(mgr, port, DP_GUID, 16, mgr->guid);
+ drm_dp_check_port_guid(mstb, port);
+ }
+ port->needs_update = false;
+ }
+
if (!port->available_pbn)
drm_dp_send_enum_path_resources(mgr, mstb, port);
@@ -1263,7 +1282,6 @@ static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
return true;
}
-#if 0
static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
{
struct drm_dp_sideband_msg_req_body req;
@@ -1276,7 +1294,6 @@ static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32
return 0;
}
-#endif
static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
bool up, u8 *msg, int len)
@@ -1752,26 +1769,43 @@ int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
}
EXPORT_SYMBOL(drm_dp_update_payload_part2);
-#if 0 /* unused as of yet */
static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port,
- int offset, int size)
+ int offset, int size, u8 *bytes)
{
int len;
+ int ret;
struct drm_dp_sideband_msg_tx *txmsg;
+ struct drm_dp_mst_branch *mstb;
+
+ mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+ if (!mstb)
+ return -EINVAL;
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
- if (!txmsg)
- return -ENOMEM;
+ if (!txmsg) {
+ ret = -ENOMEM;
+ goto fail_put;
+ }
- len = build_dpcd_read(txmsg, port->port_num, 0, 8);
- txmsg->dst = port->parent;
+ len = build_dpcd_read(txmsg, port->port_num, offset, size);
+ txmsg->dst = mstb;
drm_dp_queue_down_tx(mgr, txmsg);
- return 0;
+ ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+ if (ret > 0) {
+ if (txmsg->reply.reply_type == 1) {
+ ret = -EINVAL;
+ } else
+ ret = 0;
+ }
+ memcpy(bytes, txmsg->reply.u.remote_dpcd_read_ack.bytes, size);
+ kfree(txmsg);
+fail_put:
+ drm_dp_put_mst_branch_device(mstb);
+ return ret;
}
-#endif
static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port,
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 0f408b002d98..f1e736955ba9 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -69,7 +69,7 @@ struct drm_dp_vcpi {
*/
struct drm_dp_mst_port {
struct kref kref;
-
+ bool needs_update;
/* if dpcd 1.2 device is on this port - its GUID info */
bool guid_valid;
u8 guid[16];