summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/display/drm_dp_mst_topology.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/display/drm_dp_mst_topology.c')
-rw-r--r--drivers/gpu/drm/display/drm_dp_mst_topology.c138
1 files changed, 85 insertions, 53 deletions
diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c
index fc2ceae61db2..ac90118b9e7a 100644
--- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
@@ -89,7 +89,7 @@ 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);
static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
- u8 *guid);
+ guid_t *guid);
static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port);
static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port);
@@ -801,7 +801,7 @@ static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_
int idx = 1;
int i;
- memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
+ import_guid(&repmsg->u.link_addr.guid, &raw->msg[idx]);
idx += 16;
repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
idx++;
@@ -829,7 +829,7 @@ static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_
idx++;
if (idx > raw->curlen)
goto fail_len;
- memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
+ import_guid(&repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx]);
idx += 16;
if (idx > raw->curlen)
goto fail_len;
@@ -1029,7 +1029,7 @@ static bool drm_dp_sideband_parse_reply(const struct drm_dp_mst_topology_mgr *mg
msg->req_type = (raw->msg[0] & 0x7f);
if (msg->reply_type == DP_SIDEBAND_REPLY_NAK) {
- memcpy(msg->u.nak.guid, &raw->msg[1], 16);
+ import_guid(&msg->u.nak.guid, &raw->msg[1]);
msg->u.nak.reason = raw->msg[17];
msg->u.nak.nak_data = raw->msg[18];
return false;
@@ -1078,7 +1078,7 @@ drm_dp_sideband_parse_connection_status_notify(const struct drm_dp_mst_topology_
if (idx > raw->curlen)
goto fail_len;
- memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
+ import_guid(&msg->u.conn_stat.guid, &raw->msg[idx]);
idx += 16;
if (idx > raw->curlen)
goto fail_len;
@@ -1107,7 +1107,7 @@ static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst
if (idx > raw->curlen)
goto fail_len;
- memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
+ import_guid(&msg->u.resource_stat.guid, &raw->msg[idx]);
idx += 16;
if (idx > raw->curlen)
goto fail_len;
@@ -2174,20 +2174,24 @@ ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
offset, size, buffer);
}
-static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid)
+static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, guid_t *guid)
{
int ret = 0;
- memcpy(mstb->guid, guid, 16);
+ guid_copy(&mstb->guid, guid);
+
+ if (!drm_dp_validate_guid(mstb->mgr, &mstb->guid)) {
+ u8 buf[UUID_SIZE];
+
+ export_guid(buf, &mstb->guid);
- if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) {
if (mstb->port_parent) {
ret = drm_dp_send_dpcd_write(mstb->mgr,
mstb->port_parent,
- DP_GUID, 16, mstb->guid);
+ DP_GUID, sizeof(buf), buf);
} else {
ret = drm_dp_dpcd_write(mstb->mgr->aux,
- DP_GUID, mstb->guid, 16);
+ DP_GUID, buf, sizeof(buf));
}
}
@@ -2339,7 +2343,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
{
struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
struct drm_dp_mst_port *port;
- int old_ddps = 0, ret;
+ int ret;
u8 new_pdt = DP_PEER_DEVICE_NONE;
bool new_mcs = 0;
bool created = false, send_link_addr = false, changed = false;
@@ -2372,7 +2376,6 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
*/
drm_modeset_lock(&mgr->base.lock, NULL);
- old_ddps = port->ddps;
changed = port->ddps != port_msg->ddps ||
(port->ddps &&
(port->ldps != port_msg->legacy_device_plug_status ||
@@ -2407,15 +2410,13 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,
* Reprobe PBN caps on both hotplug, and when re-probing the link
* for our parent mstb
*/
- if (old_ddps != port->ddps || !created) {
- if (port->ddps && !port->input) {
- ret = drm_dp_send_enum_path_resources(mgr, mstb,
- port);
- if (ret == 1)
- changed = true;
- } else {
- port->full_pbn = 0;
- }
+ if (port->ddps && !port->input) {
+ ret = drm_dp_send_enum_path_resources(mgr, mstb,
+ port);
+ if (ret == 1)
+ changed = true;
+ } else {
+ port->full_pbn = 0;
}
ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);
@@ -2570,9 +2571,9 @@ out:
return mstb;
}
-static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
- struct drm_dp_mst_branch *mstb,
- const uint8_t *guid)
+static struct drm_dp_mst_branch *
+get_mst_branch_device_by_guid_helper(struct drm_dp_mst_branch *mstb,
+ const guid_t *guid)
{
struct drm_dp_mst_branch *found_mstb;
struct drm_dp_mst_port *port;
@@ -2580,10 +2581,9 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
if (!mstb)
return NULL;
- if (memcmp(mstb->guid, guid, 16) == 0)
+ if (guid_equal(&mstb->guid, guid))
return mstb;
-
list_for_each_entry(port, &mstb->ports, next) {
found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
@@ -2596,7 +2596,7 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
static struct drm_dp_mst_branch *
drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr *mgr,
- const uint8_t *guid)
+ const guid_t *guid)
{
struct drm_dp_mst_branch *mstb;
int ret;
@@ -2692,18 +2692,18 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work)
drm_kms_helper_hotplug_event(dev);
}
-static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
- u8 *guid)
+static void drm_dp_mst_queue_probe_work(struct drm_dp_mst_topology_mgr *mgr)
{
- u64 salt;
+ queue_work(system_long_wq, &mgr->work);
+}
- if (memchr_inv(guid, 0, 16))
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+ guid_t *guid)
+{
+ if (!guid_is_null(guid))
return true;
- salt = get_jiffies_64();
-
- memcpy(&guid[0], &salt, sizeof(u64));
- memcpy(&guid[8], &salt, sizeof(u64));
+ guid_gen(guid);
return false;
}
@@ -2943,7 +2943,7 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
drm_dbg_kms(mgr->dev, "link address reply: %d\n", reply->nports);
drm_dp_dump_link_address(mgr, reply);
- ret = drm_dp_check_mstb_guid(mstb, reply->guid);
+ ret = drm_dp_check_mstb_guid(mstb, &reply->guid);
if (ret) {
char buf[64];
@@ -3685,7 +3685,7 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
/* Write reset payload */
drm_dp_dpcd_write_payload(mgr, 0, 0, 0x3f);
- queue_work(system_long_wq, &mgr->work);
+ drm_dp_mst_queue_probe_work(mgr);
ret = 0;
} else {
@@ -3724,6 +3724,33 @@ drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb)
}
/**
+ * drm_dp_mst_topology_queue_probe - Queue a topology probe
+ * @mgr: manager to probe
+ *
+ * Queue a work to probe the MST topology. Driver's should call this only to
+ * sync the topology's HW->SW state after the MST link's parameters have
+ * changed in a way the state could've become out-of-sync. This is the case
+ * for instance when the link rate between the source and first downstream
+ * branch device has switched between UHBR and non-UHBR rates. Except of those
+ * cases - for instance when a sink gets plugged/unplugged to a port - the SW
+ * state will get updated automatically via MST UP message notifications.
+ */
+void drm_dp_mst_topology_queue_probe(struct drm_dp_mst_topology_mgr *mgr)
+{
+ mutex_lock(&mgr->lock);
+
+ if (drm_WARN_ON(mgr->dev, !mgr->mst_state || !mgr->mst_primary))
+ goto out_unlock;
+
+ drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary);
+ drm_dp_mst_queue_probe_work(mgr);
+
+out_unlock:
+ mutex_unlock(&mgr->lock);
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_queue_probe);
+
+/**
* drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
* @mgr: manager to suspend
*
@@ -3770,8 +3797,9 @@ EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
bool sync)
{
+ u8 buf[UUID_SIZE];
+ guid_t guid;
int ret;
- u8 guid[16];
mutex_lock(&mgr->lock);
if (!mgr->mst_primary)
@@ -3792,13 +3820,15 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
}
/* Some hubs forget their guids after they resume */
- ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
- if (ret != 16) {
+ ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
goto out_fail;
}
- ret = drm_dp_check_mstb_guid(mgr->mst_primary, guid);
+ import_guid(&guid, buf);
+
+ ret = drm_dp_check_mstb_guid(mgr->mst_primary, &guid);
if (ret) {
drm_dbg_kms(mgr->dev, "check mstb failed - undocked during suspend?\n");
goto out_fail;
@@ -3809,7 +3839,7 @@ int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr,
* state of our in-memory topology back into sync with reality. So,
* restart the probing process as if we're probing a new hub
*/
- queue_work(system_long_wq, &mgr->work);
+ drm_dp_mst_queue_probe_work(mgr);
mutex_unlock(&mgr->lock);
if (sync) {
@@ -3976,12 +4006,12 @@ drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
bool hotplug = false, dowork = false;
if (hdr->broadcast) {
- const u8 *guid = NULL;
+ const guid_t *guid = NULL;
if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY)
- guid = msg->u.conn_stat.guid;
+ guid = &msg->u.conn_stat.guid;
else if (msg->req_type == DP_RESOURCE_STATUS_NOTIFY)
- guid = msg->u.resource_stat.guid;
+ guid = &msg->u.resource_stat.guid;
if (guid)
mstb = drm_dp_get_mst_branch_device_by_guid(mgr, guid);
@@ -4963,7 +4993,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
seq_printf(m, "branch oui: %*phN devid: ", 3, buf);
for (i = 0x3; i < 0x8 && buf[i]; i++)
- seq_printf(m, "%c", buf[i]);
+ seq_putc(m, buf[i]);
seq_printf(m, " revision: hw: %x.%x sw: %x.%x\n",
buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]);
if (dump_dp_payload_table(mgr, buf))
@@ -5569,7 +5599,6 @@ EXPORT_SYMBOL(drm_dp_mst_atomic_check_mgr);
* drm_dp_atomic_release_time_slots()
*
* Returns:
- *
* 0 if the new state is valid, negative error code otherwise.
*/
int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
@@ -5606,7 +5635,6 @@ EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs);
* topology object.
*
* RETURNS:
- *
* The MST topology state or error pointer.
*/
struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
@@ -5626,7 +5654,6 @@ EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
* topology object.
*
* Returns:
- *
* The old MST topology state, or NULL if there's no topology state for this MST mgr
* in the global atomic state
*/
@@ -5651,7 +5678,6 @@ EXPORT_SYMBOL(drm_atomic_get_old_mst_topology_state);
* topology object.
*
* Returns:
- *
* The new MST topology state, or NULL if there's no topology state for this MST mgr
* in the global atomic state
*/
@@ -6057,6 +6083,7 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
struct drm_dp_aux *immediate_upstream_aux;
struct drm_dp_mst_port *fec_port;
struct drm_dp_desc desc = {};
+ u8 upstream_dsc;
u8 endpoint_fec;
u8 endpoint_dsc;
@@ -6083,8 +6110,6 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
/* 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;
@@ -6130,6 +6155,13 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD)) {
u8 dpcd_ext[DP_RECEIVER_CAP_SIZE];
+ if (drm_dp_dpcd_read(immediate_upstream_aux,
+ DP_DSC_SUPPORT, &upstream_dsc, 1) != 1)
+ return NULL;
+
+ if (!(upstream_dsc & DP_DSC_DECOMPRESSION_IS_SUPPORTED))
+ return NULL;
+
if (drm_dp_read_dpcd_caps(immediate_upstream_aux, dpcd_ext) < 0)
return NULL;