diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/dispnv50/disp.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/dispnv50/disp.c | 311 |
1 files changed, 174 insertions, 137 deletions
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 1ed242070001..b111fe24a06b 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -417,6 +417,40 @@ nv50_outp_atomic_check(struct drm_encoder *encoder, return 0; } +struct nouveau_connector * +nv50_outp_get_new_connector(struct nouveau_encoder *outp, + struct drm_atomic_state *state) +{ + struct drm_connector *connector; + struct drm_connector_state *connector_state; + struct drm_encoder *encoder = to_drm_encoder(outp); + int i; + + for_each_new_connector_in_state(state, connector, connector_state, i) { + if (connector_state->best_encoder == encoder) + return nouveau_connector(connector); + } + + return NULL; +} + +struct nouveau_connector * +nv50_outp_get_old_connector(struct nouveau_encoder *outp, + struct drm_atomic_state *state) +{ + struct drm_connector *connector; + struct drm_connector_state *connector_state; + struct drm_encoder *encoder = to_drm_encoder(outp); + int i; + + for_each_old_connector_in_state(state, connector, connector_state, i) { + if (connector_state->best_encoder == encoder) + return nouveau_connector(connector); + } + + return NULL; +} + /****************************************************************************** * DAC *****************************************************************************/ @@ -558,16 +592,31 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id, struct nouveau_drm *drm = nouveau_drm(drm_dev); struct drm_encoder *encoder; struct nouveau_encoder *nv_encoder; - struct nouveau_connector *nv_connector; + struct drm_connector *connector; struct nouveau_crtc *nv_crtc; + struct drm_connector_list_iter conn_iter; int ret = 0; *enabled = false; + drm_for_each_encoder(encoder, drm->dev) { + struct nouveau_connector *nv_connector = NULL; + nv_encoder = nouveau_encoder(encoder); - nv_connector = nouveau_encoder_connector_get(nv_encoder); + + drm_connector_list_iter_begin(drm_dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->state->best_encoder == encoder) { + nv_connector = nouveau_connector(connector); + break; + } + } + drm_connector_list_iter_end(&conn_iter); + if (!nv_connector) + continue; + nv_crtc = nouveau_crtc(encoder->crtc); - if (!nv_connector || !nv_crtc || nv_encoder->or != port || + if (!nv_crtc || nv_encoder->or != port || nv_crtc->index != dev_id) continue; *enabled = nv_encoder->audio; @@ -578,6 +627,7 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id, } break; } + return ret; } @@ -671,7 +721,8 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) } static void -nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) +nv50_audio_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, + struct drm_display_mode *mode) { struct nouveau_drm *drm = nouveau_drm(encoder->dev); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); @@ -692,7 +743,7 @@ nv50_audio_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) (0x0100 << nv_crtc->index), }; - nv_connector = nouveau_encoder_connector_get(nv_encoder); + nv_connector = nv50_outp_get_new_connector(nv_encoder, state); if (!drm_detect_monitor_audio(nv_connector->edid)) return; @@ -729,7 +780,8 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) } static void -nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) +nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, + struct drm_display_mode *mode) { struct nouveau_drm *drm = nouveau_drm(encoder->dev); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); @@ -758,7 +810,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) int ret; int size; - nv_connector = nouveau_encoder_connector_get(nv_encoder); + nv_connector = nv50_outp_get_new_connector(nv_encoder, state); if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; @@ -804,7 +856,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) + args.pwr.vendor_infoframe_length; nvif_mthd(&disp->disp->object, 0, &args, size); - nv50_audio_enable(encoder, mode); + nv50_audio_enable(encoder, state, mode); /* If SCDC is supported by the downstream monitor, update * divider / scrambling settings to what we programmed above. @@ -833,16 +885,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) #define nv50_mstc(p) container_of((p), struct nv50_mstc, connector) #define nv50_msto(p) container_of((p), struct nv50_msto, encoder) -struct nv50_mstm { - struct nouveau_encoder *outp; - - struct drm_dp_mst_topology_mgr mgr; - - bool modified; - bool disabled; - int links; -}; - struct nv50_mstc { struct nv50_mstm *mstm; struct drm_dp_mst_port *port; @@ -1222,7 +1264,10 @@ nv50_mstc_detect(struct drm_connector *connector, ret = drm_dp_mst_detect_port(connector, ctx, mstc->port->mgr, mstc->port); + if (ret != connector_status_connected) + goto out; +out: pm_runtime_mark_last_busy(connector->dev->dev); pm_runtime_put_autosuspend(connector->dev->dev); return ret; @@ -1371,41 +1416,51 @@ nv50_mstm = { .add_connector = nv50_mstm_add_connector, }; -void -nv50_mstm_service(struct nv50_mstm *mstm) +bool +nv50_mstm_service(struct nouveau_drm *drm, + struct nouveau_connector *nv_connector, + struct nv50_mstm *mstm) { - struct drm_dp_aux *aux = mstm ? mstm->mgr.aux : NULL; - bool handled = true; - int ret; + struct drm_dp_aux *aux = &nv_connector->aux; + bool handled = true, ret = true; + int rc; u8 esi[8] = {}; - if (!aux) - return; - while (handled) { - ret = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8); - if (ret != 8) { - drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); - return; + rc = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8); + if (rc != 8) { + ret = false; + break; } drm_dp_mst_hpd_irq(&mstm->mgr, esi, &handled); if (!handled) break; - drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1], 3); + rc = drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1], + 3); + if (rc != 3) { + ret = false; + break; + } } + + if (!ret) + NV_DEBUG(drm, "Failed to handle ESI on %s: %d\n", + nv_connector->base.name, rc); + + return ret; } void nv50_mstm_remove(struct nv50_mstm *mstm) { - if (mstm) - drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); + mstm->is_mst = false; + drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); } static int -nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state) +nv50_mstm_enable(struct nv50_mstm *mstm, int state) { struct nouveau_encoder *outp = mstm->outp; struct { @@ -1420,106 +1475,85 @@ nv50_mstm_enable(struct nv50_mstm *mstm, u8 dpcd, int state) }; struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev); struct nvif_object *disp = &drm->display->disp.object; - int ret; - - if (dpcd >= 0x12) { - /* Even if we're enabling MST, start with disabling the - * branching unit to clear any sink-side MST topology state - * that wasn't set by us - */ - ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, 0); - if (ret < 0) - return ret; - - if (state) { - /* Now, start initializing */ - ret = drm_dp_dpcd_writeb(mstm->mgr.aux, DP_MSTM_CTRL, - DP_MST_EN); - if (ret < 0) - return ret; - } - } return nvif_mthd(disp, 0, &args, sizeof(args)); } int -nv50_mstm_detect(struct nv50_mstm *mstm, u8 dpcd[8], int allow) +nv50_mstm_detect(struct nouveau_encoder *outp) { + struct nv50_mstm *mstm = outp->dp.mstm; struct drm_dp_aux *aux; int ret; - bool old_state, new_state; - u8 mstm_ctrl; - if (!mstm) + if (!mstm || !mstm->can_mst) return 0; - mutex_lock(&mstm->mgr.lock); - - old_state = mstm->mgr.mst_state; - new_state = old_state; aux = mstm->mgr.aux; - if (old_state) { - /* Just check that the MST hub is still as we expect it */ - ret = drm_dp_dpcd_readb(aux, DP_MSTM_CTRL, &mstm_ctrl); - if (ret < 0 || !(mstm_ctrl & DP_MST_EN)) { - DRM_DEBUG_KMS("Hub gone, disabling MST topology\n"); - new_state = false; - } - } else if (dpcd[0] >= 0x12) { - ret = drm_dp_dpcd_readb(aux, DP_MSTM_CAP, &dpcd[1]); - if (ret < 0) - goto probe_error; - - if (!(dpcd[1] & DP_MST_CAP)) - dpcd[0] = 0x11; - else - new_state = allow; - } - - if (new_state == old_state) { - mutex_unlock(&mstm->mgr.lock); - return new_state; - } - - ret = nv50_mstm_enable(mstm, dpcd[0], new_state); - if (ret) - goto probe_error; - - mutex_unlock(&mstm->mgr.lock); + /* Clear any leftover MST state we didn't set ourselves by first + * disabling MST if it was already enabled + */ + ret = drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0); + if (ret < 0) + return ret; - ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, new_state); + /* And start enabling */ + ret = nv50_mstm_enable(mstm, true); if (ret) - return nv50_mstm_enable(mstm, dpcd[0], 0); + return ret; - return new_state; + ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, true); + if (ret) { + nv50_mstm_enable(mstm, false); + return ret; + } -probe_error: - mutex_unlock(&mstm->mgr.lock); - return ret; + mstm->is_mst = true; + return 1; } static void -nv50_mstm_fini(struct nv50_mstm *mstm) +nv50_mstm_fini(struct nouveau_encoder *outp) { - if (mstm && mstm->mgr.mst_state) + struct nv50_mstm *mstm = outp->dp.mstm; + + if (!mstm) + return; + + /* Don't change the MST state of this connector until we've finished + * resuming, since we can't safely grab hpd_irq_lock in our resume + * path to protect mstm->is_mst without potentially deadlocking + */ + mutex_lock(&outp->dp.hpd_irq_lock); + mstm->suspended = true; + mutex_unlock(&outp->dp.hpd_irq_lock); + + if (mstm->is_mst) drm_dp_mst_topology_mgr_suspend(&mstm->mgr); } static void -nv50_mstm_init(struct nv50_mstm *mstm, bool runtime) +nv50_mstm_init(struct nouveau_encoder *outp, bool runtime) { - int ret; + struct nv50_mstm *mstm = outp->dp.mstm; + int ret = 0; - if (!mstm || !mstm->mgr.mst_state) + if (!mstm) return; - ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime); - if (ret == -1) { - drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); - drm_kms_helper_hotplug_event(mstm->mgr.dev); + if (mstm->is_mst) { + ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime); + if (ret == -1) + nv50_mstm_remove(mstm); } + + mutex_lock(&outp->dp.hpd_irq_lock); + mstm->suspended = false; + mutex_unlock(&outp->dp.hpd_irq_lock); + + if (ret == -1) + drm_kms_helper_hotplug_event(mstm->mgr.dev); } static void @@ -1541,17 +1575,6 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, struct drm_device *dev = outp->base.base.dev; struct nv50_mstm *mstm; int ret; - u8 dpcd; - - /* This is a workaround for some monitors not functioning - * correctly in MST mode on initial module load. I think - * some bad interaction with the VBIOS may be responsible. - * - * A good ol' off and on again seems to work here ;) - */ - ret = drm_dp_dpcd_readb(aux, DP_DPCD_REV, &dpcd); - if (ret >= 0 && dpcd >= 0x12) - drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0); if (!(mstm = *pmstm = kzalloc(sizeof(*mstm), GFP_KERNEL))) return -ENOMEM; @@ -1590,23 +1613,27 @@ nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, } static void -nv50_sor_disable(struct drm_encoder *encoder) +nv50_sor_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + struct nouveau_connector *nv_connector = + nv50_outp_get_old_connector(nv_encoder, state); nv_encoder->crtc = NULL; if (nv_crtc) { - struct nvkm_i2c_aux *aux = nv_encoder->aux; + struct drm_dp_aux *aux = &nv_connector->aux; u8 pwr; - if (aux) { - int ret = nvkm_rdaux(aux, DP_SET_POWER, &pwr, 1); + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + int ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); + if (ret == 0) { pwr &= ~DP_SET_POWER_MASK; pwr |= DP_SET_POWER_D3; - nvkm_wraux(aux, DP_SET_POWER, &pwr, 1); + drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); } } @@ -1618,7 +1645,8 @@ nv50_sor_disable(struct drm_encoder *encoder) } static void -nv50_sor_enable(struct drm_encoder *encoder) +nv50_sor_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); @@ -1642,7 +1670,7 @@ nv50_sor_enable(struct drm_encoder *encoder) u8 proto = NV507D_SOR_SET_CONTROL_PROTOCOL_CUSTOM; u8 depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT; - nv_connector = nouveau_encoder_connector_get(nv_encoder); + nv_connector = nv50_outp_get_new_connector(nv_encoder, state); nv_encoder->crtc = encoder->crtc; if ((disp->disp->object.oclass == GT214_DISP || @@ -1669,7 +1697,7 @@ nv50_sor_enable(struct drm_encoder *encoder) proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B; } - nv50_hdmi_enable(&nv_encoder->base.base, mode); + nv50_hdmi_enable(&nv_encoder->base.base, state, mode); break; case DCB_OUTPUT_LVDS: proto = NV507D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM; @@ -1710,7 +1738,7 @@ nv50_sor_enable(struct drm_encoder *encoder) else proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B; - nv50_audio_enable(encoder, mode); + nv50_audio_enable(encoder, state, mode); break; default: BUG(); @@ -1723,8 +1751,8 @@ nv50_sor_enable(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs nv50_sor_help = { .atomic_check = nv50_outp_atomic_check, - .enable = nv50_sor_enable, - .disable = nv50_sor_disable, + .atomic_enable = nv50_sor_enable, + .atomic_disable = nv50_sor_disable, }; static void @@ -1733,6 +1761,10 @@ nv50_sor_destroy(struct drm_encoder *encoder) struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); nv50_mstm_del(&nv_encoder->dp.mstm); drm_encoder_cleanup(encoder); + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) + mutex_destroy(&nv_encoder->dp.hpd_irq_lock); + kfree(encoder); } @@ -1792,6 +1824,8 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbe->i2c_index); + mutex_init(&nv_encoder->dp.hpd_irq_lock); + if (aux) { if (disp->disp->object.oclass < GF110_DISP) { /* HW has no support for address-only @@ -2035,6 +2069,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) drm_atomic_helper_wait_for_fences(dev, state, false); drm_atomic_helper_wait_for_dependencies(state); drm_atomic_helper_update_legacy_modeset_state(dev, state); + drm_atomic_helper_calc_timestamping_constants(state); if (atom->lock_core) mutex_lock(&disp->mutex); @@ -2083,7 +2118,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) outp->clr.mask, outp->set.mask); if (outp->clr.mask) { - help->disable(encoder); + help->atomic_disable(encoder, state); interlock[NV50_DISP_INTERLOCK_CORE] |= 1; if (outp->flush_disable) { nv50_disp_atomic_commit_wndw(state, interlock); @@ -2122,7 +2157,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) outp->set.mask, outp->clr.mask); if (outp->set.mask) { - help->enable(encoder); + help->atomic_enable(encoder, state); interlock[NV50_DISP_INTERLOCK_CORE] = 1; } @@ -2490,9 +2525,9 @@ nv50_disp_func = { *****************************************************************************/ static void -nv50_display_fini(struct drm_device *dev, bool suspend) +nv50_display_fini(struct drm_device *dev, bool runtime, bool suspend) { - struct nouveau_encoder *nv_encoder; + struct nouveau_drm *drm = nouveau_drm(dev); struct drm_encoder *encoder; struct drm_plane *plane; @@ -2504,11 +2539,12 @@ nv50_display_fini(struct drm_device *dev, bool suspend) } list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { - nv_encoder = nouveau_encoder(encoder); - nv50_mstm_fini(nv_encoder->dp.mstm); - } + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) + nv50_mstm_fini(nouveau_encoder(encoder)); } + + if (!runtime) + cancel_work_sync(&drm->hpd_work); } static int @@ -2525,7 +2561,7 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime) if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - nv50_mstm_init(nv_encoder->dp.mstm, runtime); + nv50_mstm_init(nv_encoder, runtime); } } @@ -2587,10 +2623,11 @@ nv50_display_create(struct drm_device *dev) dev->mode_config.normalize_zpos = true; /* small shared memory area we use for notifiers and semaphores */ - ret = nouveau_bo_new(&drm->client, 4096, 0x1000, TTM_PL_FLAG_VRAM, + ret = nouveau_bo_new(&drm->client, 4096, 0x1000, + NOUVEAU_GEM_DOMAIN_VRAM, 0, 0x0000, NULL, NULL, &disp->sync); if (!ret) { - ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM, true); + ret = nouveau_bo_pin(disp->sync, NOUVEAU_GEM_DOMAIN_VRAM, true); if (!ret) { ret = nouveau_bo_map(disp->sync); if (ret) |