diff options
author | Dave Airlie <airlied@redhat.com> | 2023-09-29 01:27:00 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2023-09-29 01:27:15 +0300 |
commit | 79fb229b8810071648b65c37382aea7819a5f935 (patch) | |
tree | bf201cd7732ad48ad98a963344cb535b95653804 /drivers/gpu/drm/nouveau | |
parent | f107ff76a8c242b298413ef52db9978dc3fe0153 (diff) | |
parent | 78f54469b871db5ba8ea49abd4e5994e97bd525b (diff) | |
download | linux-79fb229b8810071648b65c37382aea7819a5f935.tar.xz |
Merge tag 'drm-misc-next-2023-09-27' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v6.7-rc1:
UAPI Changes:
- drm_file owner is now updated during use, in the case of a drm fd
opened by the display server for a client, the correct owner is
displayed.
- Qaic gains support for the QAIC_DETACH_SLICE_BO ioctl to allow bo
recycling.
Cross-subsystem Changes:
- Disable boot logo for au1200fb, mmpfb and unexport logo helpers.
Only fbcon should manage display of logo.
- Update freescale in MAINTAINERS.
- Add some bridge files to bridge in MAINTAINERS.
- Update gma500 driver repo in MAINTAINERS to point to drm-misc.
Core Changes:
- Move size computations to drm buddy allocator.
- Make drm_atomic_helper_shutdown(NULL) a nop.
- Assorted small fixes in drm_debugfs, DP-MST payload addition error handling.
- Fix DRM_BRIDGE_ATTACH_NO_CONNECTOR handling.
- Handle bad (h/v)sync_end in EDID by clipping to htotal.
- Build GPUVM as a module.
Driver Changes:
- Simple drivers don't need to cache prepared result.
- Call drm_atomic_helper_shutdown() in shutdown/unbind for a whole lot
more drm drivers.
- Assorted small fixes in amdgpu, ssd130x, bridge/it6621, accel/qaic,
nouveau, tc358768.
- Add NV12 for komeda writeback.
- Add arbitration lost event to synopsis/dw-hdmi-cec.
- Speed up s/r in nouveau by not restoring some big bo's.
- Assorted nouveau display rework in preparation for GSP-RM,
especially related to how the modeset sequence works and
the DP sequence in relation to link training.
- Update anx7816 panel.
- Support NVSYNC and NHSYNC in tegra.
- Allow multiple power domains in simple driver.
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/f1fae5eb-25b8-192a-9a53-215e1184ce81@linux.intel.com
Diffstat (limited to 'drivers/gpu/drm/nouveau')
59 files changed, 2499 insertions, 1242 deletions
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index c52e8096cca4..1e6aaf95ff7c 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -11,6 +11,7 @@ config DRM_NOUVEAU select DRM_TTM select DRM_TTM_HELPER select DRM_EXEC + select DRM_GPUVM select DRM_SCHED select I2C select I2C_ALGOBIT diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index e9ac3fb27ff7..13705c5f1497 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -256,7 +256,7 @@ nv04_display_create(struct drm_device *dev) for (i = 0; i < dcb->entries; i++) { struct dcb_output *dcbent = &dcb->entry[i]; - connector = nouveau_connector_create(dev, dcbent); + connector = nouveau_connector_create(dev, dcbent->connector); if (IS_ERR(connector)) continue; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index bba01fa0780c..52f1569ee37c 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -66,8 +66,6 @@ #include "nouveau_fence.h" #include "nv50_display.h" -#include <subdev/bios/dp.h> - /****************************************************************************** * EVO channel *****************************************************************************/ @@ -477,7 +475,6 @@ nv50_dac_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *st core->func->dac->ctrl(core, nv_encoder->outp.or.id, ctrl, NULL); nv_encoder->crtc = NULL; - nvif_outp_release(&nv_encoder->outp); } static void @@ -502,7 +499,8 @@ nv50_dac_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta ctrl |= NVDEF(NV507D, DAC_SET_CONTROL, PROTOCOL, RGB_CRT); - nvif_outp_acquire_rgb_crt(&nv_encoder->outp); + if (!nvif_outp_acquired(&nv_encoder->outp)) + nvif_outp_acquire_dac(&nv_encoder->outp); core->func->dac->ctrl(core, nv_encoder->outp.or.id, ctrl, asyh); asyh->or.depth = 0; @@ -553,34 +551,27 @@ nv50_dac_func = { }; static int -nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) +nv50_dac_create(struct nouveau_encoder *nv_encoder) { + struct drm_connector *connector = &nv_encoder->conn->base; struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nv50_disp *disp = nv50_disp(connector->dev); struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus; - struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + struct dcb_output *dcbe = nv_encoder->dcb; int type = DRM_MODE_ENCODER_DAC; - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; - bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); if (bus) nv_encoder->i2c = &bus->i2c; encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type, "dac-%04x-%04x", dcbe->hasht, dcbe->hashm); drm_encoder_helper_add(encoder, &nv50_dac_help); drm_connector_attach_encoder(connector, encoder); - return nvif_outp_ctor(disp->disp, nv_encoder->base.base.name, dcbe->id, &nv_encoder->outp); + return 0; } /* @@ -617,7 +608,7 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id, continue; /* TODO */ nv_encoder = nouveau_encoder(encoder); - nv_connector = nouveau_connector(nv_encoder->audio.connector); + nv_connector = nv_encoder->conn; nv_crtc = nouveau_crtc(nv_encoder->crtc); if (!nv_crtc || nv_encoder->outp.or.id != port || nv_crtc->index != dev_id) @@ -713,6 +704,18 @@ nv50_audio_supported(struct drm_encoder *encoder) disp->disp->object.oclass == GT206_DISP) return false; + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + switch (nv_encoder->dcb->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_DP: + break; + default: + return false; + } + } + return true; } @@ -729,7 +732,6 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) mutex_lock(&drm->audio.lock); if (nv_encoder->audio.enabled) { nv_encoder->audio.enabled = false; - nv_encoder->audio.connector = NULL; nvif_outp_hda_eld(&nv_encoder->outp, nv_crtc->index, NULL, 0); } mutex_unlock(&drm->audio.lock); @@ -754,7 +756,6 @@ nv50_audio_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, nvif_outp_hda_eld(&nv_encoder->outp, nv_crtc->index, nv_connector->base.eld, drm_eld_size(nv_connector->base.eld)); nv_encoder->audio.enabled = true; - nv_encoder->audio.connector = &nv_connector->base; mutex_unlock(&drm->audio.lock); @@ -774,7 +775,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, struct drm_hdmi_info *hdmi = &nv_connector->base.display_info.hdmi; union hdmi_infoframe infoframe = { 0 }; const u8 rekey = 56; /* binary driver, and tegra, constant */ - u8 scdc = 0; u32 max_ac_packet; struct { struct nvif_outp_infoframe_v0 infoframe; @@ -787,8 +787,9 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, max_ac_packet -= 18; /* constant from tegra */ max_ac_packet /= 32; - if (hdmi->scdc.scrambling.supported) { + if (nv_encoder->i2c && hdmi->scdc.scrambling.supported) { const bool high_tmds_clock_ratio = mode->clock > 340000; + u8 scdc; ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &scdc); if (ret < 0) { @@ -808,8 +809,9 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, scdc, ret); } - ret = nvif_outp_acquire_tmds(&nv_encoder->outp, nv_crtc->index, true, - max_ac_packet, rekey, scdc, hda); + ret = nvif_outp_hdmi(&nv_encoder->outp, nv_crtc->index, true, max_ac_packet, rekey, + mode->clock, hdmi->scdc.supported, hdmi->scdc.scrambling.supported, + hdmi->scdc.scrambling.low_rates); if (ret) return; @@ -838,7 +840,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, nvif_outp_infoframe(&nv_encoder->outp, NVIF_OUTP_INFOFRAME_V0_VSI, &args.infoframe, size); - nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode); + nv_encoder->hdmi.enabled = true; } /****************************************************************************** @@ -865,6 +867,8 @@ struct nv50_msto { struct nv50_mstc *mstc; bool disabled; bool enabled; + + u32 display_id; }; struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) @@ -893,10 +897,17 @@ nv50_msto_cleanup(struct drm_atomic_state *state, drm_atomic_get_old_mst_topology_state(state, mgr); const struct drm_dp_mst_atomic_payload *old_payload = drm_atomic_get_mst_payload_state(old_mst_state, msto->mstc->port); + struct nv50_mstc *mstc = msto->mstc; + struct nv50_mstm *mstm = mstc->mstm; NV_ATOMIC(drm, "%s: msto cleanup\n", msto->encoder.name); if (msto->disabled) { + if (msto->head->func->display_id) { + nvif_outp_dp_mst_id_put(&mstm->outp->outp, msto->display_id); + msto->display_id = 0; + } + msto->mstc = NULL; msto->disabled = false; drm_dp_remove_payload_part2(mgr, new_mst_state, old_payload, new_payload); @@ -916,23 +927,27 @@ nv50_msto_prepare(struct drm_atomic_state *state, struct nv50_mstc *mstc = msto->mstc; struct nv50_mstm *mstm = mstc->mstm; struct drm_dp_mst_atomic_payload *payload; + int ret = 0; NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name); payload = drm_atomic_get_mst_payload_state(mst_state, mstc->port); - // TODO: Figure out if we want to do a better job of handling VCPI allocation failures here? if (msto->disabled) { drm_dp_remove_payload_part1(mgr, mst_state, payload); - nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, 0, 0, 0, 0); + ret = 1; } else { if (msto->enabled) - drm_dp_add_payload_part1(mgr, mst_state, payload); + ret = drm_dp_add_payload_part1(mgr, mst_state, payload); + } + if (ret == 0) { nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, payload->vc_start_slot, payload->time_slots, payload->pbn, payload->time_slots * mst_state->pbn_div); + } else { + nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, 0, 0, 0, 0); } } @@ -1029,8 +1044,13 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st return; if (!mstm->links++) { - /*XXX: MST audio. */ - nvif_outp_acquire_dp(&mstm->outp->outp, mstm->outp->dp.dpcd, 0, 0, false, true); + nvif_outp_acquire_sor(&mstm->outp->outp, false /*TODO: MST audio... */); + nouveau_dp_train(mstm->outp, true, 0, 0); + } + + if (head->func->display_id) { + if (!WARN_ON(nvif_outp_dp_mst_id_get(&mstm->outp->outp, &msto->display_id))) + head->func->display_id(head, msto->display_id); } if (mstm->outp->outp.or.link & 1) @@ -1053,6 +1073,9 @@ nv50_msto_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *s struct nv50_mstc *mstc = msto->mstc; struct nv50_mstm *mstm = mstc->mstm; + if (msto->head->func->display_id) + msto->head->func->display_id(msto->head, 0); + mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); mstm->modified = true; if (!--mstm->links) @@ -1291,6 +1314,12 @@ nv50_mstm_cleanup(struct drm_atomic_state *state, } } + if (mstm->disabled) { + nouveau_dp_power_down(mstm->outp); + nvif_outp_release(&mstm->outp->outp); + mstm->disabled = false; + } + mstm->modified = false; } @@ -1325,12 +1354,6 @@ nv50_mstm_prepare(struct drm_atomic_state *state, nv50_msto_prepare(state, mst_state, &mstm->mgr, msto); } } - - if (mstm->disabled) { - if (!mstm->links) - nvif_outp_release(&mstm->outp->outp); - mstm->disabled = false; - } } static struct drm_connector * @@ -1536,7 +1559,7 @@ static void nv50_sor_atomic_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 nv50_head *head = nv50_head(nv_encoder->crtc); struct nouveau_connector *nv_connector = nv50_outp_get_old_connector(state, nv_encoder); #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); @@ -1544,7 +1567,6 @@ nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *st #endif struct drm_dp_aux *aux = &nv_connector->aux; int ret; - u8 pwr; #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT if (backlight && backlight->uses_dpcd) { @@ -1555,19 +1577,20 @@ nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *st } #endif - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { - ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); - - if (ret == 0) { - pwr &= ~DP_SET_POWER_MASK; - pwr |= DP_SET_POWER_D3; - drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); - } + if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS && nv_encoder->hdmi.enabled) { + nvif_outp_hdmi(&nv_encoder->outp, head->base.index, + false, 0, 0, 0, false, false, false); + nv_encoder->hdmi.enabled = false; } - nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); - nv50_audio_disable(encoder, nv_crtc); - nvif_outp_release(&nv_encoder->outp); + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) + nouveau_dp_power_down(nv_encoder); + + if (head->func->display_id) + head->func->display_id(head, 0); + + nv_encoder->update(nv_encoder, head->base.index, NULL, 0, 0); + nv50_audio_disable(encoder, &head->base); nv_encoder->crtc = NULL; } @@ -1580,6 +1603,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta nv50_head_atom(drm_atomic_get_new_crtc_state(state, &nv_crtc->base)); struct drm_display_mode *mode = &asyh->state.adjusted_mode; struct nv50_disp *disp = nv50_disp(encoder->dev); + struct nv50_head *head = nv50_head(&nv_crtc->base); struct nvif_outp *outp = &nv_encoder->outp; struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); @@ -1597,15 +1621,17 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta if ((disp->disp->object.oclass == GT214_DISP || disp->disp->object.oclass >= GF110_DISP) && + nv_encoder->dcb->type != DCB_OUTPUT_LVDS && drm_detect_monitor_audio(nv_connector->edid)) hda = true; + if (!nvif_outp_acquired(outp)) + nvif_outp_acquire_sor(outp, hda); + switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: - if (disp->disp->object.oclass == NV50_DISP || - !drm_detect_hdmi_monitor(nv_connector->edid)) - nvif_outp_acquire_tmds(outp, nv_crtc->index, false, 0, 0, 0, false); - else + if (disp->disp->object.oclass != NV50_DISP && + drm_detect_hdmi_monitor(nv_connector->edid)) nv50_hdmi_enable(encoder, nv_crtc, nv_connector, state, mode, hda); if (nv_encoder->outp.or.link & 1) { @@ -1651,10 +1677,10 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta lvds_8bpc = true; } - nvif_outp_acquire_lvds(&nv_encoder->outp, lvds_dual, lvds_8bpc); + nvif_outp_lvds(&nv_encoder->outp, lvds_dual, lvds_8bpc); break; case DCB_OUTPUT_DP: - nvif_outp_acquire_dp(&nv_encoder->outp, nv_encoder->dp.dpcd, 0, 0, hda, false); + nouveau_dp_train(nv_encoder, false, mode->clock, asyh->or.bpc); depth = nv50_dp_bpc_to_depth(asyh->or.bpc); if (nv_encoder->outp.or.link & 1) @@ -1662,8 +1688,6 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta else proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B; - nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode); - #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT backlight = nv_connector->backlight; if (backlight && backlight->uses_dpcd) @@ -1677,6 +1701,9 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta break; } + if (head->func->display_id) + head->func->display_id(head, BIT(nv_encoder->outp.id)); + nv_encoder->update(nv_encoder, nv_crtc->index, asyh, proto, depth); } @@ -1692,14 +1719,13 @@ nv50_sor_destroy(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - nvif_outp_dtor(&nv_encoder->outp); - 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); + nvif_outp_dtor(&nv_encoder->outp); kfree(encoder); } @@ -1708,24 +1734,15 @@ nv50_sor_func = { .destroy = nv50_sor_destroy, }; -bool nv50_has_mst(struct nouveau_drm *drm) -{ - struct nvkm_bios *bios = nvxx_bios(&drm->client.device); - u32 data; - u8 ver, hdr, cnt, len; - - data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len); - return data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04); -} - static int -nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) +nv50_sor_create(struct nouveau_encoder *nv_encoder) { + struct drm_connector *connector = &nv_encoder->conn->base; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(connector->dev); struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); - struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + struct dcb_output *dcbe = nv_encoder->dcb; struct nv50_disp *disp = nv50_disp(connector->dev); int type, ret; @@ -1738,15 +1755,9 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) break; } - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; nv_encoder->update = nv50_sor_update; encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type, "sor-%04x-%04x", dcbe->hasht, dcbe->hashm); drm_encoder_helper_add(encoder, &nv50_sor_help); @@ -1757,40 +1768,40 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) nv50_outp_dump_caps(drm, nv_encoder); if (dcbe->type == DCB_OUTPUT_DP) { - 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 - * transactions, so we're required to - * use custom I2C-over-AUX code. - */ - nv_encoder->i2c = &aux->i2c; - } else { - nv_encoder->i2c = &nv_connector->aux.ddc; - } - nv_encoder->aux = aux; + if (disp->disp->object.oclass < GF110_DISP) { + /* HW has no support for address-only + * transactions, so we're required to + * use custom I2C-over-AUX code. + */ + struct nvkm_i2c_aux *aux; + + aux = nvkm_i2c_aux_find(i2c, dcbe->i2c_index); + if (!aux) + return -EINVAL; + + nv_encoder->i2c = &aux->i2c; + } else { + nv_encoder->i2c = &nv_connector->aux.ddc; } - if (nv_connector->type != DCB_CONNECTOR_eDP && - nv50_has_mst(drm)) { + if (nv_connector->type != DCB_CONNECTOR_eDP && nv_encoder->outp.info.dp.mst) { ret = nv50_mstm_new(nv_encoder, &nv_connector->aux, 16, nv_connector->base.base.id, &nv_encoder->dp.mstm); if (ret) return ret; } - } else { + } else + if (nv_encoder->outp.info.ddc != NVIF_OUTP_DDC_INVALID) { struct nvkm_i2c_bus *bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); if (bus) nv_encoder->i2c = &bus->i2c; } - return nvif_outp_ctor(disp->disp, nv_encoder->base.base.name, dcbe->id, &nv_encoder->outp); + return 0; } /****************************************************************************** @@ -1817,7 +1828,6 @@ nv50_pior_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *s core->func->pior->ctrl(core, nv_encoder->outp.or.id, ctrl, NULL); nv_encoder->crtc = NULL; - nvif_outp_release(&nv_encoder->outp); } static void @@ -1845,14 +1855,16 @@ nv50_pior_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st default: asyh->or.depth = NV837D_PIOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT; break; } + if (!nvif_outp_acquired(&nv_encoder->outp)) + nvif_outp_acquire_pior(&nv_encoder->outp); + switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC); - nvif_outp_acquire_tmds(&nv_encoder->outp, false, false, 0, 0, 0, false); break; case DCB_OUTPUT_DP: ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC); - nvif_outp_acquire_dp(&nv_encoder->outp, nv_encoder->dp.dpcd, 0, 0, false, false); + nouveau_dp_train(nv_encoder, false, asyh->state.adjusted_mode.clock, 6); break; default: BUG(); @@ -1889,8 +1901,9 @@ nv50_pior_func = { }; static int -nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) +nv50_pior_create(struct nouveau_encoder *nv_encoder) { + struct drm_connector *connector = &nv_encoder->conn->base; struct drm_device *dev = connector->dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nv50_disp *disp = nv50_disp(dev); @@ -1898,18 +1911,18 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) struct nvkm_i2c_bus *bus = NULL; struct nvkm_i2c_aux *aux = NULL; struct i2c_adapter *ddc; - struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + struct dcb_output *dcbe = nv_encoder->dcb; int type; switch (dcbe->type) { case DCB_OUTPUT_TMDS: - bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_EXT(dcbe->extdev)); + bus = nvkm_i2c_bus_find(i2c, nv_encoder->outp.info.ddc); ddc = bus ? &bus->i2c : NULL; type = DRM_MODE_ENCODER_TMDS; break; case DCB_OUTPUT_DP: - aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbe->extdev)); + aux = nvkm_i2c_aux_find(i2c, nv_encoder->outp.info.dp.aux); ddc = aux ? &aux->i2c : NULL; type = DRM_MODE_ENCODER_TMDS; break; @@ -1917,18 +1930,11 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) return -ENODEV; } - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; nv_encoder->i2c = ddc; - nv_encoder->aux = aux; mutex_init(&nv_encoder->dp.hpd_irq_lock); encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type, "pior-%04x-%04x", dcbe->hasht, dcbe->hashm); drm_encoder_helper_add(encoder, &nv50_pior_help); @@ -1938,7 +1944,7 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) disp->core->func->pior->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); nv50_outp_dump_caps(drm, nv_encoder); - return nvif_outp_ctor(disp->disp, nv_encoder->base.base.name, dcbe->id, &nv_encoder->outp); + return 0; } /****************************************************************************** @@ -1952,7 +1958,9 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) struct drm_dp_mst_topology_state *mst_state; struct nouveau_drm *drm = nouveau_drm(state->dev); struct nv50_disp *disp = nv50_disp(drm->dev); + struct nv50_atom *atom = nv50_atom(state); struct nv50_core *core = disp->core; + struct nv50_outp_atom *outp; struct nv50_mstm *mstm; int i; @@ -1975,6 +1983,23 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) if (mstm->modified) nv50_mstm_cleanup(state, mst_state, mstm); } + + list_for_each_entry(outp, &atom->outp, head) { + if (outp->encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(outp->encoder); + + if (outp->enabled) { + nv50_audio_enable(outp->encoder, nouveau_crtc(nv_encoder->crtc), + nv_encoder->conn, NULL, NULL); + outp->enabled = outp->disabled = false; + } else { + if (outp->disabled) { + nvif_outp_release(&nv_encoder->outp); + outp->disabled = false; + } + } + } + } } static void @@ -2066,14 +2091,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) if (outp->clr.mask) { help->atomic_disable(encoder, state); + outp->disabled = true; interlock[NV50_DISP_INTERLOCK_CORE] |= 1; - if (outp->flush_disable) { - nv50_disp_atomic_commit_wndw(state, interlock); - nv50_disp_atomic_commit_core(state, interlock); - memset(interlock, 0x00, sizeof(interlock)); - - flushed = true; - } } } @@ -2093,7 +2112,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_crc_atomic_init_notifier_contexts(state); /* Update output path(s). */ - list_for_each_entry_safe(outp, outt, &atom->outp, head) { + list_for_each_entry(outp, &atom->outp, head) { const struct drm_encoder_helper_funcs *help; struct drm_encoder *encoder; @@ -2105,11 +2124,9 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) if (outp->set.mask) { help->atomic_enable(encoder, state); + outp->enabled = true; interlock[NV50_DISP_INTERLOCK_CORE] = 1; } - - list_del(&outp->head); - kfree(outp); } /* Update head(s). */ @@ -2207,6 +2224,11 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) if (atom->lock_core) mutex_unlock(&disp->mutex); + list_for_each_entry_safe(outp, outt, &atom->outp, head) { + list_del(&outp->head); + kfree(outp); + } + /* Wait for HW to signal completion. */ for_each_new_plane_in_state(state, plane, new_plane_state, i) { struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state); @@ -2355,10 +2377,9 @@ nv50_disp_outp_atomic_check_clr(struct nv50_atom *atom, if (IS_ERR(outp)) return PTR_ERR(outp); - if (outp->encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { - outp->flush_disable = true; + if (outp->encoder->encoder_type == DRM_MODE_ENCODER_DPMST || + nouveau_encoder(outp->encoder)->dcb->type == DCB_OUTPUT_DP) atom->flush_disable = true; - } outp->clr.ctrl = true; atom->lock_core = true; } @@ -2519,6 +2540,104 @@ nv50_display_fini(struct drm_device *dev, bool runtime, bool suspend) cancel_work_sync(&drm->hpd_work); } +static inline void +nv50_display_read_hw_or_state(struct drm_device *dev, struct nv50_disp *disp, + struct nouveau_encoder *outp) +{ + struct drm_crtc *crtc; + struct drm_connector_list_iter conn_iter; + struct drm_connector *conn; + struct nv50_head_atom *armh; + const u32 encoder_mask = drm_encoder_mask(&outp->base.base); + bool found_conn = false, found_head = false; + u8 proto; + int head_idx; + int ret; + + switch (outp->dcb->type) { + case DCB_OUTPUT_TMDS: + ret = nvif_outp_inherit_tmds(&outp->outp, &proto); + break; + case DCB_OUTPUT_DP: + ret = nvif_outp_inherit_dp(&outp->outp, &proto); + break; + case DCB_OUTPUT_LVDS: + ret = nvif_outp_inherit_lvds(&outp->outp, &proto); + break; + case DCB_OUTPUT_ANALOG: + ret = nvif_outp_inherit_rgb_crt(&outp->outp, &proto); + break; + default: + drm_dbg_kms(dev, "Readback for %s not implemented yet, skipping\n", + outp->base.base.name); + drm_WARN_ON(dev, true); + return; + } + + if (ret < 0) + return; + + head_idx = ret; + + drm_for_each_crtc(crtc, dev) { + if (crtc->index != head_idx) + continue; + + armh = nv50_head_atom(crtc->state); + found_head = true; + break; + } + if (drm_WARN_ON(dev, !found_head)) + return; + + /* Figure out which connector is being used by this encoder */ + drm_connector_list_iter_begin(dev, &conn_iter); + nouveau_for_each_non_mst_connector_iter(conn, &conn_iter) { + if (nouveau_connector(conn)->index == outp->dcb->connector) { + found_conn = true; + break; + } + } + drm_connector_list_iter_end(&conn_iter); + if (drm_WARN_ON(dev, !found_conn)) + return; + + armh->state.encoder_mask = encoder_mask; + armh->state.connector_mask = drm_connector_mask(conn); + armh->state.active = true; + armh->state.enable = true; + pm_runtime_get_noresume(dev->dev); + + outp->crtc = crtc; + outp->ctrl = NVVAL(NV507D, SOR_SET_CONTROL, PROTOCOL, proto) | BIT(crtc->index); + + drm_connector_get(conn); + conn->state->crtc = crtc; + conn->state->best_encoder = &outp->base.base; +} + +/* Read back the currently programmed display state */ +static void +nv50_display_read_hw_state(struct nouveau_drm *drm) +{ + struct drm_device *dev = drm->dev; + struct drm_encoder *encoder; + struct drm_modeset_acquire_ctx ctx; + struct nv50_disp *disp = nv50_disp(dev); + int ret; + + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); + + drm_for_each_encoder(encoder, dev) { + if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) + continue; + + nv50_display_read_hw_or_state(dev, disp, nouveau_encoder(encoder)); + } + + DRM_MODESET_LOCK_ALL_END(dev, ctx, ret); +} + static int nv50_display_init(struct drm_device *dev, bool resume, bool runtime) { @@ -2536,6 +2655,9 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime) } } + if (!resume) + nv50_display_read_hw_state(nouveau_drm(dev)); + return 0; } @@ -2562,14 +2684,11 @@ nv50_display_destroy(struct drm_device *dev) int nv50_display_create(struct drm_device *dev) { - struct nvif_device *device = &nouveau_drm(dev)->client.device; struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_table *dcb = &drm->vbios.dcb; struct drm_connector *connector, *tmp; struct nv50_disp *disp; - struct dcb_output *dcbe; - int crtcs, ret, i; - bool has_mst = nv50_has_mst(drm); + int ret, i; + bool has_mst = false; disp = kzalloc(sizeof(*disp), GFP_KERNEL); if (!disp) @@ -2645,20 +2764,92 @@ nv50_display_create(struct drm_device *dev) dev->mode_config.cursor_height = 64; } - /* create crtc objects to represent the hw heads */ - if (disp->disp->object.oclass >= GV100_DISP) - crtcs = nvif_rd32(&device->object, 0x610060) & 0xff; - else - if (disp->disp->object.oclass >= GF110_DISP) - crtcs = nvif_rd32(&device->object, 0x612004) & 0xf; - else - crtcs = 0x3; + /* create encoder/connector objects based on VBIOS DCB table */ + for_each_set_bit(i, &disp->disp->outp_mask, sizeof(disp->disp->outp_mask) * 8) { + struct nouveau_encoder *outp; - for (i = 0; i < fls(crtcs); i++) { - struct nv50_head *head; + outp = kzalloc(sizeof(*outp), GFP_KERNEL); + if (!outp) + break; - if (!(crtcs & (1 << i))) + ret = nvif_outp_ctor(disp->disp, "kmsOutp", i, &outp->outp); + if (ret) { + kfree(outp); continue; + } + + connector = nouveau_connector_create(dev, outp->outp.info.conn); + if (IS_ERR(connector)) { + nvif_outp_dtor(&outp->outp); + kfree(outp); + continue; + } + + outp->base.base.possible_crtcs = outp->outp.info.heads; + outp->base.base.possible_clones = 0; + outp->conn = nouveau_connector(connector); + + outp->dcb = kzalloc(sizeof(*outp->dcb), GFP_KERNEL); + if (!outp->dcb) + break; + + switch (outp->outp.info.proto) { + case NVIF_OUTP_RGB_CRT: + outp->dcb->type = DCB_OUTPUT_ANALOG; + outp->dcb->crtconf.maxfreq = outp->outp.info.rgb_crt.freq_max; + break; + case NVIF_OUTP_TMDS: + outp->dcb->type = DCB_OUTPUT_TMDS; + outp->dcb->duallink_possible = outp->outp.info.tmds.dual; + break; + case NVIF_OUTP_LVDS: + outp->dcb->type = DCB_OUTPUT_LVDS; + outp->dcb->lvdsconf.use_acpi_for_edid = outp->outp.info.lvds.acpi_edid; + break; + case NVIF_OUTP_DP: + outp->dcb->type = DCB_OUTPUT_DP; + outp->dcb->dpconf.link_nr = outp->outp.info.dp.link_nr; + outp->dcb->dpconf.link_bw = outp->outp.info.dp.link_bw; + if (outp->outp.info.dp.mst) + has_mst = true; + break; + default: + WARN_ON(1); + continue; + } + + outp->dcb->heads = outp->outp.info.heads; + outp->dcb->connector = outp->outp.info.conn; + outp->dcb->i2c_index = outp->outp.info.ddc; + + switch (outp->outp.info.type) { + case NVIF_OUTP_DAC : ret = nv50_dac_create(outp); break; + case NVIF_OUTP_SOR : ret = nv50_sor_create(outp); break; + case NVIF_OUTP_PIOR: ret = nv50_pior_create(outp); break; + default: + WARN_ON(1); + continue; + } + + if (ret) { + NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", + i, outp->outp.info.type, outp->outp.info.proto, ret); + } + } + + /* cull any connectors we created that don't have an encoder */ + list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { + if (connector->possible_encoders) + continue; + + NV_WARN(drm, "%s has no encoders, removing\n", + connector->name); + connector->funcs->destroy(connector); + } + + /* create crtc objects to represent the hw heads */ + for_each_set_bit(i, &disp->disp->head_mask, sizeof(disp->disp->head_mask) * 8) { + struct nv50_head *head; head = nv50_head_create(dev, i); if (IS_ERR(head)) { @@ -2684,52 +2875,10 @@ nv50_display_create(struct drm_device *dev) * Once these issues are closed, this should be * removed */ - head->msto->encoder.possible_crtcs = crtcs; - } - } - - /* create encoder/connector objects based on VBIOS DCB table */ - for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { - connector = nouveau_connector_create(dev, dcbe); - if (IS_ERR(connector)) - continue; - - if (dcbe->location == DCB_LOC_ON_CHIP) { - switch (dcbe->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - ret = nv50_sor_create(connector, dcbe); - break; - case DCB_OUTPUT_ANALOG: - ret = nv50_dac_create(connector, dcbe); - break; - default: - ret = -ENODEV; - break; - } - } else { - ret = nv50_pior_create(connector, dcbe); - } - - if (ret) { - NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", - dcbe->location, dcbe->type, - ffs(dcbe->or) - 1, ret); - ret = 0; + head->msto->encoder.possible_crtcs = disp->disp->head_mask; } } - /* cull any connectors we created that don't have an encoder */ - list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { - if (connector->possible_encoders) - continue; - - NV_WARN(drm, "%s has no encoders, removing\n", - connector->name); - connector->funcs->destroy(connector); - } - /* Disable vblank irqs aggressively for power-saving, safe on nv50+ */ dev->vblank_disable_immediate = true; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h index 9d66c9c726c3..5508a7cfd492 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h @@ -83,7 +83,9 @@ struct nv50_outp_atom { struct list_head head; struct drm_encoder *encoder; - bool flush_disable; + + bool disabled; + bool enabled; union nv50_outp_atom_mask { struct { @@ -106,8 +108,6 @@ void nv50_dmac_destroy(struct nv50_dmac *); */ struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder); -bool nv50_has_mst(struct nouveau_drm *drm); - u32 *evo_wait(struct nv50_dmac *, int nr); void evo_kick(u32 *, struct nv50_dmac *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.h b/drivers/gpu/drm/nouveau/dispnv50/head.h index 41c8788dfb31..e9d17037ffcf 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.h +++ b/drivers/gpu/drm/nouveau/dispnv50/head.h @@ -49,6 +49,7 @@ struct nv50_head_func { int (*procamp)(struct nv50_head *, struct nv50_head_atom *); int (*or)(struct nv50_head *, struct nv50_head_atom *); void (*static_wndw_map)(struct nv50_head *, struct nv50_head_atom *); + int (*display_id)(struct nv50_head *, u32 display_id); }; extern const struct nv50_head_func head507d; diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 543f08ceaad6..53b1248c40ec 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -28,6 +28,19 @@ #include <nvhw/class/clc57d.h> static int +headc57d_display_id(struct nv50_head *head, u32 display_id) +{ + struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; + int ret; + + if ((ret = PUSH_WAIT(push, 2))) + return ret; + + PUSH_NVSQ(push, NVC57D, 0x2020 + (head->base.index * 0x400), display_id); + return 0; +} + +static int headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; @@ -250,4 +263,5 @@ headc57d = { .or = headc57d_or, /* TODO: flexible window mappings */ .static_wndw_map = headc37d_static_wndw_map, + .display_id = headc57d_display_id, }; diff --git a/drivers/gpu/drm/nouveau/include/nvif/conn.h b/drivers/gpu/drm/nouveau/include/nvif/conn.h index dc355e1dfafa..406c12a111f9 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/conn.h +++ b/drivers/gpu/drm/nouveau/include/nvif/conn.h @@ -7,6 +7,21 @@ struct nvif_disp; struct nvif_conn { struct nvif_object object; + u32 id; + + struct { + enum { + NVIF_CONN_VGA, + NVIF_CONN_TV, + NVIF_CONN_DVI_I, + NVIF_CONN_DVI_D, + NVIF_CONN_LVDS, + NVIF_CONN_LVDS_SPWG, + NVIF_CONN_HDMI, + NVIF_CONN_DP, + NVIF_CONN_EDP, + } type; + } info; }; int nvif_conn_ctor(struct nvif_disp *, const char *name, int id, struct nvif_conn *); @@ -18,11 +33,6 @@ nvif_conn_id(struct nvif_conn *conn) return conn->object.handle; } -#define NVIF_CONN_HPD_STATUS_UNSUPPORTED 0 /* negative if query fails */ -#define NVIF_CONN_HPD_STATUS_NOT_PRESENT 1 -#define NVIF_CONN_HPD_STATUS_PRESENT 2 -int nvif_conn_hpd_status(struct nvif_conn *); - int nvif_conn_event_ctor(struct nvif_conn *, const char *name, nvif_event_func, u8 types, struct nvif_event *); #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0011.h b/drivers/gpu/drm/nouveau/include/nvif/if0011.h index 69b0b779f942..3ed0ddd75bd8 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/if0011.h +++ b/drivers/gpu/drm/nouveau/include/nvif/if0011.h @@ -7,6 +7,16 @@ union nvif_conn_args { __u8 version; __u8 id; /* DCB connector table index. */ __u8 pad02[6]; +#define NVIF_CONN_V0_VGA 0x00 +#define NVIF_CONN_V0_TV 0x01 +#define NVIF_CONN_V0_DVI_I 0x02 +#define NVIF_CONN_V0_DVI_D 0x03 +#define NVIF_CONN_V0_LVDS 0x04 +#define NVIF_CONN_V0_LVDS_SPWG 0x05 +#define NVIF_CONN_V0_HDMI 0x06 +#define NVIF_CONN_V0_DP 0x07 +#define NVIF_CONN_V0_EDP 0x08 + __u8 type; } v0; }; @@ -20,15 +30,4 @@ union nvif_conn_event_args { __u8 pad02[6]; } v0; }; - -#define NVIF_CONN_V0_HPD_STATUS 0x00000000 - -union nvif_conn_hpd_status_args { - struct nvif_conn_hpd_status_v0 { - __u8 version; - __u8 support; - __u8 present; - __u8 pad03[5]; - } v0; -}; #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0012.h b/drivers/gpu/drm/nouveau/include/nvif/if0012.h index 16d4ad5023a3..bde9bfae8d11 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/if0012.h +++ b/drivers/gpu/drm/nouveau/include/nvif/if0012.h @@ -8,18 +8,86 @@ union nvif_outp_args { struct nvif_outp_v0 { __u8 version; __u8 id; /* DCB device index. */ - __u8 pad02[6]; +#define NVIF_OUTP_V0_TYPE_DAC 0x00 +#define NVIF_OUTP_V0_TYPE_SOR 0x01 +#define NVIF_OUTP_V0_TYPE_PIOR 0x02 + __u8 type; +#define NVIF_OUTP_V0_PROTO_RGB_CRT 0x00 +#define NVIF_OUTP_V0_PROTO_TMDS 0x01 +#define NVIF_OUTP_V0_PROTO_LVDS 0x02 +#define NVIF_OUTP_V0_PROTO_DP 0x03 + __u8 proto; + __u8 heads; + __u8 ddc; + __u8 conn; + union { + struct { + __u32 freq_max; + } rgb_crt; + struct { + __u8 dual; + } tmds; + struct { + __u8 acpi_edid; + } lvds; + struct { + __u8 aux; + __u8 mst; + __u8 increased_wm; + __u8 link_nr; + __u32 link_bw; + } dp; + }; } v0; }; -#define NVIF_OUTP_V0_LOAD_DETECT 0x00 -#define NVIF_OUTP_V0_ACQUIRE 0x01 -#define NVIF_OUTP_V0_RELEASE 0x02 -#define NVIF_OUTP_V0_INFOFRAME 0x03 -#define NVIF_OUTP_V0_HDA_ELD 0x04 -#define NVIF_OUTP_V0_DP_AUX_PWR 0x05 -#define NVIF_OUTP_V0_DP_RETRAIN 0x06 -#define NVIF_OUTP_V0_DP_MST_VCPI 0x07 +#define NVIF_OUTP_V0_DETECT 0x00 +#define NVIF_OUTP_V0_EDID_GET 0x01 + +#define NVIF_OUTP_V0_INHERIT 0x10 +#define NVIF_OUTP_V0_ACQUIRE 0x11 +#define NVIF_OUTP_V0_RELEASE 0x12 + +#define NVIF_OUTP_V0_LOAD_DETECT 0x20 + +#define NVIF_OUTP_V0_BL_GET 0x30 +#define NVIF_OUTP_V0_BL_SET 0x31 + +#define NVIF_OUTP_V0_LVDS 0x40 + +#define NVIF_OUTP_V0_HDMI 0x50 + +#define NVIF_OUTP_V0_INFOFRAME 0x60 +#define NVIF_OUTP_V0_HDA_ELD 0x61 + +#define NVIF_OUTP_V0_DP_AUX_PWR 0x70 +#define NVIF_OUTP_V0_DP_AUX_XFER 0x71 +#define NVIF_OUTP_V0_DP_RATES 0x72 +#define NVIF_OUTP_V0_DP_TRAIN 0x73 +#define NVIF_OUTP_V0_DP_DRIVE 0x74 +#define NVIF_OUTP_V0_DP_SST 0x75 +#define NVIF_OUTP_V0_DP_MST_ID_GET 0x76 +#define NVIF_OUTP_V0_DP_MST_ID_PUT 0x77 +#define NVIF_OUTP_V0_DP_MST_VCPI 0x78 + +union nvif_outp_detect_args { + struct nvif_outp_detect_v0 { + __u8 version; +#define NVIF_OUTP_DETECT_V0_NOT_PRESENT 0x00 +#define NVIF_OUTP_DETECT_V0_PRESENT 0x01 +#define NVIF_OUTP_DETECT_V0_UNKNOWN 0x02 + __u8 status; + } v0; +}; + +union nvif_outp_edid_get_args { + struct nvif_outp_edid_get_v0 { + __u8 version; + __u8 pad01; + __u16 size; + __u8 data[2048]; + } v0; +}; union nvif_outp_load_detect_args { struct nvif_outp_load_detect_v0 { @@ -33,40 +101,39 @@ union nvif_outp_load_detect_args { union nvif_outp_acquire_args { struct nvif_outp_acquire_v0 { __u8 version; -#define NVIF_OUTP_ACQUIRE_V0_RGB_CRT 0x00 -#define NVIF_OUTP_ACQUIRE_V0_TV 0x01 -#define NVIF_OUTP_ACQUIRE_V0_TMDS 0x02 -#define NVIF_OUTP_ACQUIRE_V0_LVDS 0x03 -#define NVIF_OUTP_ACQUIRE_V0_DP 0x04 - __u8 proto; +#define NVIF_OUTP_ACQUIRE_V0_DAC 0x00 +#define NVIF_OUTP_ACQUIRE_V0_SOR 0x01 +#define NVIF_OUTP_ACQUIRE_V0_PIOR 0x02 + __u8 type; __u8 or; __u8 link; __u8 pad04[4]; union { struct { - __u8 head; - __u8 hdmi; - __u8 hdmi_max_ac_packet; - __u8 hdmi_rekey; -#define NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_SCRAMBLE (1 << 0) -#define NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_DIV_BY_4 (1 << 1) - __u8 hdmi_scdc; - __u8 hdmi_hda; - __u8 pad06[2]; - } tmds; - struct { - __u8 dual; - __u8 bpc8; - __u8 pad02[6]; - } lvds; + __u8 hda; + } sor; + }; + } v0; +}; + +union nvif_outp_inherit_args { + struct nvif_outp_inherit_v0 { + __u8 version; +#define NVIF_OUTP_INHERIT_V0_RGB_CRT 0x00 +#define NVIF_OUTP_INHERIT_V0_TV 0x01 +#define NVIF_OUTP_INHERIT_V0_TMDS 0x02 +#define NVIF_OUTP_INHERIT_V0_LVDS 0x03 +#define NVIF_OUTP_INHERIT_V0_DP 0x04 + // In/out. Input is one of the above values, output is the actual hw protocol + __u8 proto; + __u8 or; + __u8 link; + __u8 head; + union { struct { - __u8 link_nr; /* 0 = highest possible. */ - __u8 link_bw; /* 0 = highest possible, DP BW code otherwise. */ + // TODO: Figure out padding, and whether we even want this field __u8 hda; - __u8 mst; - __u8 pad04[4]; - __u8 dpcd[DP_RECEIVER_CAP_SIZE]; - } dp; + } tmds; }; } v0; }; @@ -76,6 +143,42 @@ union nvif_outp_release_args { } vn; }; +union nvif_outp_bl_get_args { + struct nvif_outp_bl_get_v0 { + __u8 version; + __u8 level; + } v0; +}; + +union nvif_outp_bl_set_args { + struct nvif_outp_bl_set_v0 { + __u8 version; + __u8 level; + } v0; +}; + +union nvif_outp_lvds_args { + struct nvif_outp_lvds_v0 { + __u8 version; + __u8 dual; + __u8 bpc8; + } v0; +}; + +union nvif_outp_hdmi_args { + struct nvif_outp_hdmi_v0 { + __u8 version; + __u8 head; + __u8 enable; + __u8 max_ac_packet; + __u8 rekey; + __u8 scdc; + __u8 scdc_scrambling; + __u8 scdc_low_rates; + __u32 khz; + } v0; +}; + union nvif_outp_infoframe_args { struct nvif_outp_infoframe_v0 { __u8 version; @@ -105,9 +208,77 @@ union nvif_outp_dp_aux_pwr_args { } v0; }; -union nvif_outp_dp_retrain_args { - struct nvif_outp_dp_retrain_vn { - } vn; +union nvif_outp_dp_aux_xfer_args { + struct nvif_outp_dp_aux_xfer_v0 { + __u8 version; + __u8 pad01; + __u8 type; + __u8 size; + __u32 addr; + __u8 data[16]; + } v0; +}; + +union nvif_outp_dp_rates_args { + struct nvif_outp_dp_rates_v0 { + __u8 version; + __u8 pad01[6]; + __u8 rates; + struct { + __s8 dpcd; + __u32 rate; + } rate[8]; + } v0; +}; + +union nvif_outp_dp_train_args { + struct nvif_outp_dp_train_v0 { + __u8 version; + __u8 retrain; + __u8 mst; + __u8 lttprs; + __u8 post_lt_adj; + __u8 link_nr; + __u32 link_bw; + __u8 dpcd[DP_RECEIVER_CAP_SIZE]; + } v0; +}; + +union nvif_outp_dp_drive_args { + struct nvif_outp_dp_drive_v0 { + __u8 version; + __u8 pad01[2]; + __u8 lanes; + __u8 pe[4]; + __u8 vs[4]; + } v0; +}; + +union nvif_outp_dp_sst_args { + struct nvif_outp_dp_sst_v0 { + __u8 version; + __u8 head; + __u8 pad02[2]; + __u32 watermark; + __u32 hblanksym; + __u32 vblanksym; + } v0; +}; + +union nvif_outp_dp_mst_id_put_args { + struct nvif_outp_dp_mst_id_put_v0 { + __u8 version; + __u8 pad01[3]; + __u32 id; + } v0; +}; + +union nvif_outp_dp_mst_id_get_args { + struct nvif_outp_dp_mst_id_get_v0 { + __u8 version; + __u8 pad01[3]; + __u32 id; + } v0; }; union nvif_outp_dp_mst_vcpi_args { diff --git a/drivers/gpu/drm/nouveau/include/nvif/outp.h b/drivers/gpu/drm/nouveau/include/nvif/outp.h index fa76a7b5e4b3..bc122a5ba7df 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/outp.h +++ b/drivers/gpu/drm/nouveau/include/nvif/outp.h @@ -8,6 +8,46 @@ struct nvif_disp; struct nvif_outp { struct nvif_object object; + u32 id; + + struct { + enum { + NVIF_OUTP_DAC, + NVIF_OUTP_SOR, + NVIF_OUTP_PIOR, + } type; + + enum { + NVIF_OUTP_RGB_CRT, + NVIF_OUTP_TMDS, + NVIF_OUTP_LVDS, + NVIF_OUTP_DP, + } proto; + + u8 heads; +#define NVIF_OUTP_DDC_INVALID 0xff + u8 ddc; + u8 conn; + + union { + struct { + u32 freq_max; + } rgb_crt; + struct { + bool dual; + } tmds; + struct { + bool acpi_edid; + } lvds; + struct { + u8 aux; + bool mst; + bool increased_wm; + u8 link_nr; + u32 link_bw; + } dp; + }; + } info; struct { int id; @@ -17,18 +57,60 @@ struct nvif_outp { int nvif_outp_ctor(struct nvif_disp *, const char *name, int id, struct nvif_outp *); void nvif_outp_dtor(struct nvif_outp *); + +enum nvif_outp_detect_status { + NOT_PRESENT, + PRESENT, + UNKNOWN, +}; + +enum nvif_outp_detect_status nvif_outp_detect(struct nvif_outp *); +int nvif_outp_edid_get(struct nvif_outp *, u8 **pedid); + int nvif_outp_load_detect(struct nvif_outp *, u32 loadval); -int nvif_outp_acquire_rgb_crt(struct nvif_outp *); -int nvif_outp_acquire_tmds(struct nvif_outp *, int head, - bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda); -int nvif_outp_acquire_lvds(struct nvif_outp *, bool dual, bool bpc8); -int nvif_outp_acquire_dp(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], - int link_nr, int link_bw, bool hda, bool mst); +int nvif_outp_acquire_dac(struct nvif_outp *); +int nvif_outp_acquire_sor(struct nvif_outp *, bool hda); +int nvif_outp_acquire_pior(struct nvif_outp *); +int nvif_outp_inherit_rgb_crt(struct nvif_outp *outp, u8 *proto_out); +int nvif_outp_inherit_lvds(struct nvif_outp *outp, u8 *proto_out); +int nvif_outp_inherit_tmds(struct nvif_outp *outp, u8 *proto_out); +int nvif_outp_inherit_dp(struct nvif_outp *outp, u8 *proto_out); + void nvif_outp_release(struct nvif_outp *); + +static inline bool +nvif_outp_acquired(struct nvif_outp *outp) +{ + return outp->or.id >= 0; +} + +int nvif_outp_bl_get(struct nvif_outp *); +int nvif_outp_bl_set(struct nvif_outp *, int level); + +int nvif_outp_lvds(struct nvif_outp *, bool dual, bool bpc8); + +int nvif_outp_hdmi(struct nvif_outp *, int head, bool enable, u8 max_ac_packet, u8 rekey, u32 khz, + bool scdc, bool scdc_scrambling, bool scdc_low_rates); + int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size); int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size); + int nvif_outp_dp_aux_pwr(struct nvif_outp *, bool enable); -int nvif_outp_dp_retrain(struct nvif_outp *); +int nvif_outp_dp_aux_xfer(struct nvif_outp *, u8 type, u8 *size, u32 addr, u8 *data); + +struct nvif_outp_dp_rate { + int dpcd; /* -1 for non-indexed rates */ + u32 rate; +}; + +int nvif_outp_dp_rates(struct nvif_outp *, struct nvif_outp_dp_rate *rate, int rate_nr); +int nvif_outp_dp_train(struct nvif_outp *, u8 dpcd[DP_RECEIVER_CAP_SIZE], + u8 lttprs, u8 link_nr, u32 link_bw, bool mst, bool post_lt_adj, + bool retrain); +int nvif_outp_dp_drive(struct nvif_outp *, u8 link_nr, u8 pe[4], u8 vs[4]); +int nvif_outp_dp_sst(struct nvif_outp *, int head, u32 watermark, u32 hblanksym, u32 vblanksym); +int nvif_outp_dp_mst_id_get(struct nvif_outp *, u32 *id); +int nvif_outp_dp_mst_id_put(struct nvif_outp *, u32 id); int nvif_outp_dp_mst_vcpi(struct nvif_outp *, int head, u8 start_slot, u8 num_slots, u16 pbn, u16 aligned_pbn); #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h b/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h index d3b6a68ddda3..fc0f38981391 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h @@ -12,6 +12,7 @@ struct nvkm_tags { }; enum nvkm_memory_target { + NVKM_MEM_TARGET_INST_SR_LOST, /* instance memory - not preserved across suspend */ NVKM_MEM_TARGET_INST, /* instance memory */ NVKM_MEM_TARGET_VRAM, /* video memory */ NVKM_MEM_TARGET_HOST, /* coherent system memory */ diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h index fcdaefc99fe8..92a36ddfc29f 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h @@ -26,7 +26,7 @@ struct nvkm_instmem { u32 nvkm_instmem_rd32(struct nvkm_instmem *, u32 addr); void nvkm_instmem_wr32(struct nvkm_instmem *, u32 addr, u32 data); -int nvkm_instobj_new(struct nvkm_instmem *, u32 size, u32 align, bool zero, +int nvkm_instobj_new(struct nvkm_instmem *, u32 size, u32 align, bool zero, bool preserve, struct nvkm_memory **); int nvkm_instobj_wrap(struct nvkm_device *, struct nvkm_memory *, struct nvkm_memory **); diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 91b5ecc57538..d47442125fa1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -109,42 +109,6 @@ nv40_backlight_init(struct nouveau_encoder *encoder, return 0; } -static int -nv50_get_intensity(struct backlight_device *bd) -{ - struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div = 1025; - u32 val; - - val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); - val &= NV50_PDISP_SOR_PWM_CTL_VAL; - return ((val * 100) + (div / 2)) / div; -} - -static int -nv50_set_intensity(struct backlight_device *bd) -{ - struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div = 1025; - u32 val = (bd->props.brightness * div) / 100; - - nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), - NV50_PDISP_SOR_PWM_CTL_NEW | val); - return 0; -} - -static const struct backlight_ops nv50_bl_ops = { - .options = BL_CORE_SUSPENDRESUME, - .get_brightness = nv50_get_intensity, - .update_status = nv50_set_intensity, -}; - /* * eDP brightness callbacks need to happen under lock, since we need to * enable/disable the backlight ourselves for modesets @@ -238,53 +202,25 @@ static const struct backlight_ops nv50_edp_bl_ops = { }; static int -nva3_get_intensity(struct backlight_device *bd) +nv50_get_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div, val; - div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); - val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); - val &= NVA3_PDISP_SOR_PWM_CTL_VAL; - if (div && div >= val) - return ((val * 100) + (div / 2)) / div; - - return 100; + return nvif_outp_bl_get(&nv_encoder->outp); } static int -nva3_set_intensity(struct backlight_device *bd) +nv50_set_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div, val; - - div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); - val = backlight_get_brightness(bd); - if (val) - val = (val * div) / 100; - - if (div) { - nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), - val | - NV50_PDISP_SOR_PWM_CTL_NEW | - NVA3_PDISP_SOR_PWM_CTL_UNK); - return 0; - } - - return -EINVAL; + return nvif_outp_bl_set(&nv_encoder->outp, backlight_get_brightness(bd)); } -static const struct backlight_ops nva3_bl_ops = { +static const struct backlight_ops nv50_bl_ops = { .options = BL_CORE_SUSPENDRESUME, - .get_brightness = nva3_get_intensity, - .update_status = nva3_set_intensity, + .get_brightness = nv50_get_intensity, + .update_status = nv50_set_intensity, }; /* FIXME: perform backlight probing for eDP _before_ this, this only gets called after connector @@ -298,13 +234,12 @@ nv50_backlight_init(struct nouveau_backlight *bl, const struct backlight_ops **ops) { struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; /* * Note when this runs the connectors have not been probed yet, * so nv_conn->base.status is not set yet. */ - if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)) || + if (nvif_outp_bl_get(&nv_encoder->outp) < 0 || drm_helper_probe_detect(&nv_conn->base, NULL, false) != connector_status_connected) return -ENODEV; @@ -346,15 +281,8 @@ nv50_backlight_init(struct nouveau_backlight *bl, } } - if (drm->client.device.info.chipset <= 0xa0 || - drm->client.device.info.chipset == 0xaa || - drm->client.device.info.chipset == 0xac) - *ops = &nv50_bl_ops; - else - *ops = &nva3_bl_ops; - + *ops = &nv50_bl_ops; props->max_brightness = 100; - return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 189903b65edc..9e878cdc8e38 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -2093,9 +2093,11 @@ nouveau_bios_init(struct drm_device *dev) if (!NVInitVBIOS(dev)) return -ENODEV; - ret = parse_dcb_table(dev, bios); - if (ret) - return ret; + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { + ret = parse_dcb_table(dev, bios); + if (ret) + return ret; + } if (!bios->major_version) /* we don't run version 0 bios */ return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 79ea30aac31f..856b3ef5edb8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -400,10 +400,8 @@ nouveau_connector_destroy(struct drm_connector *connector) kfree(nv_connector->edid); drm_connector_unregister(connector); drm_connector_cleanup(connector); - if (nv_connector->aux.transfer) { + if (nv_connector->aux.transfer) drm_dp_cec_unregister_connector(&nv_connector->aux); - kfree(nv_connector->aux.name); - } nvif_conn_dtor(&nv_connector->conn); kfree(connector); } @@ -413,6 +411,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct pci_dev *pdev = to_pci_dev(dev->dev); + struct nouveau_connector *conn = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL, *found = NULL; struct drm_encoder *encoder; int ret; @@ -421,33 +420,48 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) drm_connector_for_each_possible_encoder(connector, encoder) { nv_encoder = nouveau_encoder(encoder); - switch (nv_encoder->dcb->type) { - case DCB_OUTPUT_DP: - ret = nouveau_dp_detect(nouveau_connector(connector), - nv_encoder); - if (ret == NOUVEAU_DP_MST) - return NULL; - else if (ret == NOUVEAU_DP_SST) - found = nv_encoder; + if (nvif_object_constructed(&nv_encoder->outp.object)) { + enum nvif_outp_detect_status status; + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + ret = nouveau_dp_detect(conn, nv_encoder); + if (ret == NOUVEAU_DP_MST) + return NULL; + if (ret != NOUVEAU_DP_SST) + continue; + + return nv_encoder; + } else { + status = nvif_outp_detect(&nv_encoder->outp); + switch (status) { + case PRESENT: + return nv_encoder; + case NOT_PRESENT: + continue; + case UNKNOWN: + break; + default: + WARN_ON(1); + break; + } + } + } - break; - case DCB_OUTPUT_LVDS: + if (!nv_encoder->i2c) + continue; + + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { switcheroo_ddc = !!(vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC); - fallthrough; - default: - if (!nv_encoder->i2c) - break; + } - if (switcheroo_ddc) - vga_switcheroo_lock_ddc(pdev); - if (nvkm_probe_i2c(nv_encoder->i2c, 0x50)) - found = nv_encoder; - if (switcheroo_ddc) - vga_switcheroo_unlock_ddc(pdev); + if (switcheroo_ddc) + vga_switcheroo_lock_ddc(pdev); + if (nvkm_probe_i2c(nv_encoder->i2c, 0x50)) + found = nv_encoder; + if (switcheroo_ddc) + vga_switcheroo_unlock_ddc(pdev); - break; - } if (found) break; } @@ -554,7 +568,6 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL; struct nouveau_encoder *nv_partner; - struct i2c_adapter *i2c; int type; int ret; enum drm_connector_status conn_status = connector_status_disconnected; @@ -577,15 +590,20 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) } nv_encoder = nouveau_connector_ddc_detect(connector); - if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) { - struct edid *new_edid; + if (nv_encoder) { + struct edid *new_edid = NULL; - if ((vga_switcheroo_handler_flags() & - VGA_SWITCHEROO_CAN_SWITCH_DDC) && - nv_connector->type == DCB_CONNECTOR_LVDS) - new_edid = drm_get_edid_switcheroo(connector, i2c); - else - new_edid = drm_get_edid(connector, i2c); + if (nv_encoder->i2c) { + if ((vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC) && + nv_connector->type == DCB_CONNECTOR_LVDS) + new_edid = drm_get_edid_switcheroo(connector, nv_encoder->i2c); + else + new_edid = drm_get_edid(connector, nv_encoder->i2c); + } else { + ret = nvif_outp_edid_get(&nv_encoder->outp, (u8 **)&new_edid); + if (ret < 0) + return connector_status_disconnected; + } nouveau_connector_set_edid(nv_connector, new_edid); if (!nv_connector->edid) { @@ -1117,7 +1135,7 @@ nouveau_connector_atomic_check(struct drm_connector *connector, struct drm_atomi struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, connector); - if (!nv_conn->dp_encoder || !nv50_has_mst(nouveau_drm(connector->dev))) + if (!nv_conn->dp_encoder || !nv_conn->dp_encoder->dp.mstm) return 0; return drm_dp_mst_root_conn_atomic_check(conn_state, &nv_conn->dp_encoder->dp.mstm->mgr); @@ -1206,23 +1224,17 @@ nouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg) struct nouveau_connector *nv_connector = container_of(obj, typeof(*nv_connector), aux); struct nouveau_encoder *nv_encoder; - struct nvkm_i2c_aux *aux; u8 size = msg->size; int ret; nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); - if (!nv_encoder || !(aux = nv_encoder->aux)) + if (!nv_encoder) return -ENODEV; if (WARN_ON(msg->size > 16)) return -E2BIG; - ret = nvkm_i2c_aux_acquire(aux); - if (ret) - return ret; - - ret = nvkm_i2c_aux_xfer(aux, false, msg->request, msg->address, - msg->buffer, &size); - nvkm_i2c_aux_release(aux); + ret = nvif_outp_dp_aux_xfer(&nv_encoder->outp, + msg->request, &size, msg->address, msg->buffer); if (ret >= 0) { msg->reply = ret; return size; @@ -1263,17 +1275,13 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb) } struct drm_connector * -nouveau_connector_create(struct drm_device *dev, - const struct dcb_output *dcbe) +nouveau_connector_create(struct drm_device *dev, int index) { - const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_display *disp = nouveau_display(dev); struct nouveau_connector *nv_connector = NULL; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; - char aux_name[48] = {0}; - int index = dcbe->connector; int type, ret = 0; bool dummy; @@ -1295,74 +1303,86 @@ nouveau_connector_create(struct drm_device *dev, nv_connector->index = index; INIT_WORK(&nv_connector->irq_work, nouveau_dp_irq); - /* attempt to parse vbios connector type and hotplug gpio */ - nv_connector->dcb = olddcb_conn(dev, index); - if (nv_connector->dcb) { - u32 entry = ROM16(nv_connector->dcb[0]); - if (olddcb_conntab(dev)[3] >= 4) - entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; - - nv_connector->type = nv_connector->dcb[0]; - if (drm_conntype_from_dcb(nv_connector->type) == - DRM_MODE_CONNECTOR_Unknown) { - NV_WARN(drm, "unknown connector type %02x\n", - nv_connector->type); - nv_connector->type = DCB_CONNECTOR_NONE; + if (disp->disp.conn_mask & BIT(nv_connector->index)) { + ret = nvif_conn_ctor(&disp->disp, nv_connector->base.name, nv_connector->index, + &nv_connector->conn); + if (ret) { + kfree(nv_connector); + return ERR_PTR(ret); } - /* Gigabyte NX85T */ - if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { - if (nv_connector->type == DCB_CONNECTOR_HDMI_1) - nv_connector->type = DCB_CONNECTOR_DVI_I; + switch (nv_connector->conn.info.type) { + case NVIF_CONN_VGA : type = DCB_CONNECTOR_VGA; break; + case NVIF_CONN_DVI_I : type = DCB_CONNECTOR_DVI_I; break; + case NVIF_CONN_DVI_D : type = DCB_CONNECTOR_DVI_D; break; + case NVIF_CONN_LVDS : type = DCB_CONNECTOR_LVDS; break; + case NVIF_CONN_LVDS_SPWG: type = DCB_CONNECTOR_LVDS_SPWG; break; + case NVIF_CONN_DP : type = DCB_CONNECTOR_DP; break; + case NVIF_CONN_EDP : type = DCB_CONNECTOR_eDP; break; + case NVIF_CONN_HDMI : type = DCB_CONNECTOR_HDMI_0; break; + default: + WARN_ON(1); + return NULL; } - /* Gigabyte GV-NX86T512H */ - if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { - if (nv_connector->type == DCB_CONNECTOR_HDMI_1) - nv_connector->type = DCB_CONNECTOR_DVI_I; - } + nv_connector->type = type; } else { - nv_connector->type = DCB_CONNECTOR_NONE; - } + u8 *dcb = olddcb_conn(dev, nv_connector->index); - /* no vbios data, or an unknown dcb connector type - attempt to - * figure out something suitable ourselves - */ - if (nv_connector->type == DCB_CONNECTOR_NONE) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_table *dcbt = &drm->vbios.dcb; - u32 encoders = 0; - int i; - - for (i = 0; i < dcbt->entries; i++) { - if (dcbt->entry[i].connector == nv_connector->index) - encoders |= (1 << dcbt->entry[i].type); + if (dcb) + nv_connector->type = dcb[0]; + else + nv_connector->type = DCB_CONNECTOR_NONE; + + /* attempt to parse vbios connector type and hotplug gpio */ + if (nv_connector->type != DCB_CONNECTOR_NONE) { + if (drm_conntype_from_dcb(nv_connector->type) == + DRM_MODE_CONNECTOR_Unknown) { + NV_WARN(drm, "unknown connector type %02x\n", + nv_connector->type); + nv_connector->type = DCB_CONNECTOR_NONE; + } } - if (encoders & (1 << DCB_OUTPUT_DP)) { - if (encoders & (1 << DCB_OUTPUT_TMDS)) - nv_connector->type = DCB_CONNECTOR_DP; - else - nv_connector->type = DCB_CONNECTOR_eDP; - } else - if (encoders & (1 << DCB_OUTPUT_TMDS)) { - if (encoders & (1 << DCB_OUTPUT_ANALOG)) - nv_connector->type = DCB_CONNECTOR_DVI_I; - else - nv_connector->type = DCB_CONNECTOR_DVI_D; - } else - if (encoders & (1 << DCB_OUTPUT_ANALOG)) { - nv_connector->type = DCB_CONNECTOR_VGA; - } else - if (encoders & (1 << DCB_OUTPUT_LVDS)) { - nv_connector->type = DCB_CONNECTOR_LVDS; - } else - if (encoders & (1 << DCB_OUTPUT_TV)) { - nv_connector->type = DCB_CONNECTOR_TV_0; + /* no vbios data, or an unknown dcb connector type - attempt to + * figure out something suitable ourselves + */ + if (nv_connector->type == DCB_CONNECTOR_NONE && + !WARN_ON(drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA)) { + struct dcb_table *dcbt = &drm->vbios.dcb; + u32 encoders = 0; + int i; + + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector == nv_connector->index) + encoders |= (1 << dcbt->entry[i].type); + } + + if (encoders & (1 << DCB_OUTPUT_TMDS)) { + if (encoders & (1 << DCB_OUTPUT_ANALOG)) + nv_connector->type = DCB_CONNECTOR_DVI_I; + else + nv_connector->type = DCB_CONNECTOR_DVI_D; + } else + if (encoders & (1 << DCB_OUTPUT_ANALOG)) { + nv_connector->type = DCB_CONNECTOR_VGA; + } else + if (encoders & (1 << DCB_OUTPUT_LVDS)) { + nv_connector->type = DCB_CONNECTOR_LVDS; + } else + if (encoders & (1 << DCB_OUTPUT_TV)) { + nv_connector->type = DCB_CONNECTOR_TV_0; + } } } - switch ((type = drm_conntype_from_dcb(nv_connector->type))) { + type = drm_conntype_from_dcb(nv_connector->type); + if (type == DRM_MODE_CONNECTOR_LVDS) + drm_connector_init(dev, connector, &nouveau_connector_funcs_lvds, type); + else + drm_connector_init(dev, connector, &nouveau_connector_funcs, type); + + switch (type) { case DRM_MODE_CONNECTOR_LVDS: ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); if (ret) { @@ -1371,24 +1391,16 @@ nouveau_connector_create(struct drm_device *dev, return ERR_PTR(ret); } - funcs = &nouveau_connector_funcs_lvds; break; case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_eDP: nv_connector->aux.dev = connector->kdev; nv_connector->aux.drm_dev = dev; nv_connector->aux.transfer = nouveau_connector_aux_xfer; - snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x", - dcbe->hasht, dcbe->hashm); - nv_connector->aux.name = kstrdup(aux_name, GFP_KERNEL); - if (!nv_connector->aux.name) { - kfree(nv_connector); - return ERR_PTR(-ENOMEM); - } + nv_connector->aux.name = connector->name; drm_dp_aux_init(&nv_connector->aux); break; default: - funcs = &nouveau_connector_funcs; break; } @@ -1403,17 +1415,10 @@ nouveau_connector_create(struct drm_device *dev, connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_connector_init(dev, connector, funcs, type); drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); connector->polled = DRM_CONNECTOR_POLL_CONNECT; - if (nv_connector->dcb && (disp->disp.conn_mask & BIT(nv_connector->index))) { - ret = nvif_conn_ctor(&disp->disp, nv_connector->base.name, nv_connector->index, - &nv_connector->conn); - if (ret) { - goto drm_conn_err; - } - + if (nvif_object_constructed(&nv_connector->conn.object)) { ret = nvif_conn_event_ctor(&nv_connector->conn, "kmsHotplug", nouveau_connector_hotplug, NVIF_CONN_EVENT_V0_PLUG | NVIF_CONN_EVENT_V0_UNPLUG, diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 35bcb541722b..a2df4918340c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -121,7 +121,6 @@ struct nouveau_connector { struct drm_connector base; enum dcb_connector_type type; u8 index; - u8 *dcb; struct nvif_conn conn; u64 hpd_pending; @@ -200,7 +199,7 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) } struct drm_connector * -nouveau_connector_create(struct drm_device *, const struct dcb_output *); +nouveau_connector_create(struct drm_device *, int id); void nouveau_connector_hpd(struct nouveau_connector *, u64 bits); extern int nouveau_tv_disable; diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index 053f703f2f68..e83db051e851 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -231,7 +231,7 @@ nouveau_debugfs_gpuva(struct seq_file *m, void *data) continue; nouveau_uvmm_lock(uvmm); - drm_debugfs_gpuva_info(m, &uvmm->umgr); + drm_debugfs_gpuva_info(m, &uvmm->base); seq_puts(m, "\n"); nouveau_debugfs_gpuva_regions(m, uvmm); nouveau_uvmm_unlock(uvmm); diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 99977e5fe716..d8c92521226d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -724,10 +724,10 @@ nouveau_display_create(struct drm_device *dev) drm_kms_helper_poll_init(dev); drm_kms_helper_poll_disable(dev); - if (nouveau_modeset != 2 && drm->vbios.dcb.entries) { - ret = nvif_disp_ctor(&drm->client.device, "kmsDisp", 0, - &disp->disp); - if (ret == 0) { + if (nouveau_modeset != 2) { + ret = nvif_disp_ctor(&drm->client.device, "kmsDisp", 0, &disp->disp); + + if (!ret && (disp->disp.outp_mask || drm->vbios.dcb.entries)) { nouveau_display_create_properties(dev); if (disp->disp.object.oclass < NV50_DISP) { dev->mode_config.fb_modifiers_not_supported = true; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 6a4980b2d4d4..7de7707ec6a8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -42,6 +42,21 @@ nouveau_dp_has_sink_count(struct drm_connector *connector, return drm_dp_read_sink_count_cap(connector, outp->dp.dpcd, &outp->dp.desc); } +static bool +nouveau_dp_probe_lttpr(struct nouveau_encoder *outp) +{ + u8 rev, size = sizeof(rev); + int ret; + + ret = nvif_outp_dp_aux_xfer(&outp->outp, DP_AUX_NATIVE_READ, &size, + DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV, + &rev); + if (ret || size < sizeof(rev) || rev < 0x14) + return false; + + return true; +} + static enum drm_connector_status nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, struct nouveau_encoder *outp) @@ -53,10 +68,112 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, int ret; u8 *dpcd = outp->dp.dpcd; + outp->dp.lttpr.nr = 0; + outp->dp.rate_nr = 0; + outp->dp.link_nr = 0; + outp->dp.link_bw = 0; + + if (connector->connector_type != DRM_MODE_CONNECTOR_eDP && + nouveau_dp_probe_lttpr(outp) && + !drm_dp_read_dpcd_caps(aux, dpcd) && + !drm_dp_read_lttpr_common_caps(aux, dpcd, outp->dp.lttpr.caps)) { + int nr = drm_dp_lttpr_count(outp->dp.lttpr.caps); + + if (nr) { + drm_dp_dpcd_writeb(aux, DP_PHY_REPEATER_MODE, + DP_PHY_REPEATER_MODE_TRANSPARENT); + + if (nr > 0) { + ret = drm_dp_dpcd_writeb(aux, DP_PHY_REPEATER_MODE, + DP_PHY_REPEATER_MODE_NON_TRANSPARENT); + if (ret != 1) { + drm_dp_dpcd_writeb(aux, DP_PHY_REPEATER_MODE, + DP_PHY_REPEATER_MODE_TRANSPARENT); + } else { + outp->dp.lttpr.nr = nr; + } + } + } + } + ret = drm_dp_read_dpcd_caps(aux, dpcd); if (ret < 0) goto out; + outp->dp.link_nr = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; + if (outp->dcb->dpconf.link_nr < outp->dp.link_nr) + outp->dp.link_nr = outp->dcb->dpconf.link_nr; + + if (outp->dp.lttpr.nr) { + int links = drm_dp_lttpr_max_lane_count(outp->dp.lttpr.caps); + + if (links && links < outp->dp.link_nr) + outp->dp.link_nr = links; + } + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) { + __le16 rates[DP_MAX_SUPPORTED_RATES]; + + ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, rates, sizeof(rates)); + if (ret == sizeof(rates)) { + for (int i = 0; i < ARRAY_SIZE(rates); i++) { + u32 rate = (le16_to_cpu(rates[i]) * 200) / 10; + int j; + + if (!rate) + break; + + for (j = 0; j < outp->dp.rate_nr; j++) { + if (rate > outp->dp.rate[j].rate) { + for (int k = outp->dp.rate_nr; k > j; k--) + outp->dp.rate[k] = outp->dp.rate[k - 1]; + break; + } + } + + outp->dp.rate[j].dpcd = i; + outp->dp.rate[j].rate = rate; + outp->dp.rate_nr++; + } + } + } + + if (!outp->dp.rate_nr) { + const u32 rates[] = { 810000, 540000, 270000, 162000 }; + u32 max_rate = dpcd[DP_MAX_LINK_RATE] * 27000; + + if (outp->dp.lttpr.nr) { + int rate = drm_dp_lttpr_max_link_rate(outp->dp.lttpr.caps); + + if (rate && rate < max_rate) + max_rate = rate; + } + + max_rate = min_t(int, max_rate, outp->dcb->dpconf.link_bw); + + for (int i = 0; i < ARRAY_SIZE(rates); i++) { + if (rates[i] <= max_rate) { + outp->dp.rate[outp->dp.rate_nr].dpcd = -1; + outp->dp.rate[outp->dp.rate_nr].rate = rates[i]; + outp->dp.rate_nr++; + } + } + + if (WARN_ON(!outp->dp.rate_nr)) + goto out; + } + + ret = nvif_outp_dp_rates(&outp->outp, outp->dp.rate, outp->dp.rate_nr); + if (ret) + goto out; + + for (int i = 0; i < outp->dp.rate_nr; i++) { + u32 link_bw = outp->dp.rate[i].rate; + + if (link_bw > outp->dp.link_bw) + outp->dp.link_bw = link_bw; + } + ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd)); if (ret < 0) goto out; @@ -132,14 +249,8 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, } } - /* Check status of HPD pin before attempting an AUX transaction that - * would result in a number of (futile) retries on a connector which - * has no display plugged. - * - * TODO: look into checking this before probing I2C to detect DVI/HDMI - */ - hpd = nvif_conn_hpd_status(&nv_connector->conn); - if (hpd == NVIF_CONN_HPD_STATUS_NOT_PRESENT) { + hpd = nvif_outp_detect(&nv_encoder->outp); + if (hpd == NOT_PRESENT) { nvif_outp_dp_aux_pwr(&nv_encoder->outp, false); goto out; } @@ -157,39 +268,14 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, goto out; } - nv_encoder->dp.link_bw = 27000 * dpcd[DP_MAX_LINK_RATE]; - nv_encoder->dp.link_nr = - dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; - - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) { - struct drm_dp_aux *aux = &nv_connector->aux; - int ret, i; - u8 sink_rates[16]; - - ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, sink_rates, sizeof(sink_rates)); - if (ret == sizeof(sink_rates)) { - for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { - int val = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; - if (val && (i == 0 || val > nv_encoder->dp.link_bw)) - nv_encoder->dp.link_bw = val; - } - } - } - - NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n", - nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, - dpcd[DP_DPCD_REV]); - NV_DEBUG(drm, "encoder: %dx%d\n", - nv_encoder->dcb->dpconf.link_nr, - nv_encoder->dcb->dpconf.link_bw); - - if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr) - nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; - if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw) - nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw; + NV_DEBUG(drm, "sink dpcd version: 0x%02x\n", dpcd[DP_DPCD_REV]); + for (int i = 0; i < nv_encoder->dp.rate_nr; i++) + NV_DEBUG(drm, "sink rate %d: %d\n", i, nv_encoder->dp.rate[i].rate); - NV_DEBUG(drm, "maximum: %dx%d\n", - nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); + NV_DEBUG(drm, "encoder: %dx%d\n", nv_encoder->dcb->dpconf.link_nr, + nv_encoder->dcb->dpconf.link_bw); + NV_DEBUG(drm, "maximum: %dx%d\n", nv_encoder->dp.link_nr, + nv_encoder->dp.link_bw); if (mstm && mstm->can_mst) { ret = nv50_mstm_detect(nv_encoder); @@ -211,15 +297,186 @@ out: return ret; } +void +nouveau_dp_power_down(struct nouveau_encoder *outp) +{ + struct drm_dp_aux *aux = &outp->conn->aux; + int ret; + u8 pwr; + + mutex_lock(&outp->dp.hpd_irq_lock); + + ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); + if (ret == 1) { + pwr &= ~DP_SET_POWER_MASK; + pwr |= DP_SET_POWER_D3; + drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); + } + + outp->dp.lt.nr = 0; + mutex_unlock(&outp->dp.hpd_irq_lock); +} + +static bool +nouveau_dp_train_link(struct nouveau_encoder *outp, bool retrain) +{ + struct drm_dp_aux *aux = &outp->conn->aux; + bool post_lt = false; + int ret, retries = 0; + + if ( (outp->dp.dpcd[DP_MAX_LANE_COUNT] & 0x20) && + !(outp->dp.dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED)) + post_lt = true; + +retry: + ret = nvif_outp_dp_train(&outp->outp, outp->dp.dpcd, + outp->dp.lttpr.nr, + outp->dp.lt.nr, + outp->dp.lt.bw, + outp->dp.lt.mst, + post_lt, + retrain); + if (ret) + return false; + + if (post_lt) { + u8 stat[DP_LINK_STATUS_SIZE]; + u8 prev[2]; + u8 time = 0, adjusts = 0, tmp; + + ret = drm_dp_dpcd_read_phy_link_status(aux, DP_PHY_DPRX, stat); + if (ret) + return false; + + for (;;) { + if (!drm_dp_channel_eq_ok(stat, outp->dp.lt.nr)) { + ret = 1; + break; + } + + if (!(stat[2] & 0x02)) + break; + + msleep(5); + time += 5; + + memcpy(prev, &stat[4], sizeof(prev)); + ret = drm_dp_dpcd_read_phy_link_status(aux, DP_PHY_DPRX, stat); + if (ret) + break; + + if (!memcmp(prev, &stat[4], sizeof(prev))) { + if (time > 200) + break; + } else { + u8 pe[4], vs[4]; + + if (adjusts++ == 6) + break; + + for (int i = 0; i < outp->dp.lt.nr; i++) { + pe[i] = drm_dp_get_adjust_request_pre_emphasis(stat, i) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT; + vs[i] = drm_dp_get_adjust_request_voltage(stat, i) >> + DP_TRAIN_VOLTAGE_SWING_SHIFT; + } + + ret = nvif_outp_dp_drive(&outp->outp, outp->dp.lt.nr, pe, vs); + if (ret) + break; + + time = 0; + } + } + + if (drm_dp_dpcd_readb(aux, DP_LANE_COUNT_SET, &tmp) == 1) { + tmp &= ~0x20; + drm_dp_dpcd_writeb(aux, DP_LANE_COUNT_SET, tmp); + } + } + + if (ret == 1 && retries++ < 3) + goto retry; + + return ret == 0; +} + bool -nouveau_dp_link_check(struct nouveau_connector *nv_connector) +nouveau_dp_train(struct nouveau_encoder *outp, bool mst, u32 khz, u8 bpc) +{ + struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev); + struct drm_dp_aux *aux = &outp->conn->aux; + u32 min_rate; + u8 pwr; + bool ret = true; + + if (mst) + min_rate = outp->dp.link_nr * outp->dp.rate[0].rate; + else + min_rate = DIV_ROUND_UP(khz * bpc * 3, 8); + + NV_DEBUG(drm, "%s link training (mst:%d min_rate:%d)\n", + outp->base.base.name, mst, min_rate); + + mutex_lock(&outp->dp.hpd_irq_lock); + + if (drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr) == 1) { + if ((pwr & DP_SET_POWER_MASK) != DP_SET_POWER_D0) { + pwr &= ~DP_SET_POWER_MASK; + pwr |= DP_SET_POWER_D0; + drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); + } + } + + for (int nr = outp->dp.link_nr; nr; nr >>= 1) { + for (int rate = 0; rate < outp->dp.rate_nr; rate++) { + if (outp->dp.rate[rate].rate * nr >= min_rate) { + outp->dp.lt.nr = nr; + outp->dp.lt.bw = outp->dp.rate[rate].rate; + outp->dp.lt.mst = mst; + if (nouveau_dp_train_link(outp, false)) + goto done; + } + } + } + + ret = false; +done: + mutex_unlock(&outp->dp.hpd_irq_lock); + return ret; +} + +static bool +nouveau_dp_link_check_locked(struct nouveau_encoder *outp) { - struct nouveau_encoder *nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); + u8 link_status[DP_LINK_STATUS_SIZE]; - if (!nv_encoder || nv_encoder->outp.or.id < 0) + if (!outp || !outp->dp.lt.nr) return true; - return nvif_outp_dp_retrain(&nv_encoder->outp) == 0; + if (drm_dp_dpcd_read_phy_link_status(&outp->conn->aux, DP_PHY_DPRX, link_status) < 0) + return false; + + if (drm_dp_channel_eq_ok(link_status, outp->dp.lt.nr)) + return true; + + return nouveau_dp_train_link(outp, true); +} + +bool +nouveau_dp_link_check(struct nouveau_connector *nv_connector) +{ + struct nouveau_encoder *outp = nv_connector->dp_encoder; + bool link_ok = true; + + if (outp) { + mutex_lock(&outp->dp.hpd_irq_lock); + if (outp->dp.lt.nr) + link_ok = nouveau_dp_link_check_locked(outp); + mutex_unlock(&outp->dp.hpd_irq_lock); + } + + return link_ok; } void diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 4396f501b16a..50589f982d1a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1133,7 +1133,10 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) } get_task_comm(tmpname, current); - snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid)); + rcu_read_lock(); + snprintf(name, sizeof(name), "%s[%d]", + tmpname, pid_nr(rcu_dereference(fpriv->pid))); + rcu_read_unlock(); if (!(cli = kzalloc(sizeof(*cli), GFP_KERNEL))) { ret = -ENOMEM; diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index bcba1a14cfab..333042fc493f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -49,8 +49,9 @@ struct nouveau_encoder { struct nvif_outp outp; int or; + struct nouveau_connector *conn; + struct i2c_adapter *i2c; - struct nvkm_i2c_aux *aux; /* different to drm_encoder.crtc, this reflects what's * actually programmed on the hw, not the proposed crtc */ @@ -60,7 +61,6 @@ struct nouveau_encoder { /* Protected by nouveau_drm.audio.lock */ struct { bool enabled; - struct drm_connector *connector; } audio; struct drm_display_mode mode; @@ -68,18 +68,38 @@ struct nouveau_encoder { struct nv04_output_reg restore; - union { + struct { + struct { + bool enabled; + } hdmi; + struct { struct nv50_mstm *mstm; + + struct { + u8 caps[DP_LTTPR_COMMON_CAP_SIZE]; + u8 nr; + } lttpr; + + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + + struct nvif_outp_dp_rate rate[8]; + int rate_nr; + int link_nr; int link_bw; + struct { + bool mst; + u8 nr; + u32 bw; + } lt; + /* Protects DP state that needs to be accessed outside * connector reprobing contexts */ struct mutex hpd_irq_lock; - u8 dpcd[DP_RECEIVER_CAP_SIZE]; u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct drm_dp_desc desc; @@ -141,6 +161,8 @@ enum nouveau_dp_status { }; int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *); +bool nouveau_dp_train(struct nouveau_encoder *, bool mst, u32 khz, u8 bpc); +void nouveau_dp_power_down(struct nouveau_encoder *); bool nouveau_dp_link_check(struct nouveau_connector *); void nouveau_dp_irq(struct work_struct *); enum drm_mode_status nv50_dp_mode_valid(struct nouveau_encoder *, diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c index 19024ce21fbb..bf6c12f4342a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.c +++ b/drivers/gpu/drm/nouveau/nouveau_exec.c @@ -107,8 +107,8 @@ nouveau_exec_job_submit(struct nouveau_job *job) drm_exec_until_all_locked(exec) { struct drm_gpuva *va; - drm_gpuva_for_each_va(va, &uvmm->umgr) { - if (unlikely(va == &uvmm->umgr.kernel_alloc_node)) + drm_gpuvm_for_each_va(va, &uvmm->base) { + if (unlikely(va == &uvmm->base.kernel_alloc_node)) continue; ret = drm_exec_prepare_obj(exec, va->gem.obj, 1); diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index aae780e4a4aa..5cf892c50f43 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -329,7 +329,7 @@ nouveau_uvma_region_create(struct nouveau_uvmm *uvmm, struct nouveau_uvma_region *reg; int ret; - if (!drm_gpuva_interval_empty(&uvmm->umgr, addr, range)) + if (!drm_gpuvm_interval_empty(&uvmm->base, addr, range)) return -ENOSPC; ret = nouveau_uvma_region_alloc(®); @@ -384,7 +384,7 @@ nouveau_uvma_region_empty(struct nouveau_uvma_region *reg) { struct nouveau_uvmm *uvmm = reg->uvmm; - return drm_gpuva_interval_empty(&uvmm->umgr, + return drm_gpuvm_interval_empty(&uvmm->base, reg->va.addr, reg->va.range); } @@ -444,7 +444,7 @@ op_map_prepare_unwind(struct nouveau_uvma *uvma) static void op_unmap_prepare_unwind(struct drm_gpuva *va) { - drm_gpuva_insert(va->mgr, va); + drm_gpuva_insert(va->vm, va); } static void @@ -589,7 +589,7 @@ op_map_prepare(struct nouveau_uvmm *uvmm, uvma->region = args->region; uvma->kind = args->kind; - drm_gpuva_map(&uvmm->umgr, &uvma->va, op); + drm_gpuva_map(&uvmm->base, &uvma->va, op); /* Keep a reference until this uvma is destroyed. */ nouveau_uvma_gem_get(uvma); @@ -1194,7 +1194,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) goto unwind_continue; } - op->ops = drm_gpuva_sm_unmap_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_unmap_ops_create(&uvmm->base, op->va.addr, op->va.range); if (IS_ERR(op->ops)) { @@ -1205,7 +1205,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) ret = nouveau_uvmm_sm_unmap_prepare(uvmm, &op->new, op->ops); if (ret) { - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; op->reg = NULL; goto unwind_continue; @@ -1240,7 +1240,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) } } - op->ops = drm_gpuva_sm_map_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_map_ops_create(&uvmm->base, op->va.addr, op->va.range, op->gem.obj, @@ -1256,7 +1256,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) op->va.range, op->flags & 0xff); if (ret) { - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; goto unwind_continue; } @@ -1264,7 +1264,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) break; } case OP_UNMAP: - op->ops = drm_gpuva_sm_unmap_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_unmap_ops_create(&uvmm->base, op->va.addr, op->va.range); if (IS_ERR(op->ops)) { @@ -1275,7 +1275,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) ret = nouveau_uvmm_sm_unmap_prepare(uvmm, &op->new, op->ops); if (ret) { - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; goto unwind_continue; } @@ -1404,7 +1404,7 @@ unwind: break; } - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; op->reg = NULL; } @@ -1509,7 +1509,7 @@ nouveau_uvmm_bind_job_free_work_fn(struct work_struct *work) } if (!IS_ERR_OR_NULL(op->ops)) - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); if (obj) drm_gem_object_put(obj); @@ -1836,11 +1836,11 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, uvmm->kernel_managed_addr = kernel_managed_addr; uvmm->kernel_managed_size = kernel_managed_size; - drm_gpuva_manager_init(&uvmm->umgr, cli->name, - NOUVEAU_VA_SPACE_START, - NOUVEAU_VA_SPACE_END, - kernel_managed_addr, kernel_managed_size, - NULL); + drm_gpuvm_init(&uvmm->base, cli->name, + NOUVEAU_VA_SPACE_START, + NOUVEAU_VA_SPACE_END, + kernel_managed_addr, kernel_managed_size, + NULL); ret = nvif_vmm_ctor(&cli->mmu, "uvmm", cli->vmm.vmm.object.oclass, RAW, @@ -1855,7 +1855,7 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, return 0; out_free_gpuva_mgr: - drm_gpuva_manager_destroy(&uvmm->umgr); + drm_gpuvm_destroy(&uvmm->base); out_unlock: mutex_unlock(&cli->mutex); return ret; @@ -1877,11 +1877,11 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm) wait_event(entity->job.wq, list_empty(&entity->job.list.head)); nouveau_uvmm_lock(uvmm); - drm_gpuva_for_each_va_safe(va, next, &uvmm->umgr) { + drm_gpuvm_for_each_va_safe(va, next, &uvmm->base) { struct nouveau_uvma *uvma = uvma_from_va(va); struct drm_gem_object *obj = va->gem.obj; - if (unlikely(va == &uvmm->umgr.kernel_alloc_node)) + if (unlikely(va == &uvmm->base.kernel_alloc_node)) continue; drm_gpuva_remove(va); @@ -1910,7 +1910,7 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm) mutex_lock(&cli->mutex); nouveau_vmm_fini(&uvmm->vmm); - drm_gpuva_manager_destroy(&uvmm->umgr); + drm_gpuvm_destroy(&uvmm->base); mutex_unlock(&cli->mutex); dma_resv_fini(&uvmm->resv); diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.h b/drivers/gpu/drm/nouveau/nouveau_uvmm.h index fc7f6fd2a4e1..a308c59760a5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.h +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.h @@ -3,13 +3,13 @@ #ifndef __NOUVEAU_UVMM_H__ #define __NOUVEAU_UVMM_H__ -#include <drm/drm_gpuva_mgr.h> +#include <drm/drm_gpuvm.h> #include "nouveau_drv.h" struct nouveau_uvmm { + struct drm_gpuvm base; struct nouveau_vmm vmm; - struct drm_gpuva_manager umgr; struct maple_tree region_mt; struct mutex mutex; struct dma_resv resv; @@ -41,10 +41,10 @@ struct nouveau_uvma { u8 kind; }; -#define uvmm_from_mgr(x) container_of((x), struct nouveau_uvmm, umgr) +#define uvmm_from_gpuvm(x) container_of((x), struct nouveau_uvmm, base) #define uvma_from_va(x) container_of((x), struct nouveau_uvma, va) -#define to_uvmm(x) uvmm_from_mgr((x)->va.mgr) +#define to_uvmm(x) uvmm_from_gpuvm((x)->va.vm) struct nouveau_uvmm_bind_job { struct nouveau_job base; diff --git a/drivers/gpu/drm/nouveau/nvif/client.c b/drivers/gpu/drm/nouveau/nvif/client.c index a3264a0e933a..3a27245f467f 100644 --- a/drivers/gpu/drm/nouveau/nvif/client.c +++ b/drivers/gpu/drm/nouveau/nvif/client.c @@ -69,7 +69,7 @@ nvif_client_ctor(struct nvif_client *parent, const char *name, u64 device, } nop = {}; int ret; - strncpy(args.name, name, sizeof(args.name)); + strscpy_pad(args.name, name, sizeof(args.name)); ret = nvif_object_ctor(parent != client ? &parent->object : NULL, name ? name : "nvifClient", 0, NVIF_CLASS_CLIENT, &args, sizeof(args), diff --git a/drivers/gpu/drm/nouveau/nvif/conn.c b/drivers/gpu/drm/nouveau/nvif/conn.c index a3cf91aeae2d..9ee18cb99264 100644 --- a/drivers/gpu/drm/nouveau/nvif/conn.c +++ b/drivers/gpu/drm/nouveau/nvif/conn.c @@ -45,20 +45,6 @@ nvif_conn_event_ctor(struct nvif_conn *conn, const char *name, nvif_event_func f return ret; } -int -nvif_conn_hpd_status(struct nvif_conn *conn) -{ - struct nvif_conn_hpd_status_v0 args; - int ret; - - args.version = 0; - - ret = nvif_mthd(&conn->object, NVIF_CONN_V0_HPD_STATUS, &args, sizeof(args)); - NVIF_ERRON(ret, &conn->object, "[HPD_STATUS] support:%d present:%d", - args.support, args.present); - return ret ? ret : !!args.support + !!args.present; -} - void nvif_conn_dtor(struct nvif_conn *conn) { @@ -77,5 +63,25 @@ nvif_conn_ctor(struct nvif_disp *disp, const char *name, int id, struct nvif_con ret = nvif_object_ctor(&disp->object, name ?: "nvifConn", id, NVIF_CLASS_CONN, &args, sizeof(args), &conn->object); NVIF_ERRON(ret, &disp->object, "[NEW conn id:%d]", id); - return ret; + if (ret) + return ret; + + conn->id = id; + + switch (args.type) { + case NVIF_CONN_V0_VGA : conn->info.type = NVIF_CONN_VGA; break; + case NVIF_CONN_V0_TV : conn->info.type = NVIF_CONN_TV; break; + case NVIF_CONN_V0_DVI_I : conn->info.type = NVIF_CONN_DVI_I; break; + case NVIF_CONN_V0_DVI_D : conn->info.type = NVIF_CONN_DVI_D; break; + case NVIF_CONN_V0_LVDS : conn->info.type = NVIF_CONN_LVDS; break; + case NVIF_CONN_V0_LVDS_SPWG: conn->info.type = NVIF_CONN_LVDS_SPWG; break; + case NVIF_CONN_V0_HDMI : conn->info.type = NVIF_CONN_HDMI; break; + case NVIF_CONN_V0_DP : conn->info.type = NVIF_CONN_DP; break; + case NVIF_CONN_V0_EDP : conn->info.type = NVIF_CONN_EDP; break; + default: + break; + } + + return 0; + } diff --git a/drivers/gpu/drm/nouveau/nvif/disp.c b/drivers/gpu/drm/nouveau/nvif/disp.c index 09915f2715af..097246e10cdb 100644 --- a/drivers/gpu/drm/nouveau/nvif/disp.c +++ b/drivers/gpu/drm/nouveau/nvif/disp.c @@ -60,7 +60,7 @@ nvif_disp_ctor(struct nvif_device *device, const char *name, s32 oclass, struct cid = nvif_sclass(&device->object, disps, oclass); disp->object.client = NULL; if (cid < 0) { - NVIF_ERRON(cid, &device->object, "[NEW disp%04x] not supported", oclass); + NVIF_DEBUG(&device->object, "[NEW disp%04x] not supported", oclass); return cid; } diff --git a/drivers/gpu/drm/nouveau/nvif/outp.c b/drivers/gpu/drm/nouveau/nvif/outp.c index c24bc5eae3ec..5d3190c05250 100644 --- a/drivers/gpu/drm/nouveau/nvif/outp.c +++ b/drivers/gpu/drm/nouveau/nvif/outp.c @@ -47,10 +47,134 @@ nvif_outp_dp_mst_vcpi(struct nvif_outp *outp, int head, } int -nvif_outp_dp_retrain(struct nvif_outp *outp) +nvif_outp_dp_mst_id_put(struct nvif_outp *outp, u32 id) { - int ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_RETRAIN, NULL, 0); - NVIF_ERRON(ret, &outp->object, "[DP_RETRAIN]"); + struct nvif_outp_dp_mst_id_get_v0 args; + int ret; + + args.version = 0; + args.id = id; + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_MST_ID_PUT, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_MST_ID_PUT id:%08x]", args.id); + return ret; +} + +int +nvif_outp_dp_mst_id_get(struct nvif_outp *outp, u32 *id) +{ + struct nvif_outp_dp_mst_id_get_v0 args; + int ret; + + args.version = 0; + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_MST_ID_GET, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_MST_ID_GET] id:%08x", args.id); + if (ret) + return ret; + + *id = args.id; + return 0; +} + +int +nvif_outp_dp_sst(struct nvif_outp *outp, int head, u32 watermark, u32 hblanksym, u32 vblanksym) +{ + struct nvif_outp_dp_sst_v0 args; + int ret; + + args.version = 0; + args.head = head; + args.watermark = watermark; + args.hblanksym = hblanksym; + args.vblanksym = vblanksym; + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_SST, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, + "[DP_SST head:%d watermark:%d hblanksym:%d vblanksym:%d]", + args.head, args.watermark, args.hblanksym, args.vblanksym); + return ret; +} + +int +nvif_outp_dp_drive(struct nvif_outp *outp, u8 link_nr, u8 pe[4], u8 vs[4]) +{ + struct nvif_outp_dp_drive_v0 args; + int ret; + + args.version = 0; + args.lanes = link_nr; + memcpy(args.pe, pe, sizeof(args.pe)); + memcpy(args.vs, vs, sizeof(args.vs)); + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_DRIVE, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_DRIVE lanes:%d]", args.lanes); + return ret; +} + +int +nvif_outp_dp_train(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], u8 lttprs, + u8 link_nr, u32 link_bw, bool mst, bool post_lt_adj, bool retrain) +{ + struct nvif_outp_dp_train_v0 args; + int ret; + + args.version = 0; + args.retrain = retrain; + args.mst = mst; + args.lttprs = lttprs; + args.post_lt_adj = post_lt_adj; + args.link_nr = link_nr; + args.link_bw = link_bw; + memcpy(args.dpcd, dpcd, sizeof(args.dpcd)); + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_TRAIN, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, + "[DP_TRAIN retrain:%d mst:%d lttprs:%d post_lt_adj:%d nr:%d bw:%d]", + args.retrain, args.mst, args.lttprs, args.post_lt_adj, args.link_nr, + args.link_bw); + return ret; +} + +int +nvif_outp_dp_rates(struct nvif_outp *outp, struct nvif_outp_dp_rate *rate, int rate_nr) +{ + struct nvif_outp_dp_rates_v0 args; + int ret; + + if (rate_nr > ARRAY_SIZE(args.rate)) + return -EINVAL; + + args.version = 0; + args.rates = rate_nr; + for (int i = 0; i < args.rates; i++, rate++) { + args.rate[i].dpcd = rate->dpcd; + args.rate[i].rate = rate->rate; + } + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_RATES, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_RATES rates:%d]", args.rates); + return ret; +} + +int +nvif_outp_dp_aux_xfer(struct nvif_outp *outp, u8 type, u8 *psize, u32 addr, u8 *data) +{ + struct nvif_outp_dp_aux_xfer_v0 args; + u8 size = *psize; + int ret; + + args.version = 0; + args.type = type; + args.size = size; + args.addr = addr; + memcpy(args.data, data, size); + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_AUX_XFER, &args, sizeof(args)); + NVIF_DEBUG(&outp->object, "[DP_AUX_XFER type:%d size:%d addr:%05x] %d size:%d (ret: %d)", + args.type, size, args.addr, ret, args.size, ret); + if (ret < 0) + return ret; + + *psize = args.size; + + memcpy(data, args.data, size); return ret; } @@ -101,6 +225,74 @@ nvif_outp_infoframe(struct nvif_outp *outp, u8 type, struct nvif_outp_infoframe_ return ret; } +int +nvif_outp_hdmi(struct nvif_outp *outp, int head, bool enable, u8 max_ac_packet, u8 rekey, + u32 khz, bool scdc, bool scdc_scrambling, bool scdc_low_rates) +{ + struct nvif_outp_hdmi_v0 args; + int ret; + + args.version = 0; + args.head = head; + args.enable = enable; + args.max_ac_packet = max_ac_packet; + args.rekey = rekey; + args.khz = khz; + args.scdc = scdc; + args.scdc_scrambling = scdc_scrambling; + args.scdc_low_rates = scdc_low_rates; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_HDMI, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, + "[HDMI head:%d enable:%d max_ac_packet:%d rekey:%d khz:%d scdc:%d " + "scdc_scrambling:%d scdc_low_rates:%d]", + args.head, args.enable, args.max_ac_packet, args.rekey, args.khz, + args.scdc, args.scdc_scrambling, args.scdc_low_rates); + return ret; +} + +int +nvif_outp_lvds(struct nvif_outp *outp, bool dual, bool bpc8) +{ + struct nvif_outp_lvds_v0 args; + int ret; + + args.version = 0; + args.dual = dual; + args.bpc8 = bpc8; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_LVDS, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[LVDS dual:%d 8bpc:%d]", args.dual, args.bpc8); + return ret; +} + +int +nvif_outp_bl_set(struct nvif_outp *outp, int level) +{ + struct nvif_outp_bl_set_v0 args; + int ret; + + args.version = 0; + args.level = level; + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_BL_SET, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[BL_SET level:%d]", args.level); + return ret; +} + +int +nvif_outp_bl_get(struct nvif_outp *outp) +{ + struct nvif_outp_bl_get_v0 args; + int ret; + + args.version = 0; + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_BL_GET, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[BL_GET level:%d]", args.level); + return ret ? ret : args.level; +} + void nvif_outp_release(struct nvif_outp *outp) { @@ -110,12 +302,12 @@ nvif_outp_release(struct nvif_outp *outp) } static inline int -nvif_outp_acquire(struct nvif_outp *outp, u8 proto, struct nvif_outp_acquire_v0 *args) +nvif_outp_acquire(struct nvif_outp *outp, u8 type, struct nvif_outp_acquire_v0 *args) { int ret; args->version = 0; - args->proto = proto; + args->type = type; ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_ACQUIRE, args, sizeof(*args)); if (ret) @@ -127,73 +319,106 @@ nvif_outp_acquire(struct nvif_outp *outp, u8 proto, struct nvif_outp_acquire_v0 } int -nvif_outp_acquire_dp(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], - int link_nr, int link_bw, bool hda, bool mst) +nvif_outp_acquire_pior(struct nvif_outp *outp) { struct nvif_outp_acquire_v0 args; int ret; - args.dp.link_nr = link_nr; - args.dp.link_bw = link_bw; - args.dp.hda = hda; - args.dp.mst = mst; - memcpy(args.dp.dpcd, dpcd, sizeof(args.dp.dpcd)); - - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_DP, &args); - NVIF_ERRON(ret, &outp->object, - "[ACQUIRE proto:DP link_nr:%d link_bw:%02x hda:%d mst:%d] or:%d link:%d", - args.dp.link_nr, args.dp.link_bw, args.dp.hda, args.dp.mst, args.or, args.link); + ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_PIOR, &args); + NVIF_ERRON(ret, &outp->object, "[ACQUIRE PIOR] or:%d", args.or); return ret; } int -nvif_outp_acquire_lvds(struct nvif_outp *outp, bool dual, bool bpc8) +nvif_outp_acquire_sor(struct nvif_outp *outp, bool hda) { struct nvif_outp_acquire_v0 args; int ret; - args.lvds.dual = dual; - args.lvds.bpc8 = bpc8; + args.sor.hda = hda; - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_LVDS, &args); - NVIF_ERRON(ret, &outp->object, - "[ACQUIRE proto:LVDS dual:%d 8bpc:%d] or:%d link:%d", - args.lvds.dual, args.lvds.bpc8, args.or, args.link); + ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_SOR, &args); + NVIF_ERRON(ret, &outp->object, "[ACQUIRE SOR] or:%d link:%d", args.or, args.link); return ret; } int -nvif_outp_acquire_tmds(struct nvif_outp *outp, int head, - bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda) +nvif_outp_acquire_dac(struct nvif_outp *outp) { struct nvif_outp_acquire_v0 args; int ret; - args.tmds.head = head; - args.tmds.hdmi = hdmi; - args.tmds.hdmi_max_ac_packet = max_ac_packet; - args.tmds.hdmi_rekey = rekey; - args.tmds.hdmi_scdc = scdc; - args.tmds.hdmi_hda = hda; - - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_TMDS, &args); - NVIF_ERRON(ret, &outp->object, - "[ACQUIRE proto:TMDS head:%d hdmi:%d max_ac_packet:%d rekey:%d scdc:%d hda:%d]" - " or:%d link:%d", args.tmds.head, args.tmds.hdmi, args.tmds.hdmi_max_ac_packet, - args.tmds.hdmi_rekey, args.tmds.hdmi_scdc, args.tmds.hdmi_hda, - args.or, args.link); + ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_DAC, &args); + NVIF_ERRON(ret, &outp->object, "[ACQUIRE DAC] or:%d", args.or); return ret; } +static int +nvif_outp_inherit(struct nvif_outp *outp, + u8 proto, + struct nvif_outp_inherit_v0 *args, + u8 *proto_out) +{ + int ret; + + args->version = 0; + args->proto = proto; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_INHERIT, args, sizeof(*args)); + if (ret) + return ret; + + outp->or.id = args->or; + outp->or.link = args->link; + *proto_out = args->proto; + return 0; +} + int -nvif_outp_acquire_rgb_crt(struct nvif_outp *outp) +nvif_outp_inherit_lvds(struct nvif_outp *outp, u8 *proto_out) { - struct nvif_outp_acquire_v0 args; + struct nvif_outp_inherit_v0 args; int ret; - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_RGB_CRT, &args); - NVIF_ERRON(ret, &outp->object, "[ACQUIRE proto:RGB_CRT] or:%d", args.or); - return ret; + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_LVDS, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:LVDS] ret:%d", ret); + return ret ?: args.head; +} + +int +nvif_outp_inherit_tmds(struct nvif_outp *outp, u8 *proto_out) +{ + struct nvif_outp_inherit_v0 args; + int ret; + + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_TMDS, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:TMDS] ret:%d", ret); + return ret ?: args.head; +} + +int +nvif_outp_inherit_dp(struct nvif_outp *outp, u8 *proto_out) +{ + struct nvif_outp_inherit_v0 args; + int ret; + + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_DP, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:DP] ret:%d", ret); + + // TODO: Get current link info + + return ret ?: args.head; +} + +int +nvif_outp_inherit_rgb_crt(struct nvif_outp *outp, u8 *proto_out) +{ + struct nvif_outp_inherit_v0 args; + int ret; + + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_RGB_CRT, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:RGB_CRT] ret:%d", ret); + return ret ?: args.head; } int @@ -210,6 +435,61 @@ nvif_outp_load_detect(struct nvif_outp *outp, u32 loadval) return ret < 0 ? ret : args.load; } +int +nvif_outp_edid_get(struct nvif_outp *outp, u8 **pedid) +{ + struct nvif_outp_edid_get_v0 *args; + int ret; + + args = kmalloc(sizeof(*args), GFP_KERNEL); + if (!args) + return -ENOMEM; + + args->version = 0; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_EDID_GET, args, sizeof(*args)); + NVIF_ERRON(ret, &outp->object, "[EDID_GET] size:%d", args->size); + if (ret) + goto done; + + *pedid = kmalloc(args->size, GFP_KERNEL); + if (!*pedid) { + ret = -ENOMEM; + goto done; + } + + memcpy(*pedid, args->data, args->size); + ret = args->size; +done: + kfree(args); + return ret; +} + +enum nvif_outp_detect_status +nvif_outp_detect(struct nvif_outp *outp) +{ + struct nvif_outp_detect_v0 args; + int ret; + + args.version = 0; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_DETECT, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DETECT] status:%02x", args.status); + if (ret) + return UNKNOWN; + + switch (args.status) { + case NVIF_OUTP_DETECT_V0_NOT_PRESENT: return NOT_PRESENT; + case NVIF_OUTP_DETECT_V0_PRESENT: return PRESENT; + case NVIF_OUTP_DETECT_V0_UNKNOWN: return UNKNOWN; + default: + WARN_ON(1); + break; + } + + return UNKNOWN; +} + void nvif_outp_dtor(struct nvif_outp *outp) { @@ -231,6 +511,50 @@ nvif_outp_ctor(struct nvif_disp *disp, const char *name, int id, struct nvif_out if (ret) return ret; + outp->id = args.id; + + switch (args.type) { + case NVIF_OUTP_V0_TYPE_DAC : outp->info.type = NVIF_OUTP_DAC; break; + case NVIF_OUTP_V0_TYPE_SOR : outp->info.type = NVIF_OUTP_SOR; break; + case NVIF_OUTP_V0_TYPE_PIOR: outp->info.type = NVIF_OUTP_PIOR; break; + break; + default: + WARN_ON(1); + nvif_outp_dtor(outp); + return -EINVAL; + } + + switch (args.proto) { + case NVIF_OUTP_V0_PROTO_RGB_CRT: + outp->info.proto = NVIF_OUTP_RGB_CRT; + outp->info.rgb_crt.freq_max = args.rgb_crt.freq_max; + break; + case NVIF_OUTP_V0_PROTO_TMDS: + outp->info.proto = NVIF_OUTP_TMDS; + outp->info.tmds.dual = args.tmds.dual; + break; + case NVIF_OUTP_V0_PROTO_LVDS: + outp->info.proto = NVIF_OUTP_LVDS; + outp->info.lvds.acpi_edid = args.lvds.acpi_edid; + break; + case NVIF_OUTP_V0_PROTO_DP: + outp->info.proto = NVIF_OUTP_DP; + outp->info.dp.aux = args.dp.aux; + outp->info.dp.mst = args.dp.mst; + outp->info.dp.increased_wm = args.dp.increased_wm; + outp->info.dp.link_nr = args.dp.link_nr; + outp->info.dp.link_bw = args.dp.link_bw; + break; + default: + WARN_ON(1); + nvif_outp_dtor(outp); + return -EINVAL; + } + + outp->info.heads = args.heads; + outp->info.ddc = args.ddc; + outp->info.conn = args.conn; + outp->or.id = -1; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c index 91fb494d4009..374212da9e95 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c @@ -79,8 +79,7 @@ nvkm_firmware_get(const struct nvkm_subdev *subdev, const char *fwname, int ver, int i; /* Convert device name to lowercase */ - strncpy(cname, device->chip->name, sizeof(cname)); - cname[sizeof(cname) - 1] = '\0'; + strscpy(cname, device->chip->name, sizeof(cname)); i = strlen(cname); while (i) { --i; diff --git a/drivers/gpu/drm/nouveau/nvkm/core/memory.c b/drivers/gpu/drm/nouveau/nvkm/core/memory.c index c69daac9bac7..a705c2dfca80 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/memory.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/memory.c @@ -140,12 +140,23 @@ nvkm_memory_new(struct nvkm_device *device, enum nvkm_memory_target target, { struct nvkm_instmem *imem = device->imem; struct nvkm_memory *memory; + bool preserve = true; int ret; - if (unlikely(target != NVKM_MEM_TARGET_INST || !imem)) + if (unlikely(!imem)) return -ENOSYS; - ret = nvkm_instobj_new(imem, size, align, zero, &memory); + switch (target) { + case NVKM_MEM_TARGET_INST_SR_LOST: + preserve = false; + break; + case NVKM_MEM_TARGET_INST: + break; + default: + return -ENOSYS; + } + + ret = nvkm_instobj_new(imem, size, align, zero, preserve, &memory); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c index 73104b59f97f..39f7e7ce9f4a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c @@ -23,15 +23,12 @@ */ #include "priv.h" #include "conn.h" -#include "dp.h" #include "head.h" #include "ior.h" #include "outp.h" #include <core/client.h> #include <core/ramht.h> -#include <subdev/bios.h> -#include <subdev/bios/dcb.h> #include <nvif/class.h> #include <nvif/cl0046.h> @@ -105,18 +102,14 @@ static int nvkm_disp_fini(struct nvkm_engine *engine, bool suspend) { struct nvkm_disp *disp = nvkm_disp(engine); - struct nvkm_conn *conn; struct nvkm_outp *outp; if (disp->func->fini) disp->func->fini(disp); list_for_each_entry(outp, &disp->outps, head) { - nvkm_outp_fini(outp); - } - - list_for_each_entry(conn, &disp->conns, head) { - nvkm_conn_fini(conn); + if (outp->func->fini) + outp->func->fini(outp); } return 0; @@ -126,16 +119,12 @@ static int nvkm_disp_init(struct nvkm_engine *engine) { struct nvkm_disp *disp = nvkm_disp(engine); - struct nvkm_conn *conn; struct nvkm_outp *outp; struct nvkm_ior *ior; - list_for_each_entry(conn, &disp->conns, head) { - nvkm_conn_init(conn); - } - list_for_each_entry(outp, &disp->outps, head) { - nvkm_outp_init(outp); + if (outp->func->init) + outp->func->init(outp); } if (disp->func->init) { @@ -159,142 +148,15 @@ nvkm_disp_oneinit(struct nvkm_engine *engine) { struct nvkm_disp *disp = nvkm_disp(engine); struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvkm_bios *bios = subdev->device->bios; - struct nvkm_outp *outp, *outt, *pair; - struct nvkm_conn *conn; struct nvkm_head *head; - struct nvkm_ior *ior; - struct nvbios_connE connE; - struct dcb_output dcbE; - u8 hpd = 0, ver, hdr; - u32 data; int ret, i; - /* Create output path objects for each VBIOS display path. */ - i = -1; - while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { - if (ver < 0x40) /* No support for chipsets prior to NV50. */ - break; - if (dcbE.type == DCB_OUTPUT_UNUSED) - continue; - if (dcbE.type == DCB_OUTPUT_EOL) - break; - outp = NULL; - - switch (dcbE.type) { - case DCB_OUTPUT_ANALOG: - case DCB_OUTPUT_TV: - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - ret = nvkm_outp_new(disp, i, &dcbE, &outp); - break; - case DCB_OUTPUT_DP: - ret = nvkm_dp_new(disp, i, &dcbE, &outp); - break; - case DCB_OUTPUT_WFD: - /* No support for WFD yet. */ - ret = -ENODEV; - continue; - default: - nvkm_warn(subdev, "dcb %d type %d unknown\n", - i, dcbE.type); - continue; - } - - if (ret) { - if (outp) { - if (ret != -ENODEV) - OUTP_ERR(outp, "ctor failed: %d", ret); - else - OUTP_DBG(outp, "not supported"); - nvkm_outp_del(&outp); - continue; - } - nvkm_error(subdev, "failed to create outp %d\n", i); - continue; - } - - list_add_tail(&outp->head, &disp->outps); - hpd = max(hpd, (u8)(dcbE.connector + 1)); - } - - /* Create connector objects based on available output paths. */ - list_for_each_entry_safe(outp, outt, &disp->outps, head) { - /* VBIOS data *should* give us the most useful information. */ - data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, - &connE); - - /* No bios connector data... */ - if (!data) { - /* Heuristic: anything with the same ccb index is - * considered to be on the same connector, any - * output path without an associated ccb entry will - * be put on its own connector. - */ - int ccb_index = outp->info.i2c_index; - if (ccb_index != 0xf) { - list_for_each_entry(pair, &disp->outps, head) { - if (pair->info.i2c_index == ccb_index) { - outp->conn = pair->conn; - break; - } - } - } - - /* Connector shared with another output path. */ - if (outp->conn) - continue; - - memset(&connE, 0x00, sizeof(connE)); - connE.type = DCB_CONNECTOR_NONE; - i = -1; - } else { - i = outp->info.connector; - } - - /* Check that we haven't already created this connector. */ - list_for_each_entry(conn, &disp->conns, head) { - if (conn->index == outp->info.connector) { - outp->conn = conn; - break; - } - } - - if (outp->conn) - continue; - - /* Apparently we need to create a new one! */ - ret = nvkm_conn_new(disp, i, &connE, &outp->conn); - if (ret) { - nvkm_error(subdev, "failed to create outp %d conn: %d\n", outp->index, ret); - nvkm_conn_del(&outp->conn); - list_del(&outp->head); - nvkm_outp_del(&outp); - continue; - } - - list_add_tail(&outp->conn->head, &disp->conns); - } - if (disp->func->oneinit) { ret = disp->func->oneinit(disp); if (ret) return ret; } - /* Enforce identity-mapped SOR assignment for panels, which have - * certain bits (ie. backlight controls) wired to a specific SOR. - */ - list_for_each_entry(outp, &disp->outps, head) { - if (outp->conn->info.type == DCB_CONNECTOR_LVDS || - outp->conn->info.type == DCB_CONNECTOR_eDP) { - ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1); - if (!WARN_ON(!ior)) - ior->identity = true; - outp->identity = true; - } - } - i = 0; list_for_each_entry(head, &disp->heads, head) i = max(i, head->id + 1); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c index fbdae1137864..ff88a5a5253a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c @@ -30,16 +30,6 @@ #include <nvif/event.h> void -nvkm_conn_fini(struct nvkm_conn *conn) -{ -} - -void -nvkm_conn_init(struct nvkm_conn *conn) -{ -} - -void nvkm_conn_del(struct nvkm_conn **pconn) { struct nvkm_conn *conn = *pconn; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h index a0600e72b0ec..01c3146c7066 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h @@ -19,8 +19,6 @@ struct nvkm_conn { int nvkm_conn_new(struct nvkm_disp *, int index, struct nvbios_connE *, struct nvkm_conn **); void nvkm_conn_del(struct nvkm_conn **); -void nvkm_conn_init(struct nvkm_conn *); -void nvkm_conn_fini(struct nvkm_conn *); #define CONN_MSG(c,l,f,a...) do { \ struct nvkm_conn *_conn = (c); \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index b8ac66b4a2c4..a109348bd63b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -41,6 +41,40 @@ */ #define AMPERE_IED_HACK(disp) ((disp)->engine.subdev.device->card_type >= GA100) +static int +nvkm_dp_mst_id_put(struct nvkm_outp *outp, u32 id) +{ + return 0; +} + +static int +nvkm_dp_mst_id_get(struct nvkm_outp *outp, u32 *pid) +{ + *pid = BIT(outp->index); + return 0; +} + +static int +nvkm_dp_aux_xfer(struct nvkm_outp *outp, u8 type, u32 addr, u8 *data, u8 *size) +{ + int ret = nvkm_i2c_aux_acquire(outp->dp.aux); + + if (ret) + return ret; + + ret = nvkm_i2c_aux_xfer(outp->dp.aux, false, type, addr, data, size); + nvkm_i2c_aux_release(outp->dp.aux); + return ret; +} + +static int +nvkm_dp_aux_pwr(struct nvkm_outp *outp, bool pu) +{ + outp->dp.enabled = pu; + nvkm_dp_enable(outp, outp->dp.enabled); + return 0; +} + struct lt_state { struct nvkm_outp *outp; @@ -282,31 +316,20 @@ nvkm_dp_train_link(struct nvkm_outp *outp, int rate) struct lt_state lt = { .outp = outp, .pc2 = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED, + .repeaters = outp->dp.lttprs, }; - u8 sink[2], data; + u8 sink[2]; int ret; OUTP_DBG(outp, "training %dx%02x", ior->dp.nr, ior->dp.bw); - /* Select LTTPR non-transparent mode if we have a valid configuration, - * use transparent mode otherwise. - */ - if (outp->dp.lttpr[0] >= 0x14) { - data = DPCD_LTTPR_MODE_TRANSPARENT; - nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); - - if (outp->dp.lttprs) { - data = DPCD_LTTPR_MODE_NON_TRANSPARENT; - nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); - lt.repeaters = outp->dp.lttprs; - } - } - /* Set desired link configuration on the sink. */ sink[0] = (outp->dp.rate[rate].dpcd < 0) ? ior->dp.bw : 0; sink[1] = ior->dp.nr; if (ior->dp.ef) sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; + if (outp->dp.lt.post_adj) + sink[1] |= 0x20; ret = nvkm_wraux(outp->dp.aux, DPCD_LC00_LINK_BW_SET, sink, 2); if (ret) @@ -447,71 +470,58 @@ nvkm_dp_train_init(struct nvkm_outp *outp) } static int -nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps) +nvkm_dp_drive(struct nvkm_outp *outp, u8 lanes, u8 pe[4], u8 vs[4]) +{ + struct lt_state lt = { + .outp = outp, + .stat[4] = (pe[0] << 2) | (vs[0] << 0) | + (pe[1] << 6) | (vs[1] << 4), + .stat[5] = (pe[2] << 2) | (vs[2] << 0) | + (pe[3] << 6) | (vs[3] << 4), + }; + + return nvkm_dp_train_drive(<, false); +} + +static int +nvkm_dp_train(struct nvkm_outp *outp, bool retrain) { struct nvkm_ior *ior = outp->ior; - int ret = -EINVAL, nr, rate; - u8 pwr; + int ret, rate; - /* Retraining link? Skip source configuration, it can mess up the active modeset. */ - if (atomic_read(&outp->dp.lt.done)) { - for (rate = 0; rate < outp->dp.rates; rate++) { - if (outp->dp.rate[rate].rate == ior->dp.bw * 27000) - return nvkm_dp_train_link(outp, ret); - } - WARN_ON(1); - return -EINVAL; + for (rate = 0; rate < outp->dp.rates; rate++) { + if (outp->dp.rate[rate].rate == (retrain ? ior->dp.bw : outp->dp.lt.bw) * 27000) + break; } - /* Ensure sink is not in a low-power state. */ - if (!nvkm_rdaux(outp->dp.aux, DPCD_SC00, &pwr, 1)) { - if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { - pwr &= ~DPCD_SC00_SET_POWER; - pwr |= DPCD_SC00_SET_POWER_D0; - nvkm_wraux(outp->dp.aux, DPCD_SC00, &pwr, 1); - } + if (WARN_ON(rate == outp->dp.rates)) + return -EINVAL; + + /* Retraining link? Skip source configuration, it can mess up the active modeset. */ + if (retrain) { + mutex_lock(&outp->dp.mutex); + ret = nvkm_dp_train_link(outp, rate); + mutex_unlock(&outp->dp.mutex); + return ret; } + mutex_lock(&outp->dp.mutex); + OUTP_DBG(outp, "training"); + ior->dp.mst = outp->dp.lt.mst; ior->dp.ef = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; - ior->dp.nr = 0; + ior->dp.bw = outp->dp.lt.bw; + ior->dp.nr = outp->dp.lt.nr; - /* Link training. */ - OUTP_DBG(outp, "training"); nvkm_dp_train_init(outp); - - /* Validate and train at configuration requested (if any) on ACQUIRE. */ - if (outp->dp.lt.nr) { - for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) { - for (rate = 0; nr == outp->dp.lt.nr && rate < outp->dp.rates; rate++) { - if (outp->dp.rate[rate].rate / 27000 == outp->dp.lt.bw) { - ior->dp.bw = outp->dp.rate[rate].rate / 27000; - ior->dp.nr = nr; - ret = nvkm_dp_train_links(outp, rate); - } - } - } - } - - /* Otherwise, loop through all valid link configurations that support the data rate. */ - for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) { - for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) { - if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) { - /* Program selected link configuration. */ - ior->dp.bw = outp->dp.rate[rate].rate / 27000; - ior->dp.nr = nr; - ret = nvkm_dp_train_links(outp, rate); - } - } - } - - /* Finish up. */ + ret = nvkm_dp_train_links(outp, rate); nvkm_dp_train_fini(outp); if (ret < 0) OUTP_ERR(outp, "training failed"); else OUTP_DBG(outp, "training done"); - atomic_set(&outp->dp.lt.done, 1); + + mutex_unlock(&outp->dp.mutex); return ret; } @@ -529,155 +539,10 @@ nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) static void nvkm_dp_release(struct nvkm_outp *outp) { - /* Prevent link from being retrained if sink sends an IRQ. */ - atomic_set(&outp->dp.lt.done, 0); outp->ior->dp.nr = 0; -} - -static int -nvkm_dp_acquire(struct nvkm_outp *outp) -{ - struct nvkm_ior *ior = outp->ior; - struct nvkm_head *head; - bool retrain = true; - u32 datakbps = 0; - u32 dataKBps; - u32 linkKBps; - u8 stat[3]; - int ret, i; - - mutex_lock(&outp->dp.mutex); - - /* Check that link configuration meets current requirements. */ - list_for_each_entry(head, &outp->disp->heads, head) { - if (ior->asy.head & (1 << head->id)) { - u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000; - datakbps += khz * head->asy.or.depth; - } - } - - linkKBps = ior->dp.bw * 27000 * ior->dp.nr; - dataKBps = DIV_ROUND_UP(datakbps, 8); - OUTP_DBG(outp, "data %d KB/s link %d KB/s mst %d->%d", - dataKBps, linkKBps, ior->dp.mst, outp->dp.lt.mst); - if (linkKBps < dataKBps || ior->dp.mst != outp->dp.lt.mst) { - OUTP_DBG(outp, "link requirements changed"); - goto done; - } - - /* Check that link is still trained. */ - ret = nvkm_rdaux(outp->dp.aux, DPCD_LS02, stat, 3); - if (ret) { - OUTP_DBG(outp, "failed to read link status, assuming no sink"); - goto done; - } - - if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { - for (i = 0; i < ior->dp.nr; i++) { - u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; - if (!(lane & DPCD_LS02_LANE0_CR_DONE) || - !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || - !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { - OUTP_DBG(outp, "lane %d not equalised", lane); - goto done; - } - } - retrain = false; - } else { - OUTP_DBG(outp, "no inter-lane alignment"); - } - -done: - if (retrain || !atomic_read(&outp->dp.lt.done)) - ret = nvkm_dp_train(outp, dataKBps); - mutex_unlock(&outp->dp.mutex); - return ret; -} - -static bool -nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp) -{ - u8 sink_rates[DPCD_RC10_SUPPORTED_LINK_RATES__SIZE]; - int i, j, k; - - if (outp->conn->info.type != DCB_CONNECTOR_eDP || - outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x13 || - nvkm_rdaux(outp->dp.aux, DPCD_RC10_SUPPORTED_LINK_RATES(0), - sink_rates, sizeof(sink_rates))) - return false; - - for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { - const u32 rate = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; - - if (!rate || WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) - break; - - if (rate > outp->info.dpconf.link_bw * 27000) { - OUTP_DBG(outp, "rate %d !outp", rate); - continue; - } - - for (j = 0; j < outp->dp.rates; j++) { - if (rate > outp->dp.rate[j].rate) { - for (k = outp->dp.rates; k > j; k--) - outp->dp.rate[k] = outp->dp.rate[k - 1]; - break; - } - } - - outp->dp.rate[j].dpcd = i / 2; - outp->dp.rate[j].rate = rate; - outp->dp.rates++; - } - - for (i = 0; i < outp->dp.rates; i++) - OUTP_DBG(outp, "link_rate[%d] = %d", outp->dp.rate[i].dpcd, outp->dp.rate[i].rate); + nvkm_dp_disable(outp, outp->ior); - return outp->dp.rates != 0; -} - -/* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps() - * converted to work inside nvkm. This is a temporary holdover until we start - * passing the drm_dp_aux device through NVKM - */ -static int -nvkm_dp_read_dpcd_caps(struct nvkm_outp *outp) -{ - struct nvkm_i2c_aux *aux = outp->dp.aux; - u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; - int ret; - - ret = nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, DP_RECEIVER_CAP_SIZE); - if (ret < 0) - return ret; - - /* - * Prior to DP1.3 the bit represented by - * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. - * If it is set DP_DPCD_REV at 0000h could be at a value less than - * the true capability of the panel. The only way to check is to - * then compare 0000h and 2200h. - */ - if (!(outp->dp.dpcd[DP_TRAINING_AUX_RD_INTERVAL] & - DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) - return 0; - - ret = nvkm_rdaux(aux, DP_DP13_DPCD_REV, dpcd_ext, sizeof(dpcd_ext)); - if (ret < 0) - return ret; - - if (outp->dp.dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { - OUTP_DBG(outp, "Extended DPCD rev less than base DPCD rev (%d > %d)\n", - outp->dp.dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]); - return 0; - } - - if (!memcmp(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext))) - return 0; - - memcpy(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext)); - - return 0; + nvkm_outp_release(outp); } void @@ -711,66 +576,11 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) OUTP_DBG(outp, "aux power -> always"); nvkm_i2c_aux_monitor(aux, true); outp->dp.aux_pwr = true; - - /* Detect any LTTPRs before reading DPCD receiver caps. */ - if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) && - outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) { - switch (outp->dp.lttpr[2]) { - case 0x80: outp->dp.lttprs = 1; break; - case 0x40: outp->dp.lttprs = 2; break; - case 0x20: outp->dp.lttprs = 3; break; - case 0x10: outp->dp.lttprs = 4; break; - case 0x08: outp->dp.lttprs = 5; break; - case 0x04: outp->dp.lttprs = 6; break; - case 0x02: outp->dp.lttprs = 7; break; - case 0x01: outp->dp.lttprs = 8; break; - default: - /* Unknown LTTPR count, we'll switch to transparent mode. */ - WARN_ON(1); - outp->dp.lttprs = 0; - break; - } - } else { - /* No LTTPR support, or zero LTTPR count - don't touch it at all. */ - memset(outp->dp.lttpr, 0x00, sizeof(outp->dp.lttpr)); - } - - if (!nvkm_dp_read_dpcd_caps(outp)) { - const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 }; - const u8 *rate; - int rate_max; - - outp->dp.rates = 0; - outp->dp.links = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; - outp->dp.links = min(outp->dp.links, outp->info.dpconf.link_nr); - if (outp->dp.lttprs && outp->dp.lttpr[4]) - outp->dp.links = min_t(int, outp->dp.links, outp->dp.lttpr[4]); - - rate_max = outp->dp.dpcd[DPCD_RC01_MAX_LINK_RATE]; - rate_max = min(rate_max, outp->info.dpconf.link_bw); - if (outp->dp.lttprs && outp->dp.lttpr[1]) - rate_max = min_t(int, rate_max, outp->dp.lttpr[1]); - - if (!nvkm_dp_enable_supported_link_rates(outp)) { - for (rate = rates; *rate; rate++) { - if (*rate > rate_max) - continue; - - if (WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) - break; - - outp->dp.rate[outp->dp.rates].dpcd = -1; - outp->dp.rate[outp->dp.rates].rate = *rate * 27000; - outp->dp.rates++; - } - } - } } else if (!auxpwr && outp->dp.aux_pwr) { OUTP_DBG(outp, "aux power -> demand"); nvkm_i2c_aux_monitor(aux, false); outp->dp.aux_pwr = false; - atomic_set(&outp->dp.lt.done, 0); /* Restore eDP panel GPIO to its prior state if we changed it, as * it could potentially interfere with other outputs. @@ -793,6 +603,7 @@ nvkm_dp_fini(struct nvkm_outp *outp) static void nvkm_dp_init(struct nvkm_outp *outp) { + nvkm_outp_init(outp); nvkm_dp_enable(outp, outp->dp.enabled); } @@ -807,9 +618,18 @@ nvkm_dp_func = { .dtor = nvkm_dp_dtor, .init = nvkm_dp_init, .fini = nvkm_dp_fini, - .acquire = nvkm_dp_acquire, + .detect = nvkm_outp_detect, + .inherit = nvkm_outp_inherit, + .acquire = nvkm_outp_acquire, .release = nvkm_dp_release, - .disable = nvkm_dp_disable, + .bl.get = nvkm_outp_bl_get, + .bl.set = nvkm_outp_bl_set, + .dp.aux_pwr = nvkm_dp_aux_pwr, + .dp.aux_xfer = nvkm_dp_aux_xfer, + .dp.train = nvkm_dp_train, + .dp.drive = nvkm_dp_drive, + .dp.mst_id_get = nvkm_dp_mst_id_get, + .dp.mst_id_put = nvkm_dp_mst_id_put, }; int @@ -819,7 +639,7 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n struct nvkm_bios *bios = device->bios; struct nvkm_i2c *i2c = device->i2c; struct nvkm_outp *outp; - u8 hdr, cnt, len; + u8 ver, hdr, cnt, len; u32 data; int ret; @@ -847,7 +667,9 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len); + data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len); + outp->dp.mst = data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04); + mutex_init(&outp->dp.mutex); - atomic_set(&outp->dp.lt.done, 0); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c index 23ae451ba473..1be97a68a83e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c @@ -124,6 +124,7 @@ g84_sor = { .state = nv50_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = &nv50_sor_bl, .hdmi = &g84_sor_hdmi, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c index 67ef889a0c5f..843a2661ce9d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c @@ -295,6 +295,7 @@ g94_sor = { .clock = nv50_sor_clock, .war_2 = g94_sor_war_2, .war_3 = g94_sor_war_3, + .bl = &nv50_sor_bl, .hdmi = &g84_sor_hdmi, .dp = &g94_sor_dp, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c index 52099b75f52a..efe66ba3c61f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c @@ -105,6 +105,7 @@ ga102_sor = { .state = gv100_sor_state, .power = nv50_sor_power, .clock = ga102_sor_clock, + .bl = >215_sor_bl, .hdmi = &gv100_sor_hdmi, .dp = &ga102_sor_dp, .hda = &gv100_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c index a48e9bdf4cd0..b48ead31da30 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c @@ -328,6 +328,7 @@ gf119_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gf119_sor_hdmi, .dp = &gf119_sor_dp, .hda = &gf119_sor_hda, @@ -1038,7 +1039,6 @@ gf119_disp_super(struct work_struct *work) continue; nv50_disp_super_2_0(disp, head); } - nvkm_outp_route(disp); list_for_each_entry(head, &disp->heads, head) { if (!(mask[head->id] & 0x00010000)) continue; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c index 876a21a0cebb..a3e2fbadade4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c @@ -115,6 +115,7 @@ gk104_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gk104_sor_hdmi, .dp = &gf119_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c index b4d8e868616f..688e123ad482 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c @@ -70,6 +70,7 @@ gm107_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gk104_sor_hdmi, .dp = &gm107_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c index 562ebae57d44..511e7831b2f5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c @@ -68,15 +68,23 @@ gm200_sor_dp = { }; void -gm200_sor_hdmi_scdc(struct nvkm_ior *ior, u8 scdc) +gm200_sor_hdmi_scdc(struct nvkm_ior *ior, u32 khz, bool support, bool scrambling, + bool scrambling_low_rates) { struct nvkm_device *device = ior->disp->engine.subdev.device; const u32 soff = nv50_ior_base(ior); - const u32 ctrl = scdc & 0x3; + u32 ctrl = 0; - nvkm_mask(device, 0x61c5bc + soff, 0x00000003, ctrl); + ior->tmds.high_speed = khz > 340000; + + if (support && scrambling) { + if (ior->tmds.high_speed) + ctrl |= 0x00000002; + if (ior->tmds.high_speed || scrambling_low_rates) + ctrl |= 0x00000001; + } - ior->tmds.high_speed = !!(scdc & 0x2); + nvkm_mask(device, 0x61c5bc + soff, 0x00000003, ctrl); } const struct nvkm_ior_func_hdmi @@ -139,6 +147,7 @@ gm200_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gm200_sor_hdmi, .dp = &gm200_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c index 7f1eb4332040..4070447bd800 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c @@ -37,6 +37,7 @@ gp100_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gm200_sor_hdmi, .dp = &gm200_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c index 506ffbe7b842..6318721b66f6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c @@ -182,11 +182,49 @@ gt215_sor_hdmi = { .infoframe_vsi = gt215_sor_hdmi_infoframe_vsi, }; +static int +gt215_sor_bl_set(struct nvkm_ior *ior, int lvl) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div, val; + + div = nvkm_rd32(device, 0x61c080 + soff); + val = (lvl * div) / 100; + if (div) + nvkm_wr32(device, 0x61c084 + soff, 0xc0000000 | val); + + return 0; +} + +static int +gt215_sor_bl_get(struct nvkm_ior *ior) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div, val; + + div = nvkm_rd32(device, 0x61c080 + soff); + val = nvkm_rd32(device, 0x61c084 + soff); + val &= 0x00ffffff; + if (div && div >= val) + return ((val * 100) + (div / 2)) / div; + + return 100; +} + +const struct nvkm_ior_func_bl +gt215_sor_bl = { + .get = gt215_sor_bl_get, + .set = gt215_sor_bl_set, +}; + static const struct nvkm_ior_func gt215_sor = { .state = g94_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = >215_sor_bl, .hdmi = >215_sor_hdmi, .dp = >215_sor_dp, .hda = >215_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c index 4ebc030e40d1..e1634f7bca56 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c @@ -212,6 +212,7 @@ gv100_sor = { .state = gv100_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gv100_sor_hdmi, .dp = &gv100_sor_dp, .hda = &gv100_sor_hda, @@ -863,7 +864,6 @@ gv100_disp_super(struct work_struct *work) continue; nv50_disp_super_2_0(disp, head); } - nvkm_outp_route(disp); list_for_each_entry(head, &disp->heads, head) { if (!(mask[head->id] & 0x00010000)) continue; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index da1b1a626ef2..9beb9d1e8633 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -63,11 +63,18 @@ struct nvkm_ior_func { void (*war_2)(struct nvkm_ior *); void (*war_3)(struct nvkm_ior *); + const struct nvkm_ior_func_bl { + int (*get)(struct nvkm_ior *); + int (*set)(struct nvkm_ior *, int lvl); + } *bl; + const struct nvkm_ior_func_hdmi { void (*ctrl)(struct nvkm_ior *, int head, bool enable, u8 max_ac_packet, u8 rekey); - void (*scdc)(struct nvkm_ior *, u8 scdc); + void (*scdc)(struct nvkm_ior *, u32 khz, bool support, bool scrambling, + bool scrambling_low_rates); void (*infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size); void (*infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size); + void (*audio)(struct nvkm_ior *, int head, bool enable); } *hdmi; const struct nvkm_ior_func_dp { @@ -77,6 +84,8 @@ struct nvkm_ior_func { void (*pattern)(struct nvkm_ior *, int pattern); void (*drive)(struct nvkm_ior *, int ln, int pc, int dc, int pe, int tx_pu); + int (*sst)(struct nvkm_ior *, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym); void (*vcpi)(struct nvkm_ior *, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned); void (*audio)(struct nvkm_ior *, int head, bool enable); @@ -122,6 +131,7 @@ int nv50_sor_cnt(struct nvkm_disp *, unsigned long *); void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool); void nv50_sor_clock(struct nvkm_ior *); +extern const struct nvkm_ior_func_bl nv50_sor_bl; int g84_sor_new(struct nvkm_disp *, int); extern const struct nvkm_ior_func_hdmi g84_sor_hdmi; @@ -138,6 +148,7 @@ void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8); void g94_sor_dp_watermark(struct nvkm_ior *, int, u8); +extern const struct nvkm_ior_func_bl gt215_sor_bl; extern const struct nvkm_ior_func_hdmi gt215_sor_hdmi; void gt215_sor_dp_audio(struct nvkm_ior *, int, bool); extern const struct nvkm_ior_func_hda gt215_sor_hda; @@ -167,7 +178,7 @@ void gm107_sor_dp_pattern(struct nvkm_ior *, int); void gm200_sor_route_set(struct nvkm_outp *, struct nvkm_ior *); int gm200_sor_route_get(struct nvkm_outp *, int *); extern const struct nvkm_ior_func_hdmi gm200_sor_hdmi; -void gm200_sor_hdmi_scdc(struct nvkm_ior *, u8); +void gm200_sor_hdmi_scdc(struct nvkm_ior *, u32, bool, bool, bool); extern const struct nvkm_ior_func_dp gm200_sor_dp; void gm200_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c index f96ba4752655..e0c5fb6df3d7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c @@ -44,6 +44,7 @@ mcp89_sor = { .state = g94_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = >215_sor_bl, .hdmi = >215_sor_hdmi, .dp = &mcp89_sor_dp, .hda = >215_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index be8116802960..2d05e2f7e46b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -23,7 +23,9 @@ */ #include "priv.h" #include "chan.h" +#include "conn.h" #include "head.h" +#include "dp.h" #include "ior.h" #include "outp.h" @@ -156,6 +158,37 @@ nv50_pior_cnt(struct nvkm_disp *disp, unsigned long *pmask) return 3; } +static int +nv50_sor_bl_set(struct nvkm_ior *ior, int lvl) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div = 1025; + u32 val = (lvl * div) / 100; + + nvkm_wr32(device, 0x61c084 + soff, 0x80000000 | val); + return 0; +} + +static int +nv50_sor_bl_get(struct nvkm_ior *ior) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div = 1025; + u32 val; + + val = nvkm_rd32(device, 0x61c084 + soff); + val &= 0x000007ff; + return ((val * 100) + (div / 2)) / div; +} + +const struct nvkm_ior_func_bl +nv50_sor_bl = { + .get = nv50_sor_bl_get, + .set = nv50_sor_bl_set, +}; + void nv50_sor_clock(struct nvkm_ior *sor) { @@ -220,6 +253,7 @@ nv50_sor = { .state = nv50_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = &nv50_sor_bl, }; static int @@ -1254,10 +1288,6 @@ nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head) ior->asy.link = outp->lvds.dual ? 3 : 1; } - /* Handle any link training, etc. */ - if (outp && outp->func->acquire) - outp->func->acquire(outp); - /* Execute OnInt2 IED script. */ nv50_disp_super_ied_on(head, ior, 0, khz); @@ -1287,7 +1317,6 @@ nv50_disp_super_2_1(struct nvkm_disp *disp, struct nvkm_head *head) void nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head) { - struct nvkm_outp *outp; struct nvkm_ior *ior; /* Determine which OR, if any, we're detaching from the head. */ @@ -1298,14 +1327,6 @@ nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head) /* Execute OffInt2 IED script. */ nv50_disp_super_ied_off(head, ior, 2); - - /* If we're shutting down the OR's only active head, execute - * the output path's disable function. - */ - if (ior->arm.head == (1 << head->id)) { - if ((outp = ior->arm.outp) && outp->func->disable) - outp->func->disable(outp, ior); - } } void @@ -1371,7 +1392,6 @@ nv50_disp_super(struct work_struct *work) continue; nv50_disp_super_2_0(disp, head); } - nvkm_outp_route(disp); list_for_each_entry(head, &disp->heads, head) { if (!(super & (0x00000200 << head->id))) continue; @@ -1563,7 +1583,15 @@ nv50_disp_oneinit(struct nvkm_disp *disp) const struct nvkm_disp_func *func = disp->func; struct nvkm_subdev *subdev = &disp->engine.subdev; struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvkm_outp *outp, *outt, *pair; + struct nvkm_conn *conn; + struct nvkm_ior *ior; int ret, i; + u8 ver, hdr; + u32 data; + struct dcb_output dcbE; + struct nvbios_connE connE; if (func->wndw.cnt) { disp->wndw.nr = func->wndw.cnt(disp, &disp->wndw.mask); @@ -1610,8 +1638,130 @@ nv50_disp_oneinit(struct nvkm_disp *disp) if (ret) return ret; - return nvkm_ramht_new(device, func->ramht_size ? func->ramht_size : - 0x1000, 0, disp->inst, &disp->ramht); + ret = nvkm_ramht_new(device, func->ramht_size ? func->ramht_size : 0x1000, 0, disp->inst, + &disp->ramht); + if (ret) + return ret; + + /* Create output path objects for each VBIOS display path. */ + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { + if (WARN_ON((ver & 0xf0) != 0x40)) + return -EINVAL; + if (dcbE.type == DCB_OUTPUT_UNUSED) + continue; + if (dcbE.type == DCB_OUTPUT_EOL) + break; + outp = NULL; + + switch (dcbE.type) { + case DCB_OUTPUT_ANALOG: + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + ret = nvkm_outp_new(disp, i, &dcbE, &outp); + break; + case DCB_OUTPUT_DP: + ret = nvkm_dp_new(disp, i, &dcbE, &outp); + break; + case DCB_OUTPUT_TV: + case DCB_OUTPUT_WFD: + /* No support for WFD yet. */ + ret = -ENODEV; + continue; + default: + nvkm_warn(subdev, "dcb %d type %d unknown\n", + i, dcbE.type); + continue; + } + + if (ret) { + if (outp) { + if (ret != -ENODEV) + OUTP_ERR(outp, "ctor failed: %d", ret); + else + OUTP_DBG(outp, "not supported"); + nvkm_outp_del(&outp); + continue; + } + nvkm_error(subdev, "failed to create outp %d\n", i); + continue; + } + + list_add_tail(&outp->head, &disp->outps); + } + + /* Create connector objects based on available output paths. */ + list_for_each_entry_safe(outp, outt, &disp->outps, head) { + /* VBIOS data *should* give us the most useful information. */ + data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, + &connE); + + /* No bios connector data... */ + if (!data) { + /* Heuristic: anything with the same ccb index is + * considered to be on the same connector, any + * output path without an associated ccb entry will + * be put on its own connector. + */ + int ccb_index = outp->info.i2c_index; + if (ccb_index != 0xf) { + list_for_each_entry(pair, &disp->outps, head) { + if (pair->info.i2c_index == ccb_index) { + outp->conn = pair->conn; + break; + } + } + } + + /* Connector shared with another output path. */ + if (outp->conn) + continue; + + memset(&connE, 0x00, sizeof(connE)); + connE.type = DCB_CONNECTOR_NONE; + i = -1; + } else { + i = outp->info.connector; + } + + /* Check that we haven't already created this connector. */ + list_for_each_entry(conn, &disp->conns, head) { + if (conn->index == outp->info.connector) { + outp->conn = conn; + break; + } + } + + if (outp->conn) + continue; + + /* Apparently we need to create a new one! */ + ret = nvkm_conn_new(disp, i, &connE, &outp->conn); + if (ret) { + nvkm_error(subdev, "failed to create outp %d conn: %d\n", outp->index, ret); + nvkm_conn_del(&outp->conn); + list_del(&outp->head); + nvkm_outp_del(&outp); + continue; + } + + list_add_tail(&outp->conn->head, &disp->conns); + } + + /* Enforce identity-mapped SOR assignment for panels, which have + * certain bits (ie. backlight controls) wired to a specific SOR. + */ + list_for_each_entry(outp, &disp->outps, head) { + if (outp->conn->info.type == DCB_CONNECTOR_LVDS || + outp->conn->info.type == DCB_CONNECTOR_eDP) { + ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1); + if (!WARN_ON(!ior)) + ior->identity = true; + outp->identity = true; + } + } + + return 0; } static const struct nvkm_disp_func diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index 6094805fbd63..bfb2a4db8d64 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -22,14 +22,16 @@ * Authors: Ben Skeggs */ #include "outp.h" +#include "conn.h" #include "dp.h" #include "ior.h" #include <subdev/bios.h> #include <subdev/bios/dcb.h> +#include <subdev/gpio.h> #include <subdev/i2c.h> -void +static void nvkm_outp_route(struct nvkm_disp *disp) { struct nvkm_outp *outp; @@ -46,8 +48,8 @@ nvkm_outp_route(struct nvkm_disp *disp) list_for_each_entry(ior, &disp->iors, head) { if ((outp = ior->asy.outp)) { - OUTP_DBG(outp, "acquire %s", ior->name); if (ior->asy.outp != ior->arm.outp) { + OUTP_DBG(outp, "acquire %s", ior->name); if (ior->func->route.set) ior->func->route.set(outp, ior); ior->arm.outp = ior->asy.outp; @@ -87,22 +89,20 @@ nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) } void -nvkm_outp_release(struct nvkm_outp *outp, u8 user) +nvkm_outp_release_or(struct nvkm_outp *outp, u8 user) { struct nvkm_ior *ior = outp->ior; OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior); if (ior) { outp->acquired &= ~user; if (!outp->acquired) { - if (outp->func->release && outp->ior) - outp->func->release(outp); outp->ior->asy.outp = NULL; outp->ior = NULL; } } } -static inline int +int nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior) { outp->ior = ior; @@ -140,7 +140,7 @@ nvkm_outp_acquire_hda(struct nvkm_outp *outp, enum nvkm_ior_type type, } int -nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda) +nvkm_outp_acquire_or(struct nvkm_outp *outp, u8 user, bool hda) { struct nvkm_ior *ior = outp->ior; enum nvkm_ior_proto proto; @@ -207,39 +207,110 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda) return nvkm_outp_acquire_hda(outp, type, user, false); } +int +nvkm_outp_bl_set(struct nvkm_outp *outp, int level) +{ + int ret; + + ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false); + if (ret) + return ret; + + if (outp->ior->func->bl) + ret = outp->ior->func->bl->set(outp->ior, level); + else + ret = -EINVAL; + + nvkm_outp_release_or(outp, NVKM_OUTP_PRIV); + return ret; +} + +int +nvkm_outp_bl_get(struct nvkm_outp *outp) +{ + int ret; + + ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false); + if (ret) + return ret; + + if (outp->ior->func->bl) + ret = outp->ior->func->bl->get(outp->ior); + else + ret = -EINVAL; + + nvkm_outp_release_or(outp, NVKM_OUTP_PRIV); + return ret; +} + +int +nvkm_outp_detect(struct nvkm_outp *outp) +{ + struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; + int ret = -EINVAL; + + if (outp->conn->info.hpd != DCB_GPIO_UNUSED) { + ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, outp->conn->info.hpd); + if (ret < 0) + return ret; + if (ret) + return 1; + + /*TODO: Look into returning NOT_PRESENT if !HPD on DVI/HDMI. + * + * It's uncertain whether this is accurate for all older chipsets, + * so we're returning UNKNOWN, and the DRM will probe DDC instead. + */ + if (outp->info.type == DCB_OUTPUT_DP) + return 0; + } + + return ret; +} + void -nvkm_outp_fini(struct nvkm_outp *outp) +nvkm_outp_release(struct nvkm_outp *outp) { - if (outp->func->fini) - outp->func->fini(outp); + nvkm_outp_release_or(outp, NVKM_OUTP_USER); + nvkm_outp_route(outp->disp); } -static void -nvkm_outp_init_route(struct nvkm_outp *outp) +int +nvkm_outp_acquire(struct nvkm_outp *outp, bool hda) +{ + int ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_USER, hda); + + if (ret) + return ret; + + nvkm_outp_route(outp->disp); + return 0; +} + +struct nvkm_ior * +nvkm_outp_inherit(struct nvkm_outp *outp) { struct nvkm_disp *disp = outp->disp; + struct nvkm_ior *ior; enum nvkm_ior_proto proto; enum nvkm_ior_type type; - struct nvkm_ior *ior; int id, link; /* Find any OR from the class that is able to support this device. */ proto = nvkm_outp_xlat(outp, &type); if (proto == UNKNOWN) - return; + return NULL; ior = nvkm_ior_find(disp, type, -1); - if (!ior) { - WARN_ON(1); - return; - } + if (WARN_ON(!ior)) + return NULL; /* Determine the specific OR, if any, this device is attached to. */ if (ior->func->route.get) { id = ior->func->route.get(outp, &link); if (id < 0) { OUTP_DBG(outp, "no route"); - return; + return NULL; } } else { /* Prior to DCB 4.1, this is hardwired like so. */ @@ -248,10 +319,24 @@ nvkm_outp_init_route(struct nvkm_outp *outp) } ior = nvkm_ior_find(disp, type, id); - if (!ior) { - WARN_ON(1); + if (WARN_ON(!ior)) + return NULL; + + return ior; +} + +void +nvkm_outp_init(struct nvkm_outp *outp) +{ + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + struct nvkm_ior *ior; + + /* Find any OR from the class that is able to support this device. */ + proto = nvkm_outp_xlat(outp, &type); + ior = outp->func->inherit(outp); + if (!ior) return; - } /* Determine if the OR is already configured for this device. */ ior->func->state(ior, &ior->arm); @@ -274,14 +359,6 @@ nvkm_outp_init_route(struct nvkm_outp *outp) } void -nvkm_outp_init(struct nvkm_outp *outp) -{ - nvkm_outp_init_route(outp); - if (outp->func->init) - outp->func->init(outp); -} - -void nvkm_outp_del(struct nvkm_outp **poutp) { struct nvkm_outp *outp = *poutp; @@ -328,6 +405,13 @@ nvkm_outp_new_(const struct nvkm_outp_func *func, struct nvkm_disp *disp, static const struct nvkm_outp_func nvkm_outp = { + .init = nvkm_outp_init, + .detect = nvkm_outp_detect, + .inherit = nvkm_outp_inherit, + .acquire = nvkm_outp_acquire, + .release = nvkm_outp_release, + .bl.get = nvkm_outp_bl_get, + .bl.set = nvkm_outp_bl_set, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index 4e7f873f66e2..ebd2f499b4b1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -35,6 +35,8 @@ struct nvkm_outp { struct { struct nvbios_dpout info; u8 version; + bool mst; + bool increased_wm; struct nvkm_i2c_aux *aux; @@ -50,14 +52,13 @@ struct nvkm_outp { u32 rate; } rate[8]; int rates; - int links; struct mutex mutex; struct { - atomic_t done; u8 nr; u8 bw; bool mst; + bool post_adj; } lt; } dp; }; @@ -74,17 +75,45 @@ int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *, struct nvk void nvkm_outp_del(struct nvkm_outp **); void nvkm_outp_init(struct nvkm_outp *); void nvkm_outp_fini(struct nvkm_outp *); -int nvkm_outp_acquire(struct nvkm_outp *, u8 user, bool hda); -void nvkm_outp_release(struct nvkm_outp *, u8 user); -void nvkm_outp_route(struct nvkm_disp *); + +int nvkm_outp_detect(struct nvkm_outp *); + +struct nvkm_ior *nvkm_outp_inherit(struct nvkm_outp *); +int nvkm_outp_acquire(struct nvkm_outp *, bool hda); +int nvkm_outp_acquire_or(struct nvkm_outp *, u8 user, bool hda); +int nvkm_outp_acquire_ior(struct nvkm_outp *, u8 user, struct nvkm_ior *); +void nvkm_outp_release(struct nvkm_outp *); +void nvkm_outp_release_or(struct nvkm_outp *, u8 user); + +int nvkm_outp_bl_get(struct nvkm_outp *); +int nvkm_outp_bl_set(struct nvkm_outp *, int level); struct nvkm_outp_func { void *(*dtor)(struct nvkm_outp *); void (*init)(struct nvkm_outp *); void (*fini)(struct nvkm_outp *); - int (*acquire)(struct nvkm_outp *); + + int (*detect)(struct nvkm_outp *); + int (*edid_get)(struct nvkm_outp *, u8 *data, u16 *size); + + struct nvkm_ior *(*inherit)(struct nvkm_outp *); + int (*acquire)(struct nvkm_outp *, bool hda); void (*release)(struct nvkm_outp *); - void (*disable)(struct nvkm_outp *, struct nvkm_ior *); + + struct { + int (*get)(struct nvkm_outp *); + int (*set)(struct nvkm_outp *, int level); + } bl; + + struct { + int (*aux_pwr)(struct nvkm_outp *, bool pu); + int (*aux_xfer)(struct nvkm_outp *, u8 type, u32 addr, u8 *data, u8 *size); + int (*rates)(struct nvkm_outp *); + int (*train)(struct nvkm_outp *, bool retrain); + int (*drive)(struct nvkm_outp *, u8 lanes, u8 pe[4], u8 vs[4]); + int (*mst_id_get)(struct nvkm_outp *, u32 *id); + int (*mst_id_put)(struct nvkm_outp *, u32 id); + } dp; }; #define OUTP_MSG(o,l,f,a...) do { \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c index f5242a672279..19f5d3a6035e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c @@ -88,6 +88,7 @@ tu102_sor = { .state = gv100_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gv100_sor_hdmi, .dp = &tu102_sor_dp, .hda = &gv100_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c index 46b057fe1412..ff82bb248492 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c @@ -109,46 +109,6 @@ nvkm_uconn_uevent(struct nvkm_object *object, void *argv, u32 argc, struct nvkm_ nvkm_uconn_uevent_gpio); } -static int -nvkm_uconn_mthd_hpd_status(struct nvkm_conn *conn, void *argv, u32 argc) -{ - struct nvkm_gpio *gpio = conn->disp->engine.subdev.device->gpio; - union nvif_conn_hpd_status_args *args = argv; - - if (argc != sizeof(args->v0) || args->v0.version != 0) - return -ENOSYS; - - args->v0.support = gpio && conn->info.hpd != DCB_GPIO_UNUSED; - args->v0.present = 0; - - if (args->v0.support) { - int ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, conn->info.hpd); - - if (WARN_ON(ret < 0)) { - args->v0.support = false; - return 0; - } - - args->v0.present = ret; - } - - return 0; -} - -static int -nvkm_uconn_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) -{ - struct nvkm_conn *conn = nvkm_uconn(object); - - switch (mthd) { - case NVIF_CONN_V0_HPD_STATUS: return nvkm_uconn_mthd_hpd_status(conn, argv, argc); - default: - break; - } - - return -EINVAL; -} - static void * nvkm_uconn_dtor(struct nvkm_object *object) { @@ -164,7 +124,6 @@ nvkm_uconn_dtor(struct nvkm_object *object) static const struct nvkm_object_func nvkm_uconn = { .dtor = nvkm_uconn_dtor, - .mthd = nvkm_uconn_mthd, .uevent = nvkm_uconn_uevent, }; @@ -192,6 +151,32 @@ nvkm_uconn_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv ret = -EBUSY; spin_lock(&disp->client.lock); if (!conn->object.func) { + switch (conn->info.type) { + case DCB_CONNECTOR_VGA : args->v0.type = NVIF_CONN_V0_VGA; break; + case DCB_CONNECTOR_TV_0 : + case DCB_CONNECTOR_TV_1 : + case DCB_CONNECTOR_TV_3 : args->v0.type = NVIF_CONN_V0_TV; break; + case DCB_CONNECTOR_DMS59_0 : + case DCB_CONNECTOR_DMS59_1 : + case DCB_CONNECTOR_DVI_I : args->v0.type = NVIF_CONN_V0_DVI_I; break; + case DCB_CONNECTOR_DVI_D : args->v0.type = NVIF_CONN_V0_DVI_D; break; + case DCB_CONNECTOR_LVDS : args->v0.type = NVIF_CONN_V0_LVDS; break; + case DCB_CONNECTOR_LVDS_SPWG: args->v0.type = NVIF_CONN_V0_LVDS_SPWG; break; + case DCB_CONNECTOR_DMS59_DP0: + case DCB_CONNECTOR_DMS59_DP1: + case DCB_CONNECTOR_DP : + case DCB_CONNECTOR_mDP : + case DCB_CONNECTOR_USB_C : args->v0.type = NVIF_CONN_V0_DP; break; + case DCB_CONNECTOR_eDP : args->v0.type = NVIF_CONN_V0_EDP; break; + case DCB_CONNECTOR_HDMI_0 : + case DCB_CONNECTOR_HDMI_1 : + case DCB_CONNECTOR_HDMI_C : args->v0.type = NVIF_CONN_V0_HDMI; break; + default: + WARN_ON(1); + ret = -EINVAL; + break; + } + nvkm_object_ctor(&nvkm_uconn, oclass, &conn->object); *pobject = &conn->object; ret = 0; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c index fc283a4a1522..e4279f1772a1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c @@ -25,6 +25,8 @@ #include "head.h" #include "ior.h" +#include <subdev/i2c.h> + #include <nvif/if0012.h> static int @@ -44,17 +46,121 @@ nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp *outp, void *argv, u32 argc) } static int -nvkm_uoutp_mthd_dp_retrain(struct nvkm_outp *outp, void *argv, u32 argc) +nvkm_uoutp_mthd_dp_mst_id_put(struct nvkm_outp *outp, void *argv, u32 argc) { - union nvif_outp_dp_retrain_args *args = argv; + union nvif_outp_dp_mst_id_put_args *args = argv; - if (argc != sizeof(args->vn)) + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.mst_id_put) + return -EINVAL; + + return outp->func->dp.mst_id_put(outp, args->v0.id); +} + +static int +nvkm_uoutp_mthd_dp_mst_id_get(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_mst_id_get_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.mst_id_get) + return -EINVAL; + + return outp->func->dp.mst_id_get(outp, &args->v0.id); +} + +static int +nvkm_uoutp_mthd_dp_sst(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_sst_args *args = argv; + struct nvkm_disp *disp = outp->disp; + struct nvkm_ior *ior = outp->ior; + + if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - if (!atomic_read(&outp->dp.lt.done)) + if (!ior->func->dp || !nvkm_head_find(disp, args->v0.head)) + return -EINVAL; + if (!ior->func->dp->sst) return 0; - return outp->func->acquire(outp); + return ior->func->dp->sst(ior, args->v0.head, + outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP, + args->v0.watermark, args->v0.hblanksym, args->v0.vblanksym); +} + +static int +nvkm_uoutp_mthd_dp_drive(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_drive_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.drive) + return -EINVAL; + + return outp->func->dp.drive(outp, args->v0.lanes, args->v0.pe, args->v0.vs); +} + +static int +nvkm_uoutp_mthd_dp_train(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_train_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.train) + return -EINVAL; + + if (!args->v0.retrain) { + memcpy(outp->dp.dpcd, args->v0.dpcd, sizeof(outp->dp.dpcd)); + outp->dp.lttprs = args->v0.lttprs; + outp->dp.lt.nr = args->v0.link_nr; + outp->dp.lt.bw = args->v0.link_bw / 27000; + outp->dp.lt.mst = args->v0.mst; + outp->dp.lt.post_adj = args->v0.post_lt_adj; + } + + return outp->func->dp.train(outp, args->v0.retrain); +} + +static int +nvkm_uoutp_mthd_dp_rates(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_rates_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (args->v0.rates > ARRAY_SIZE(outp->dp.rate)) + return -EINVAL; + + for (int i = 0; i < args->v0.rates; i++) { + outp->dp.rate[i].dpcd = args->v0.rate[i].dpcd; + outp->dp.rate[i].rate = args->v0.rate[i].rate; + } + + outp->dp.rates = args->v0.rates; + + if (outp->func->dp.rates) + outp->func->dp.rates(outp); + + return 0; +} + +static int +nvkm_uoutp_mthd_dp_aux_xfer(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_aux_xfer_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.aux_xfer) + return -EINVAL; + + return outp->func->dp.aux_xfer(outp, args->v0.type, args->v0.addr, + args->v0.data, &args->v0.size); } static int @@ -64,10 +170,10 @@ nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp *outp, void *argv, u32 argc) if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; + if (!outp->func->dp.aux_pwr) + return -EINVAL; - outp->dp.enabled = !!args->v0.state; - nvkm_dp_enable(outp, outp->dp.enabled); - return 0; + return outp->func->dp.aux_pwr(outp, !!args->v0.state); } static int @@ -88,12 +194,20 @@ nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc) if (argc && args->v0.data[0]) { if (outp->info.type == DCB_OUTPUT_DP) ior->func->dp->audio(ior, args->v0.head, true); + else + if (ior->func->hdmi->audio) + ior->func->hdmi->audio(ior, args->v0.head, true); + ior->func->hda->hpd(ior, args->v0.head, true); ior->func->hda->eld(ior, args->v0.head, args->v0.data, argc); } else { + ior->func->hda->hpd(ior, args->v0.head, false); + if (outp->info.type == DCB_OUTPUT_DP) ior->func->dp->audio(ior, args->v0.head, false); - ior->func->hda->hpd(ior, args->v0.head, false); + else + if (ior->func->hdmi->audio) + ior->func->hdmi->audio(ior, args->v0.head, false); } return 0; @@ -126,84 +240,105 @@ nvkm_uoutp_mthd_infoframe(struct nvkm_outp *outp, void *argv, u32 argc) } static int -nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc) +nvkm_uoutp_mthd_hdmi(struct nvkm_outp *outp, void *argv, u32 argc) { - struct nvkm_head *head = outp->asy.head; + union nvif_outp_hdmi_args *args = argv; struct nvkm_ior *ior = outp->ior; - union nvif_outp_release_args *args = argv; - if (argc != sizeof(args->vn)) + if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - if (ior->func->hdmi && head) { - ior->func->hdmi->infoframe_avi(ior, head->id, NULL, 0); - ior->func->hdmi->infoframe_vsi(ior, head->id, NULL, 0); - ior->func->hdmi->ctrl(ior, head->id, false, 0, 0); + if (!(outp->asy.head = nvkm_head_find(outp->disp, args->v0.head))) + return -EINVAL; + + if (!ior->func->hdmi || + args->v0.max_ac_packet > 0x1f || + args->v0.rekey > 0x7f || + (args->v0.scdc && !ior->func->hdmi->scdc)) + return -EINVAL; + + if (!args->v0.enable) { + ior->func->hdmi->infoframe_avi(ior, args->v0.head, NULL, 0); + ior->func->hdmi->infoframe_vsi(ior, args->v0.head, NULL, 0); + ior->func->hdmi->ctrl(ior, args->v0.head, false, 0, 0); + return 0; } - nvkm_outp_release(outp, NVKM_OUTP_USER); + ior->func->hdmi->ctrl(ior, args->v0.head, args->v0.enable, + args->v0.max_ac_packet, args->v0.rekey); + if (ior->func->hdmi->scdc) + ior->func->hdmi->scdc(ior, args->v0.khz, args->v0.scdc, args->v0.scdc_scrambling, + args->v0.scdc_low_rates); + return 0; } static int -nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], - u8 link_nr, u8 link_bw, bool hda, bool mst) +nvkm_uoutp_mthd_lvds(struct nvkm_outp *outp, void *argv, u32 argc) { - int ret; + union nvif_outp_lvds_args *args = argv; - ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hda); - if (ret) - return ret; + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (outp->info.type != DCB_OUTPUT_LVDS) + return -EINVAL; - memcpy(outp->dp.dpcd, dpcd, sizeof(outp->dp.dpcd)); - outp->dp.lt.nr = link_nr; - outp->dp.lt.bw = link_bw; - outp->dp.lt.mst = mst; + outp->lvds.dual = !!args->v0.dual; + outp->lvds.bpc8 = !!args->v0.bpc8; return 0; } static int -nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp *outp, u8 head, u8 hdmi, u8 hdmi_max_ac_packet, - u8 hdmi_rekey, u8 hdmi_scdc, u8 hdmi_hda) +nvkm_uoutp_mthd_bl_set(struct nvkm_outp *outp, void *argv, u32 argc) { - struct nvkm_ior *ior; + union nvif_outp_bl_get_args *args = argv; int ret; - if (!(outp->asy.head = nvkm_head_find(outp->disp, head))) - return -EINVAL; + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; - ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hdmi && hdmi_hda); - if (ret) - return ret; + if (outp->func->bl.set) + ret = outp->func->bl.set(outp, args->v0.level); + else + ret = -EINVAL; - ior = outp->ior; + return ret; +} - if (hdmi) { - if (!ior->func->hdmi || - hdmi_max_ac_packet > 0x1f || hdmi_rekey > 0x7f || - (hdmi_scdc && !ior->func->hdmi->scdc)) { - nvkm_outp_release(outp, NVKM_OUTP_USER); - return -EINVAL; - } +static int +nvkm_uoutp_mthd_bl_get(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_bl_get_args *args = argv; + int ret; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; - ior->func->hdmi->ctrl(ior, head, hdmi, hdmi_max_ac_packet, hdmi_rekey); - if (ior->func->hdmi->scdc) - ior->func->hdmi->scdc(ior, hdmi_scdc); + if (outp->func->bl.get) { + ret = outp->func->bl.get(outp); + if (ret >= 0) { + args->v0.level = ret; + ret = 0; + } + } else { + ret = -EINVAL; } - return 0; + return ret; } static int -nvkm_uoutp_mthd_acquire_lvds(struct nvkm_outp *outp, bool dual, bool bpc8) +nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc) { - if (outp->info.type != DCB_OUTPUT_LVDS) - return -EINVAL; + union nvif_outp_release_args *args = argv; - outp->lvds.dual = dual; - outp->lvds.bpc8 = bpc8; + if (argc != sizeof(args->vn)) + return -ENOSYS; + if (!outp->ior) + return -EINVAL; - return nvkm_outp_acquire(outp, NVKM_OUTP_USER, false); + outp->func->release(outp); + return 0; } static int @@ -214,30 +349,16 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc) if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - if (outp->ior) + if (outp->ior && args->v0.type <= NVIF_OUTP_ACQUIRE_V0_PIOR) return -EBUSY; - switch (args->v0.proto) { - case NVIF_OUTP_ACQUIRE_V0_RGB_CRT: - ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, false); + switch (args->v0.type) { + case NVIF_OUTP_ACQUIRE_V0_DAC: + case NVIF_OUTP_ACQUIRE_V0_PIOR: + ret = outp->func->acquire(outp, false); break; - case NVIF_OUTP_ACQUIRE_V0_TMDS: - ret = nvkm_uoutp_mthd_acquire_tmds(outp, args->v0.tmds.head, - args->v0.tmds.hdmi, - args->v0.tmds.hdmi_max_ac_packet, - args->v0.tmds.hdmi_rekey, - args->v0.tmds.hdmi_scdc, - args->v0.tmds.hdmi_hda); - break; - case NVIF_OUTP_ACQUIRE_V0_LVDS: - ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8); - break; - case NVIF_OUTP_ACQUIRE_V0_DP: - ret = nvkm_uoutp_mthd_acquire_dp(outp, args->v0.dp.dpcd, - args->v0.dp.link_nr, - args->v0.dp.link_bw, - args->v0.dp.hda != 0, - args->v0.dp.mst != 0); + case NVIF_OUTP_ACQUIRE_V0_SOR: + ret = outp->func->acquire(outp, args->v0.sor.hda); break; default: ret = -EINVAL; @@ -253,6 +374,69 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc) } static int +nvkm_uoutp_mthd_inherit(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_inherit_args *args = argv; + struct nvkm_ior *ior; + int ret = 0; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + + /* Ensure an ior is hooked up to this outp already */ + ior = outp->func->inherit(outp); + if (!ior) + return -ENODEV; + + /* With iors, there will be a separate output path for each type of connector - and all of + * them will appear to be hooked up. Figure out which one is actually the one we're using + * based on the protocol we were given over nvif + */ + switch (args->v0.proto) { + case NVIF_OUTP_INHERIT_V0_TMDS: + if (ior->arm.proto != TMDS) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_DP: + if (ior->arm.proto != DP) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_LVDS: + if (ior->arm.proto != LVDS) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_TV: + if (ior->arm.proto != TV) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_RGB_CRT: + if (ior->arm.proto != CRT) + return -ENODEV; + break; + default: + ret = -EINVAL; + break; + } + + /* Make sure that userspace hasn't already acquired this */ + if (outp->acquired) { + OUTP_ERR(outp, "cannot inherit an already acquired (%02x) outp", outp->acquired); + return -EBUSY; + } + + /* Mark the outp acquired by userspace now that we've confirmed it's already active */ + OUTP_TRACE(outp, "inherit %02x |= %02x %p", outp->acquired, NVKM_OUTP_USER, ior); + nvkm_outp_acquire_ior(outp, NVKM_OUTP_USER, ior); + + args->v0.or = ior->id; + args->v0.link = ior->arm.link; + args->v0.head = ffs(ior->arm.head) - 1; + args->v0.proto = ior->arm.proto_evo; + + return ret; +} + +static int nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc) { union nvif_outp_load_detect_args *args = argv; @@ -261,7 +445,7 @@ nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc) if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV, false); + ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false); if (ret == 0) { if (outp->ior->func->sense) { ret = outp->ior->func->sense(outp->ior, args->v0.data); @@ -269,21 +453,64 @@ nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc) } else { ret = -EINVAL; } - nvkm_outp_release(outp, NVKM_OUTP_PRIV); + nvkm_outp_release_or(outp, NVKM_OUTP_PRIV); } return ret; } static int +nvkm_uoutp_mthd_edid_get(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_edid_get_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->edid_get) + return -EINVAL; + + args->v0.size = ARRAY_SIZE(args->v0.data); + return outp->func->edid_get(outp, args->v0.data, &args->v0.size); +} + +static int +nvkm_uoutp_mthd_detect(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_detect_args *args = argv; + int ret; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->detect) + return -EINVAL; + + ret = outp->func->detect(outp); + switch (ret) { + case 0: args->v0.status = NVIF_OUTP_DETECT_V0_NOT_PRESENT; break; + case 1: args->v0.status = NVIF_OUTP_DETECT_V0_PRESENT; break; + default: + args->v0.status = NVIF_OUTP_DETECT_V0_UNKNOWN; + break; + } + + return 0; +} + +static int nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc) { switch (mthd) { - case NVIF_OUTP_V0_RELEASE : return nvkm_uoutp_mthd_release (outp, argv, argc); - case NVIF_OUTP_V0_INFOFRAME : return nvkm_uoutp_mthd_infoframe (outp, argv, argc); - case NVIF_OUTP_V0_HDA_ELD : return nvkm_uoutp_mthd_hda_eld (outp, argv, argc); - case NVIF_OUTP_V0_DP_RETRAIN : return nvkm_uoutp_mthd_dp_retrain (outp, argv, argc); - case NVIF_OUTP_V0_DP_MST_VCPI: return nvkm_uoutp_mthd_dp_mst_vcpi(outp, argv, argc); + case NVIF_OUTP_V0_RELEASE : return nvkm_uoutp_mthd_release (outp, argv, argc); + case NVIF_OUTP_V0_LVDS : return nvkm_uoutp_mthd_lvds (outp, argv, argc); + case NVIF_OUTP_V0_HDMI : return nvkm_uoutp_mthd_hdmi (outp, argv, argc); + case NVIF_OUTP_V0_INFOFRAME : return nvkm_uoutp_mthd_infoframe (outp, argv, argc); + case NVIF_OUTP_V0_HDA_ELD : return nvkm_uoutp_mthd_hda_eld (outp, argv, argc); + case NVIF_OUTP_V0_DP_TRAIN : return nvkm_uoutp_mthd_dp_train (outp, argv, argc); + case NVIF_OUTP_V0_DP_DRIVE : return nvkm_uoutp_mthd_dp_drive (outp, argv, argc); + case NVIF_OUTP_V0_DP_SST : return nvkm_uoutp_mthd_dp_sst (outp, argv, argc); + case NVIF_OUTP_V0_DP_MST_ID_GET: return nvkm_uoutp_mthd_dp_mst_id_get(outp, argv, argc); + case NVIF_OUTP_V0_DP_MST_ID_PUT: return nvkm_uoutp_mthd_dp_mst_id_put(outp, argv, argc); + case NVIF_OUTP_V0_DP_MST_VCPI : return nvkm_uoutp_mthd_dp_mst_vcpi (outp, argv, argc); default: break; } @@ -292,17 +519,25 @@ nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc) } static int -nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc) +nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc, bool *invalid) { switch (mthd) { - case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc); + case NVIF_OUTP_V0_DETECT : return nvkm_uoutp_mthd_detect (outp, argv, argc); + case NVIF_OUTP_V0_EDID_GET : return nvkm_uoutp_mthd_edid_get (outp, argv, argc); + case NVIF_OUTP_V0_INHERIT : return nvkm_uoutp_mthd_inherit (outp, argv, argc); case NVIF_OUTP_V0_ACQUIRE : return nvkm_uoutp_mthd_acquire (outp, argv, argc); + case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc); + case NVIF_OUTP_V0_BL_GET : return nvkm_uoutp_mthd_bl_get (outp, argv, argc); + case NVIF_OUTP_V0_BL_SET : return nvkm_uoutp_mthd_bl_set (outp, argv, argc); case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc); + case NVIF_OUTP_V0_DP_AUX_XFER: return nvkm_uoutp_mthd_dp_aux_xfer(outp, argv, argc); + case NVIF_OUTP_V0_DP_RATES : return nvkm_uoutp_mthd_dp_rates (outp, argv, argc); default: break; } - return 1; + *invalid = true; + return 0; } static int @@ -310,12 +545,13 @@ nvkm_uoutp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) { struct nvkm_outp *outp = nvkm_uoutp(object); struct nvkm_disp *disp = outp->disp; + bool invalid = false; int ret; mutex_lock(&disp->super.mutex); - ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc); - if (ret <= 0) + ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc, &invalid); + if (!invalid) goto done; if (outp->ior) @@ -370,10 +606,60 @@ nvkm_uoutp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv ret = -EBUSY; spin_lock(&disp->client.lock); if (!outp->object.func) { + switch (outp->info.type) { + case DCB_OUTPUT_ANALOG: + args->v0.type = NVIF_OUTP_V0_TYPE_DAC; + args->v0.proto = NVIF_OUTP_V0_PROTO_RGB_CRT; + args->v0.rgb_crt.freq_max = outp->info.crtconf.maxfreq; + break; + case DCB_OUTPUT_TMDS: + if (!outp->info.location) { + args->v0.type = NVIF_OUTP_V0_TYPE_SOR; + args->v0.tmds.dual = (outp->info.tmdsconf.sor.link == 3); + } else { + args->v0.type = NVIF_OUTP_V0_TYPE_PIOR; + args->v0.tmds.dual = 0; + } + args->v0.proto = NVIF_OUTP_V0_PROTO_TMDS; + break; + case DCB_OUTPUT_LVDS: + args->v0.type = NVIF_OUTP_V0_TYPE_SOR; + args->v0.proto = NVIF_OUTP_V0_PROTO_LVDS; + args->v0.lvds.acpi_edid = outp->info.lvdsconf.use_acpi_for_edid; + break; + case DCB_OUTPUT_DP: + if (!outp->info.location) { + args->v0.type = NVIF_OUTP_V0_TYPE_SOR; + args->v0.dp.aux = outp->info.i2c_index; + } else { + args->v0.type = NVIF_OUTP_V0_TYPE_PIOR; + args->v0.dp.aux = NVKM_I2C_AUX_EXT(outp->info.extdev); + } + args->v0.proto = NVIF_OUTP_V0_PROTO_DP; + args->v0.dp.mst = outp->dp.mst; + args->v0.dp.increased_wm = outp->dp.increased_wm; + args->v0.dp.link_nr = outp->info.dpconf.link_nr; + args->v0.dp.link_bw = outp->info.dpconf.link_bw * 27000; + break; + default: + WARN_ON(1); + ret = -EINVAL; + goto done; + } + + if (outp->info.location) + args->v0.ddc = NVKM_I2C_BUS_EXT(outp->info.extdev); + else + args->v0.ddc = outp->info.i2c_index; + args->v0.heads = outp->info.heads; + args->v0.conn = outp->info.connector; + nvkm_object_ctor(&nvkm_uoutp, oclass, &outp->object); *pobject = &outp->object; ret = 0; } + +done: spin_unlock(&disp->client.lock); return ret; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c index 3648868bb9fc..c494a1ff2d57 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -2032,18 +2032,18 @@ gf100_gr_oneinit(struct nvkm_gr *base) } /* Allocate global context buffers. */ - ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, gr->func->grctx->pagepool_size, - 0x100, false, &gr->pagepool); + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST_SR_LOST, + gr->func->grctx->pagepool_size, 0x100, false, &gr->pagepool); if (ret) return ret; - ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, gr->func->grctx->bundle_size, + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST_SR_LOST, gr->func->grctx->bundle_size, 0x100, false, &gr->bundle_cb); if (ret) return ret; - ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, gr->func->grctx->attrib_cb_size(gr), - 0x1000, false, &gr->attrib_cb); + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST_SR_LOST, + gr->func->grctx->attrib_cb_size(gr), 0x1000, false, &gr->attrib_cb); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c index 8fe0444f761e..131db2645f84 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c @@ -462,7 +462,7 @@ nvkm_perfmon_mthd_query_domain(struct nvkm_perfmon *perfmon, args->v0.id = di; args->v0.signal_nr = nvkm_perfdom_count_perfsig(dom); - strncpy(args->v0.name, dom->name, sizeof(args->v0.name) - 1); + strscpy(args->v0.name, dom->name, sizeof(args->v0.name)); /* Currently only global counters (PCOUNTER) are implemented * but this will be different for local counters (MP). */ @@ -513,8 +513,7 @@ nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon, snprintf(args->v0.name, sizeof(args->v0.name), "/%s/%02x", dom->name, si); } else { - strncpy(args->v0.name, sig->name, - sizeof(args->v0.name) - 1); + strscpy(args->v0.name, sig->name, sizeof(args->v0.name)); } args->v0.signal = si; @@ -572,7 +571,7 @@ nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon, args->v0.source = sig->source[si]; args->v0.mask = src->mask; - strncpy(args->v0.name, src->name, sizeof(args->v0.name) - 1); + strscpy(args->v0.name, src->name, sizeof(args->v0.name)); } if (++si < source_nr) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c index 81a1ad2c88a7..40997ad1d101 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c @@ -83,17 +83,9 @@ tu102_devinit_wait(struct nvkm_device *device) } int -tu102_devinit_post(struct nvkm_devinit *base, bool post) +tu102_devinit_post(struct nvkm_devinit *init, bool post) { - struct nv50_devinit *init = nv50_devinit(base); - int ret; - - ret = tu102_devinit_wait(init->base.subdev.device); - if (ret) - return ret; - - gm200_devinit_preos(init, post); - return 0; + return tu102_devinit_wait(init->subdev.device); } static const struct nvkm_devinit_func diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c index e0e4f97be029..24886eabe8dc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c @@ -94,15 +94,21 @@ nvkm_instobj_wrap(struct nvkm_device *device, struct nvkm_memory *memory, struct nvkm_memory **pmemory) { struct nvkm_instmem *imem = device->imem; + int ret; if (!imem->func->memory_wrap) return -ENOSYS; - return imem->func->memory_wrap(imem, memory, pmemory); + ret = imem->func->memory_wrap(imem, memory, pmemory); + if (ret) + return ret; + + container_of(*pmemory, struct nvkm_instobj, memory)->preserve = true; + return 0; } int -nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, +nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, bool preserve, struct nvkm_memory **pmemory) { struct nvkm_subdev *subdev = &imem->subdev; @@ -130,6 +136,7 @@ nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, nvkm_done(memory); } + container_of(memory, struct nvkm_instobj, memory)->preserve = preserve; done: if (ret) nvkm_memory_unref(&memory); @@ -176,9 +183,11 @@ nvkm_instmem_fini(struct nvkm_subdev *subdev, bool suspend) if (suspend) { list_for_each_entry(iobj, &imem->list, head) { - int ret = nvkm_instobj_save(iobj); - if (ret) - return ret; + if (iobj->preserve) { + int ret = nvkm_instobj_save(iobj); + if (ret) + return ret; + } } nvkm_bar_bar2_fini(subdev->device); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h index fe92986a3885..390ca00ab567 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h @@ -25,6 +25,7 @@ void nvkm_instmem_boot(struct nvkm_instmem *); struct nvkm_instobj { struct nvkm_memory memory; struct list_head head; + bool preserve; u32 *suspend; }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c index f3630d0e0d55..bddac77f48f0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c @@ -558,7 +558,7 @@ gp100_vmm_invalidate_pdb(struct nvkm_vmm *vmm, u64 addr) void gp100_vmm_flush(struct nvkm_vmm *vmm, int depth) { - u32 type = (5 /* CACHE_LEVEL_UP_TO_PDE3 */ - depth) << 24; + u32 type = 0; if (atomic_read(&vmm->engref[NVKM_SUBDEV_BAR])) type |= 0x00000004; /* HUB_ONLY */ type |= 0x00000001; /* PAGE_ALL */ diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c index 6cb5eefa45e9..0095d58d4d9a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c @@ -27,7 +27,7 @@ static void tu102_vmm_flush(struct nvkm_vmm *vmm, int depth) { struct nvkm_device *device = vmm->mmu->subdev.device; - u32 type = (5 /* CACHE_LEVEL_UP_TO_PDE3 */ - depth) << 24; + u32 type = 0; type |= 0x00000001; /* PAGE_ALL */ if (atomic_read(&vmm->engref[NVKM_SUBDEV_BAR])) |