[PATCH v2 03/10] drm/nouveau/disp: route GSP-RM display MMIO through nvkm_disp_func hooks

From: Mohamed Ahmed

Date: Thu Aug 20 2026 - 13:24:45 EST


The GSP-RM display code in rm/r535/disp.c borrows a few
register-programming routines from engine/disp (the head-timing
interrupt handler, vblank enables, armed head state and scanout position
readback, the AVI/VSI infoframe writers and the GCP AVMute write) and so
far picked them by name, which means it has to know which chip it runs
on the moment a generation changes any of them.

Give nvkm_disp_func a .gsp table that each chip fills with exactly those
hooks, add tu102_gsp_disp (TU1xx) and ga102_gsp_disp (GA10x onwards)
carrying the current functions, hand them to r535_disp_new() instead of
the full hardware tables, and make rm/r535/disp.c call through the
hooks. r535_head becomes four forwarders, r535_sor_hdmi gets infoframe
forwarders, r535_sor_hdmi_audio() calls the GCP hook, and the interrupt
handler and its vector come from the table (intr_low_latency selects the
second DISP interrupt instance for chips that raise head timing on a
separate vector). The tables are per chip even though the two currently
coincide, so a generation that changes a hook only touches its own file.
rm/r535/disp.c no longer contains chip-specific register code, and a new
display generation only has to provide its own table. No functional
change.

Signed-off-by: Mohamed Ahmed <mohamedahmedegypt2001@xxxxxxxxx>
---
.../gpu/drm/nouveau/nvkm/engine/disp/ga102.c | 16 ++++-
.../gpu/drm/nouveau/nvkm/engine/disp/priv.h | 19 ++++++
.../gpu/drm/nouveau/nvkm/engine/disp/tu102.c | 16 ++++-
.../nouveau/nvkm/subdev/gsp/rm/r535/disp.c | 60 ++++++++++++++++---
4 files changed, 100 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
index ab0a85c92430..b48ed7146396 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
@@ -144,12 +144,26 @@ ga102_disp = {
},
};

+static const struct nvkm_disp_func
+ga102_gsp_disp = {
+ .uevent = &gv100_disp_chan_uevent,
+ .ramht_size = 0x2000,
+ .gsp.intr = tu102_disp_intr,
+ .gsp.head_state = gv100_head_state,
+ .gsp.head_rgpos = gv100_head_rgpos,
+ .gsp.vblank_get = tu102_head_vblank_get,
+ .gsp.vblank_put = tu102_head_vblank_put,
+ .gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
+ .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+ .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+};
+
int
ga102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_disp **pdisp)
{
if (nvkm_gsp_rm(device->gsp))
- return r535_disp_new(&ga102_disp, device, type, inst, pdisp);
+ return r535_disp_new(&ga102_gsp_disp, device, type, inst, pdisp);

return nvkm_disp_new_(&ga102_disp, device, type, inst, pdisp);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
index 722ec340e12a..3cb903741fb8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
@@ -5,6 +5,8 @@
#include <engine/disp.h>
#include <core/enum.h>
struct nvkm_head;
+struct nvkm_head_state;
+struct nvkm_ior;
struct nvkm_outp;
struct dcb_output;

@@ -34,6 +36,23 @@ struct nvkm_disp_func {
int (*new)(struct nvkm_disp *, int id);
} wndw, head, dac, sor, pior;

+ /* Register programming that the GSP-RM display path (rm/r535) needs from
+ * the chip, everything else on that path goes through RM. Every hook
+ * is called unconditionally.
+ */
+ struct {
+ irqreturn_t (*intr)(struct nvkm_inth *);
+ /* Head-timing interrupts arrive on a second DISP vector. */
+ bool intr_low_latency;
+ void (*head_state)(struct nvkm_head *, struct nvkm_head_state *);
+ void (*head_rgpos)(struct nvkm_head *, u16 *hline, u16 *vline);
+ void (*vblank_get)(struct nvkm_head *);
+ void (*vblank_put)(struct nvkm_head *);
+ void (*hdmi_gcp)(struct nvkm_ior *, int head, bool enable);
+ void (*hdmi_infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size);
+ void (*hdmi_infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size);
+ } gsp;
+
u16 ramht_size;

struct nvkm_sclass root;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
index 6cfd52c9056f..9db3cac487e3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
@@ -295,12 +295,26 @@ tu102_disp = {
},
};

+static const struct nvkm_disp_func
+tu102_gsp_disp = {
+ .uevent = &gv100_disp_chan_uevent,
+ .ramht_size = 0x2000,
+ .gsp.intr = tu102_disp_intr,
+ .gsp.head_state = gv100_head_state,
+ .gsp.head_rgpos = gv100_head_rgpos,
+ .gsp.vblank_get = tu102_head_vblank_get,
+ .gsp.vblank_put = tu102_head_vblank_put,
+ .gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
+ .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+ .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+};
+
int
tu102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_disp **pdisp)
{
if (nvkm_gsp_rm(device->gsp))
- return r535_disp_new(&tu102_disp, device, type, inst, pdisp);
+ return r535_disp_new(&tu102_gsp_disp, device, type, inst, pdisp);

return nvkm_disp_new_(&tu102_disp, device, type, inst, pdisp);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
index cd4451e62512..f3e55253bcbc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
@@ -547,7 +547,19 @@ r535_sor_hdmi_audio(struct nvkm_ior *sor, int head, bool enable)
{
r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable);
r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable);
- tu102_sor_hdmi_gcp(sor, head, enable);
+ sor->disp->func->gsp.hdmi_gcp(sor, head, enable);
+}
+
+static void
+r535_sor_hdmi_infoframe_avi(struct nvkm_ior *sor, int head, void *data, u32 size)
+{
+ sor->disp->func->gsp.hdmi_infoframe_avi(sor, head, data, size);
+}
+
+static void
+r535_sor_hdmi_infoframe_vsi(struct nvkm_ior *sor, int head, void *data, u32 size)
+{
+ sor->disp->func->gsp.hdmi_infoframe_vsi(sor, head, data, size);
}

static void
@@ -575,8 +587,8 @@ r535_sor_hdmi = {
.ctrl = r535_sor_hdmi_ctrl,
.scdc = r535_sor_hdmi_scdc,
/*TODO: SF_USER -> KMS. */
- .infoframe_avi = gv100_sor_hdmi_infoframe_avi,
- .infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+ .infoframe_avi = r535_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = r535_sor_hdmi_infoframe_vsi,
.audio = r535_sor_hdmi_audio,
};

@@ -601,12 +613,36 @@ r535_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask)
return 4;
}

+static void
+r535_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
+{
+ head->disp->func->gsp.head_state(head, state);
+}
+
+static void
+r535_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
+{
+ head->disp->func->gsp.head_rgpos(head, hline, vline);
+}
+
+static void
+r535_head_vblank_get(struct nvkm_head *head)
+{
+ head->disp->func->gsp.vblank_get(head);
+}
+
+static void
+r535_head_vblank_put(struct nvkm_head *head)
+{
+ head->disp->func->gsp.vblank_put(head);
+}
+
static const struct nvkm_head_func
r535_head = {
- .state = gv100_head_state,
- .rgpos = gv100_head_rgpos,
- .vblank_get = tu102_head_vblank_get,
- .vblank_put = tu102_head_vblank_put,
+ .state = r535_head_state,
+ .rgpos = r535_head_rgpos,
+ .vblank_get = r535_head_vblank_get,
+ .vblank_put = r535_head_vblank_put,
};

static struct nvkm_conn *
@@ -1650,12 +1686,17 @@ r535_disp_oneinit(struct nvkm_disp *disp)
if (ret)
return ret;

- ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, disp->engine.subdev.inst);
+ /* Chips that raise head-timing interrupts on a separate low-latency
+ * vector report it as a second DISP interrupt table entry, exposed
+ * as instance 1 by the RM engine-index translation.
+ */
+ ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type,
+ disp->func->gsp.intr_low_latency ? 1 : disp->engine.subdev.inst);
if (ret < 0)
return ret;

ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &disp->engine.subdev,
- tu102_disp_intr, &disp->engine.subdev.inth);
+ disp->func->gsp.intr, &disp->engine.subdev.inth);
if (ret)
return ret;

@@ -1688,6 +1729,7 @@ r535_disp_new(const struct nvkm_disp_func *hw, struct nvkm_device *device,
rm->uevent = hw->uevent;
rm->sor.cnt = r535_sor_cnt;
rm->sor.new = r535_sor_new;
+ rm->gsp = hw->gsp;
rm->ramht_size = hw->ramht_size;

rm->root.oclass = gpu->disp.class.root;
--
2.55.0