All of 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  0:52 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.