summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-11-01 06:28:35 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2023-11-01 06:28:35 -1000
commit7d461b291e65938f15f56fe58da2303b07578a76 (patch)
tree015dd7c2f1743dd70be52787dd9aff33822bc938 /drivers/gpu/drm/nouveau
parent8bc9e6515183935fa0cccaf67455c439afe4982b (diff)
parent631808095a82e6b6f8410a95f8b12b8d0d38b161 (diff)
Merge tag 'drm-next-2023-10-31-1' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie: "Highlights: - AMD adds some more upcoming HW platforms - Intel made Meteorlake stable and started adding Lunarlake - nouveau has a bunch of display rework in prepartion for the NVIDIA GSP firmware support - msm adds a7xx support - habanalabs has finished migration to accel subsystem Detail summary: kernel: - add initial vmemdup-user-array core: - fix platform remove() to return void - drm_file owner updated to reflect owner - move size calcs to drm buddy allocator - let GPUVM build as a module - allow variable number of run-queues in scheduler edid: - handle bad h/v sync_end in EDIDs panfrost: - add Boris as maintainer fbdev: - use fb_ops helpers more - only allow logo use from fbcon - rename fb_pgproto to pgprot_framebuffer - add HPD state to drm_connector_oob_hotplug_event - convert to fbdev i/o mem helpers i915: - Enable meteorlake by default - Early Xe2 LPD/Lunarlake display enablement - Rework subplatforms into IP version checks - GuC based TLB invalidation for Meteorlake - Display rework for future Xe driver integration - LNL FBC features - LNL display feature capability reads - update recommended fw versions for DG2+ - drop fastboot module parameter - added deviceid for Arrowlake-S - drop preproduction workarounds - don't disable preemption for resets - cleanup inlines in headers - PXP firmware loading fix - Fix sg list lengths - DSC PPS state readout/verification - Add more RPL P/U PCI IDs - Add new DG2-G12 stepping - DP enhanced framing support to state checker - Improve shared link bandwidth management - stop using GEM macros in display code - refactor related code into display code - locally enable W=1 warnings - remove PSR watchdog timers on LNL amdgpu: - RAS/FRU EEPROM updatse - IP discovery updatses - GC 11.5 support - DCN 3.5 support - VPE 6.1 support - NBIO 7.11 support - DML2 support - lots of IP updates - use flexible arrays for bo list handling - W=1 fixes - Enable seamless boot in more cases - Enable context type property for HDMI - Rework GPUVM TLB flushing - VCN IB start/size alignment fixes amdkfd: - GC 10/11 fixes - GC 11.5 support - use partial migration in GPU faults radeon: - W=1 Fixes - fix some possible buffer overflow/NULL derefs nouveau: - update uapi for NO_PREFETCH - scheduler/fence fixes - rework suspend/resume for GSP-RM - rework display in preparation for GSP-RM habanalabs: - uapi: expose tsc clock - uapi: block access to eventfd through control device - uapi: force dma-buf export to PAGE_SIZE alignments - complete move to accel subsystem - move firmware interface include files - perform hard reset on PCIe AXI drain event - optimise user interrupt handling msm: - DP: use existing helpers for DPCD - DPU: interrupts reworked - gpu: a7xx (a730/a740) support - decouple msm_drv from kms for headless devices mediatek: - MT8188 dsi/dp/edp support - DDP GAMMA - 12 bit LUT support - connector dynamic selection capability rockchip: - rv1126 mipi-dsi/vop support - add planar formats ast: - rename constants panels: - Mitsubishi AA084XE01 - JDI LPM102A188A - LTK050H3148W-CTA6 ivpu: - power management fixes qaic: - add detach slice bo api komeda: - add NV12 writeback tegra: - support NVSYNC/NHSYNC - host1x suspend fixes ili9882t: - separate into own driver" * tag 'drm-next-2023-10-31-1' of git://anongit.freedesktop.org/drm/drm: (1803 commits) drm/amdgpu: Remove unused variables from amdgpu_show_fdinfo drm/amdgpu: Remove duplicate fdinfo fields drm/amd/amdgpu: avoid to disable gfxhub interrupt when driver is unloaded drm/amdgpu: Add EXT_COHERENT support for APU and NUMA systems drm/amdgpu: Retrieve CE count from ce_count_lo_chip in EccInfo table drm/amdgpu: Identify data parity error corrected in replay mode drm/amdgpu: Fix typo in IP discovery parsing drm/amd/display: fix S/G display enablement drm/amdxcp: fix amdxcp unloads incompletely drm/amd/amdgpu: fix the GPU power print error in pm info drm/amdgpu: Use pcie domain of xcc acpi objects drm/amd: check num of link levels when update pcie param drm/amdgpu: Add a read to GFX v9.4.3 ring test drm/amd/pm: call smu_cmn_get_smc_version in is_mode1_reset_supported. drm/amdgpu: get RAS poison status from DF v4_6_2 drm/amdgpu: Use discovery table's subrevision drm/amd/display: 3.2.256 drm/amd/display: add interface to query SubVP status drm/amd/display: Read before writing Backlight Mode Set Register drm/amd/display: Disable SYMCLK32_SE RCO on DCN314 ...
Diffstat (limited to 'drivers/gpu/drm/nouveau')
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv04/disp.c2
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c536
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.h6
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/head.h1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/headc57d.c14
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/conn.h20
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0011.h21
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/if0012.h249
-rw-r--r--drivers/gpu/drm/nouveau/include/nvif/outp.h96
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/memory.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_backlight.c90
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c251
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_debugfs.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c345
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h30
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_exec.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sched.c1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_uvmm.c42
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_uvmm.h8
-rw-r--r--drivers/gpu/drm/nouveau/nvif/client.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvif/conn.c36
-rw-r--r--drivers/gpu/drm/nouveau/nvif/disp.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvif/outp.c412
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/firmware.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/memory.c15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c146
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c362
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c38
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h15
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c182
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c144
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h43
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c67
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c460
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c7
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c12
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c19
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c2
61 files changed, 2513 insertions, 1256 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 4e7c9c353c51..a0ac8c258d9f 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)
@@ -882,21 +886,33 @@ struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder)
static void
nv50_msto_cleanup(struct drm_atomic_state *state,
- struct drm_dp_mst_topology_state *mst_state,
+ struct drm_dp_mst_topology_state *new_mst_state,
struct drm_dp_mst_topology_mgr *mgr,
struct nv50_msto *msto)
{
struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev);
- struct drm_dp_mst_atomic_payload *payload =
- drm_atomic_get_mst_payload_state(mst_state, msto->mstc->port);
+ struct drm_dp_mst_atomic_payload *new_payload =
+ drm_atomic_get_mst_payload_state(new_mst_state, msto->mstc->port);
+ struct drm_dp_mst_topology_state *old_mst_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);
} else if (msto->enabled) {
- drm_dp_add_payload_part2(mgr, state, payload);
+ drm_dp_add_payload_part2(mgr, state, new_payload);
msto->enabled = false;
}
}
@@ -910,28 +926,28 @@ nv50_msto_prepare(struct drm_atomic_state *state,
struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev);
struct nv50_mstc *mstc = msto->mstc;
struct nv50_mstm *mstm = mstc->mstm;
- struct drm_dp_mst_topology_state *old_mst_state;
- struct drm_dp_mst_atomic_payload *payload, *old_payload;
+ struct drm_dp_mst_atomic_payload *payload;
+ int ret = 0;
NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name);
- old_mst_state = drm_atomic_get_old_mst_topology_state(state, mgr);
-
payload = drm_atomic_get_mst_payload_state(mst_state, mstc->port);
- old_payload = drm_atomic_get_mst_payload_state(old_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(mgr, mst_state, old_payload, payload);
-
+ 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);
}
}
@@ -1028,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)
@@ -1052,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)
@@ -1290,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;
}
@@ -1324,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 *
@@ -1535,17 +1559,14 @@ 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 nouveau_connector *nv_connector = nv50_outp_get_old_connector(state, nv_encoder);
+ struct nv50_head *head = nv50_head(nv_encoder->crtc);
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
+ struct nouveau_connector *nv_connector = nv50_outp_get_old_connector(state, nv_encoder);
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
struct nouveau_backlight *backlight = nv_connector->backlight;
-#endif
struct drm_dp_aux *aux = &nv_connector->aux;
int ret;
- u8 pwr;
-#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
if (backlight && backlight->uses_dpcd) {
ret = drm_edp_backlight_disable(aux, &backlight->edp_info);
if (ret < 0)
@@ -1554,19 +1575,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;
}
@@ -1579,6 +1601,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);
@@ -1596,15 +1619,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) {
@@ -1650,10 +1675,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)
@@ -1661,8 +1686,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)
@@ -1676,6 +1699,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);
}
@@ -1691,14 +1717,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);
}
@@ -1707,24 +1732,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;
@@ -1737,15 +1753,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);
@@ -1756,40 +1766,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;
}
/******************************************************************************
@@ -1816,7 +1826,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
@@ -1844,14 +1853,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();
@@ -1888,8 +1899,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);
@@ -1897,18 +1909,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;
@@ -1916,18 +1928,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);
@@ -1937,7 +1942,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;
}
/******************************************************************************
@@ -1951,7 +1956,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;
@@ -1974,6 +1981,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
@@ -2065,14 +2089,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;
- }
}
}
@@ -2092,7 +2110,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;
@@ -2104,11 +2122,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). */
@@ -2206,6 +2222,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);
@@ -2354,10 +2375,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;
}
@@ -2518,6 +2538,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)
{
@@ -2535,6 +2653,9 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime)
}
}
+ if (!resume)
+ nv50_display_read_hw_state(nouveau_drm(dev));
+
return 0;
}
@@ -2561,14 +2682,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)
@@ -2644,20 +2762,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;
+
+ 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;
- if (!(crtcs & (1 << i)))
+ 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)) {
@@ -2683,50 +2873,8 @@ 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);
+ head->msto->encoder.possible_crtcs = disp->disp->head_mask;
}
-
- 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;
- }
- }
-
- /* 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+ */
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 c1837ba95fb5..9a5ef574744b 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_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c
index 3b7ea5221226..7c376c4ccdcf 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sched.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sched.c
@@ -436,6 +436,7 @@ int nouveau_sched_init(struct nouveau_drm *drm)
return -ENOMEM;
return drm_sched_init(sched, &nouveau_sched_ops,
+ DRM_SCHED_PRIORITY_COUNT,
NOUVEAU_SCHED_HW_SUBMISSIONS, 0, job_hang_limit,
NULL, NULL, "nouveau_sched", drm->dev->dev);
}
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(&reg);
@@ -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(&lt, 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 = &gt215_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 = &gt215_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 = &gt215_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 = &gt215_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 = &gt215_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 = &gt215_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 = &gt215_sor_bl,
.hdmi = &gt215_sor_hdmi,
.dp = &gt215_sor_dp,
.hda = &gt215_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 = &gt215_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 = &gt215_sor_bl,
.hdmi = &gt215_sor_hdmi,
.dp = &mcp89_sor_dp,
.hda = &gt215_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 = &gt215_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 3249e5c1c893..104f6ee9ae6d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c
@@ -121,46 +121,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)
{
@@ -176,7 +136,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,
};
@@ -204,6 +163,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/engine/pm/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h
index 6ae25d3e7f45..c011227f7052 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/priv.h
@@ -82,7 +82,7 @@ struct nvkm_perfdom {
u8 mode;
u32 clk;
u16 signal_nr;
- struct nvkm_perfsig signal[];
+ struct nvkm_perfsig signal[] __counted_by(signal_nr);
};
struct nvkm_funcdom {
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]))