Nouveau Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Dave Airlie <airlied@gmail.com>
To: dri-devel@lists.freedesktop.org
Cc: nouveau@lists.freedesktop.org
Subject: [PATCH 1/4] nouveau: move HDMI sink caps to outp level for GSP-RM
Date: Thu, 23 Apr 2026 10:42:13 +1000	[thread overview]
Message-ID: <20260423004552.3289884-2-airlied@gmail.com> (raw)
In-Reply-To: <20260423004552.3289884-1-airlied@gmail.com>

From: Dave Airlie <airlied@redhat.com>

NVIDIA's driver sends SET_HDMI_SINK_CAPS during connector detection
(ApplyNewEdid), not during modeset. Move this RM call from the IOR-level
scdc callback to a new outp-level hdmi_sink_caps method that doesn't
require an acquired SOR.

Add nvif_outp_hdmi_sink_caps() routed through the noacquire dispatch
table and call it from nouveau_connector_detect() for TMDS outputs on
Turing+. Remove r535_sor_hdmi_scdc and its .scdc entry from r535_sor_hdmi
since GSP now gets sink caps at detect time. The gm200 hardware scdc
callback is preserved for direct register writes on pre-GSP chips.

This was claude code assisted.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/nouveau/include/nvif/if0012.h | 11 ++++
 drivers/gpu/drm/nouveau/include/nvif/outp.h   |  2 +
 drivers/gpu/drm/nouveau/nouveau_connector.c   | 11 ++++
 drivers/gpu/drm/nouveau/nvif/outp.c           | 19 +++++++
 .../gpu/drm/nouveau/nvkm/engine/disp/outp.h   |  2 +
 .../gpu/drm/nouveau/nvkm/engine/disp/uoutp.c  | 23 +++++++--
 .../nouveau/nvkm/subdev/gsp/rm/r535/disp.c    | 51 +++++++++----------
 7 files changed, 89 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0012.h b/drivers/gpu/drm/nouveau/include/nvif/if0012.h
index bde9bfae8d11f..89cfac7ef3a54 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/if0012.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/if0012.h
@@ -43,6 +43,7 @@ union nvif_outp_args {
 
 #define NVIF_OUTP_V0_DETECT        0x00
 #define NVIF_OUTP_V0_EDID_GET      0x01
+#define NVIF_OUTP_V0_HDMI_SINK_CAPS 0x02
 
 #define NVIF_OUTP_V0_INHERIT       0x10
 #define NVIF_OUTP_V0_ACQUIRE       0x11
@@ -165,6 +166,16 @@ union nvif_outp_lvds_args {
 	} v0;
 };
 
+union nvif_outp_hdmi_sink_caps_args {
+	struct nvif_outp_hdmi_sink_caps_v0 {
+		__u8 version;
+		__u8 scdc;
+		__u8 scdc_scrambling;
+		__u8 scdc_low_rates;
+		__u8 pad04[4];
+	} v0;
+};
+
 union nvif_outp_hdmi_args {
 	struct nvif_outp_hdmi_v0 {
 		__u8 version;
diff --git a/drivers/gpu/drm/nouveau/include/nvif/outp.h b/drivers/gpu/drm/nouveau/include/nvif/outp.h
index bc122a5ba7df7..240e1f80ed539 100644
--- a/drivers/gpu/drm/nouveau/include/nvif/outp.h
+++ b/drivers/gpu/drm/nouveau/include/nvif/outp.h
@@ -66,6 +66,8 @@ enum nvif_outp_detect_status {
 
 enum nvif_outp_detect_status nvif_outp_detect(struct nvif_outp *);
 int nvif_outp_edid_get(struct nvif_outp *, u8 **pedid);
+int nvif_outp_hdmi_sink_caps(struct nvif_outp *, bool scdc, bool scdc_scrambling,
+			     bool scdc_low_rates);
 
 int nvif_outp_load_detect(struct nvif_outp *, u32 loadval);
 int nvif_outp_acquire_dac(struct nvif_outp *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index cc239492c7f0b..eae37d938f3bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -48,6 +48,7 @@
 
 #include <nvif/class.h>
 #include <nvif/if0011.h>
+#include <nvif/outp.h>
 
 struct drm_display_mode *
 nouveau_conn_native_mode(struct drm_connector *connector)
@@ -637,6 +638,16 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 		nouveau_connector_set_encoder(connector, nv_encoder);
 		conn_status = connector_status_connected;
 
+		if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS &&
+		    drm->client.device.info.family >= NV_DEVICE_INFO_V0_TURING) {
+			struct drm_hdmi_info *hdmi = &connector->display_info.hdmi;
+
+			nvif_outp_hdmi_sink_caps(&nv_encoder->outp,
+						hdmi->scdc.supported,
+						hdmi->scdc.scrambling.supported,
+						hdmi->scdc.scrambling.low_rates);
+		}
+
 		if (nv_encoder->dcb->type == DCB_OUTPUT_DP)
 			drm_dp_cec_set_edid(&nv_connector->aux, nv_connector->edid);
 
diff --git a/drivers/gpu/drm/nouveau/nvif/outp.c b/drivers/gpu/drm/nouveau/nvif/outp.c
index 8cf4775a0a1eb..d66f437587ff3 100644
--- a/drivers/gpu/drm/nouveau/nvif/outp.c
+++ b/drivers/gpu/drm/nouveau/nvif/outp.c
@@ -461,6 +461,25 @@ nvif_outp_edid_get(struct nvif_outp *outp, u8 **pedid)
 	return ret;
 }
 
+int
+nvif_outp_hdmi_sink_caps(struct nvif_outp *outp, bool scdc, bool scdc_scrambling,
+			 bool scdc_low_rates)
+{
+	struct nvif_outp_hdmi_sink_caps_v0 args;
+	int ret;
+
+	args.version = 0;
+	args.scdc = scdc;
+	args.scdc_scrambling = scdc_scrambling;
+	args.scdc_low_rates = scdc_low_rates;
+
+	ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_HDMI_SINK_CAPS, &args, sizeof(args));
+	NVIF_ERRON(ret, &outp->object,
+		   "[HDMI_SINK_CAPS scdc:%d scrambling:%d low_rates:%d]",
+		   args.scdc, args.scdc_scrambling, args.scdc_low_rates);
+	return ret;
+}
+
 enum nvif_outp_detect_status
 nvif_outp_detect(struct nvif_outp *outp)
 {
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
index ebd2f499b4b1d..fd54c17ba4d6d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
@@ -95,6 +95,8 @@ struct nvkm_outp_func {
 
 	int (*detect)(struct nvkm_outp *);
 	int (*edid_get)(struct nvkm_outp *, u8 *data, u16 *size);
+	void (*hdmi_sink_caps)(struct nvkm_outp *, bool scdc, bool scrambling,
+			       bool low_rates);
 
 	struct nvkm_ior *(*inherit)(struct nvkm_outp *);
 	int (*acquire)(struct nvkm_outp *, bool hda);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
index 377d0e0cef848..84381ba71ebaf 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c
@@ -253,8 +253,7 @@ nvkm_uoutp_mthd_hdmi(struct nvkm_outp *outp, void *argv, u32 argc)
 
 	if (!ior->func->hdmi ||
 	    args->v0.max_ac_packet > 0x1f ||
-	    args->v0.rekey > 0x7f ||
-	    (args->v0.scdc && !ior->func->hdmi->scdc))
+	    args->v0.rekey > 0x7f)
 		return -EINVAL;
 
 	if (!args->v0.enable) {
@@ -473,6 +472,21 @@ nvkm_uoutp_mthd_edid_get(struct nvkm_outp *outp, void *argv, u32 argc)
 	return outp->func->edid_get(outp, args->v0.data, &args->v0.size);
 }
 
+static int
+nvkm_uoutp_mthd_hdmi_sink_caps(struct nvkm_outp *outp, void *argv, u32 argc)
+{
+	union nvif_outp_hdmi_sink_caps_args *args = argv;
+
+	if (argc != sizeof(args->v0) || args->v0.version != 0)
+		return -ENOSYS;
+	if (!outp->func->hdmi_sink_caps)
+		return -EINVAL;
+
+	outp->func->hdmi_sink_caps(outp, args->v0.scdc, args->v0.scdc_scrambling,
+				   args->v0.scdc_low_rates);
+	return 0;
+}
+
 static int
 nvkm_uoutp_mthd_detect(struct nvkm_outp *outp, void *argv, u32 argc)
 {
@@ -522,8 +536,9 @@ static int
 nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc, bool *invalid)
 {
 	switch (mthd) {
-	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_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_HDMI_SINK_CAPS : return nvkm_uoutp_mthd_hdmi_sink_caps (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);
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 6e63df816d855..fa20a0c633653 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
@@ -468,31 +468,6 @@ r535_sor_dp = {
 	.audio = r535_sor_dp_audio,
 };
 
-static void
-r535_sor_hdmi_scdc(struct nvkm_ior *sor, u32 khz, bool support, bool scrambling,
-		   bool scrambling_low_rates)
-{
-	struct nvkm_outp *outp = sor->asy.outp;
-	struct nvkm_disp *disp = outp->disp;
-	NV0073_CTRL_SPECIFIC_SET_HDMI_SINK_CAPS_PARAMS *ctrl;
-
-	ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
-				    NV0073_CTRL_CMD_SPECIFIC_SET_HDMI_SINK_CAPS, sizeof(*ctrl));
-	if (WARN_ON(IS_ERR(ctrl)))
-		return;
-
-	ctrl->displayId = BIT(outp->index);
-	ctrl->caps = 0;
-	if (support)
-		ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, SCDC_SUPPORTED, TRUE);
-	if (scrambling)
-		ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, GT_340MHZ_CLOCK_SUPPORTED, TRUE);
-	if (scrambling_low_rates)
-		ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, LTE_340MHZ_SCRAMBLING_SUPPORTED, TRUE);
-
-	WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
-}
-
 static void
 r535_sor_hdmi_ctrl_audio_mute(struct nvkm_outp *outp, bool mute)
 {
@@ -580,7 +555,6 @@ r535_sor_hdmi_ctrl(struct nvkm_ior *sor, int head, bool enable, u8 max_ac_packet
 static const struct nvkm_ior_func_hdmi
 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,
@@ -1242,6 +1216,30 @@ r535_tmds_edid_get(struct nvkm_outp *outp, u8 *data, u16 *psize)
 	return ret;
 }
 
+static void
+r535_outp_hdmi_sink_caps(struct nvkm_outp *outp, bool scdc, bool scrambling,
+			 bool low_rates)
+{
+	struct nvkm_disp *disp = outp->disp;
+	NV0073_CTRL_SPECIFIC_SET_HDMI_SINK_CAPS_PARAMS *ctrl;
+
+	ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
+				    NV0073_CTRL_CMD_SPECIFIC_SET_HDMI_SINK_CAPS, sizeof(*ctrl));
+	if (WARN_ON(IS_ERR(ctrl)))
+		return;
+
+	ctrl->displayId = BIT(outp->index);
+	ctrl->caps = 0;
+	if (scdc)
+		ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, SCDC_SUPPORTED, TRUE);
+	if (scrambling)
+		ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, GT_340MHZ_CLOCK_SUPPORTED, TRUE);
+	if (low_rates)
+		ctrl->caps |= NVDEF(NV0073_CTRL_CMD_SPECIFIC, SET_HDMI_SINK_CAPS, LTE_340MHZ_SCRAMBLING_SUPPORTED, TRUE);
+
+	WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
+}
+
 static const struct nvkm_outp_func
 r535_tmds = {
 	.detect = r535_outp_detect,
@@ -1249,6 +1247,7 @@ r535_tmds = {
 	.acquire = r535_outp_acquire,
 	.release = r535_outp_release,
 	.edid_get = r535_tmds_edid_get,
+	.hdmi_sink_caps = r535_outp_hdmi_sink_caps,
 };
 
 static int
-- 
2.53.0


  reply	other threads:[~2026-04-23  2:55 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-23  0:42 nouveau: add HDMI FRL support for GSP enabled GPUs Dave Airlie
2026-04-23  0:42 ` Dave Airlie [this message]
2026-04-23  0:42 ` [PATCH 2/4] nouveau: add FRL and DSC parameters to HDMI sink caps interface Dave Airlie
2026-04-23  0:42 ` [PATCH 3/4] nouveau: add HDMI FRL training API and retrain event handling Dave Airlie
2026-04-23  0:42 ` [PATCH 4/4] nouveau: wire up HDMI FRL in the display frontend Dave Airlie

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260423004552.3289884-2-airlied@gmail.com \
    --to=airlied@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=nouveau@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox