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: diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index c1924a4529a7..21937f1c7dd9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -74,6 +74,8 @@ struct nouveau_encoder { u8 dpcd[DP_RECEIVER_CAP_SIZE]; u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct drm_dp_desc desc; + + u8 sink_count; } dp; };