summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/nouveau_dp.c
diff options
context:
space:
mode:
authorLyude Paul <lyude@redhat.com>2020-08-26 21:24:53 +0300
committerLyude Paul <lyude@redhat.com>2020-09-01 02:10:09 +0300
commita4efad354cf1b9d5275adc85f67fcaaa1c2912d8 (patch)
treeea0acea868ea033db9ef1bf27b005ad3769532ef /drivers/gpu/drm/nouveau/nouveau_dp.c
parent4778ff052812029bc076f5e78eff3ba1851cbea7 (diff)
downloadlinux-a4efad354cf1b9d5275adc85f67fcaaa1c2912d8.tar.xz
drm/nouveau/kms/nv50-: Add support for DP_SINK_COUNT
This is another bit that we never implemented for nouveau: dongle detection. When a "dongle", e.g. an active display adaptor, is hooked up to the system and causes an HPD to be fired, we don't actually know whether or not there's anything plugged into the dongle without checking the sink count. As a result, plugging in a dongle without anything plugged into it currently results in a bogus EDID retrieval error in the kernel log. Additionally, most dongles won't send another long HPD signal if the user suddenly plugs something in, they'll only send a short HPD IRQ with the expectation that the source will check the sink count and reprobe the connector if it's changed - something we don't actually do. As a result, nothing will happen if the user plugs the dongle in before plugging something into the dongle. So, let's fix this by checking the sink count in both nouveau_dp_probe_dpcd() and nouveau_dp_irq(), and reprobing the connector if things change. Signed-off-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Ben Skeggs <bskeggs@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20200826182456.322681-18-lyude@redhat.com
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_dp.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c53
1 files changed, 48 insertions, 5 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index ad852e572cfe..59be357b17e0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -36,12 +36,21 @@ MODULE_PARM_DESC(mst, "Enable DisplayPort multi-stream (default: enabled)");
static int nouveau_mst = 1;
module_param_named(mst, nouveau_mst, int, 0400);
+static bool
+nouveau_dp_has_sink_count(struct drm_connector *connector,
+ struct nouveau_encoder *outp)
+{
+ return drm_dp_read_sink_count_cap(connector, outp->dp.dpcd, &outp->dp.desc);
+}
+
static enum drm_connector_status
nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
struct nouveau_encoder *outp)
{
+ struct drm_connector *connector = &nv_connector->base;
struct drm_dp_aux *aux = &nv_connector->aux;
struct nv50_mstm *mstm = NULL;
+ enum drm_connector_status status = connector_status_disconnected;
int ret;
u8 *dpcd = outp->dp.dpcd;
@@ -50,9 +59,9 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
ret = drm_dp_read_desc(aux, &outp->dp.desc,
drm_dp_is_branch(dpcd));
if (ret < 0)
- return connector_status_disconnected;
+ goto out;
} else {
- return connector_status_disconnected;
+ goto out;
}
if (nouveau_mst) {
@@ -61,12 +70,33 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector,
mstm->can_mst = drm_dp_read_mst_cap(aux, dpcd);
}
+ if (nouveau_dp_has_sink_count(connector, outp)) {
+ ret = drm_dp_read_sink_count(aux);
+ if (ret < 0)
+ goto out;
+
+ outp->dp.sink_count = ret;
+
+ /*
+ * Dongle connected, but no display. Don't bother reading
+ * downstream port info
+ */
+ if (!outp->dp.sink_count)
+ return connector_status_disconnected;
+ }
+
ret = drm_dp_read_downstream_info(aux, dpcd,
outp->dp.downstream_ports);
if (ret < 0)
- return connector_status_disconnected;
+ goto out;
- return connector_status_connected;
+ status = connector_status_connected;
+out:
+ if (status != connector_status_connected) {
+ /* Clear any cached info */
+ outp->dp.sink_count = 0;
+ }
+ return status;
}
int
@@ -159,6 +189,8 @@ void nouveau_dp_irq(struct nouveau_drm *drm,
struct drm_connector *connector = &nv_connector->base;
struct nouveau_encoder *outp = find_encoder(connector, DCB_OUTPUT_DP);
struct nv50_mstm *mstm;
+ int ret;
+ bool send_hpd = false;
if (!outp)
return;
@@ -170,12 +202,23 @@ void nouveau_dp_irq(struct nouveau_drm *drm,
if (mstm && mstm->is_mst) {
if (!nv50_mstm_service(drm, nv_connector, mstm))
- nouveau_connector_hpd(connector);
+ send_hpd = true;
} else {
drm_dp_cec_irq(&nv_connector->aux);
+
+ if (nouveau_dp_has_sink_count(connector, outp)) {
+ ret = drm_dp_read_sink_count(&nv_connector->aux);
+ if (ret != outp->dp.sink_count)
+ send_hpd = true;
+ if (ret >= 0)
+ outp->dp.sink_count = ret;
+ }
}
mutex_unlock(&outp->dp.hpd_irq_lock);
+
+ if (send_hpd)
+ nouveau_connector_hpd(connector);
}
/* TODO: