From: Daniel Campos Ramos <Capitain_Jack@yahoo.com>
To: dri-devel@lists.freedesktop.org
Cc: nouveau@lists.freedesktop.org, lyude@redhat.com, dakr@kernel.org,
Daniel Campos Ramos <Capitain_Jack@yahoo.com>
Subject: [PATCH 1/3] drm/nouveau: select HDMI deep-color link depth
Date: Tue, 22 Sep 2026 16:21:30 -0300 [thread overview]
Message-ID: <20260922192132.114546-2-Capitain_Jack@yahoo.com> (raw)
In-Reply-To: <20260922192132.114546-1-Capitain_Jack@yahoo.com>
Nouveau currently initializes the output depth from the EDID basic display
parameters and does not expose a max bpc property on HDMI connectors. As a
result, HDMI deep-color modes advertised by the VSDB are never selected.
Attach an 8..16 max bpc property. Select 10, 12 or 16 bpc only when
the sink advertises the corresponding RGB 4:4:4 deep-color mode and the
resulting TMDS character rate fits the existing link limit. Program the
matching head output-resource depth on NVDisplay.
Share the existing TMDS bandwidth helper with the atomic check so the depth
selection observes both sink and GPU limits.
Signed-off-by: Daniel Campos Ramos <Capitain_Jack@yahoo.com>
---
drivers/gpu/drm/nouveau/dispnv50/disp.c | 57 ++++++++++++++++++++-
drivers/gpu/drm/nouveau/dispnv50/headc37d.c | 6 +++
drivers/gpu/drm/nouveau/dispnv50/headc57d.c | 6 +++
drivers/gpu/drm/nouveau/dispnv50/headca7d.c | 6 +++
drivers/gpu/drm/nouveau/nouveau_connector.c | 9 ++--
drivers/gpu/drm/nouveau/nouveau_connector.h | 1 +
6 files changed, 80 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index add19d47..6410b96b 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -375,10 +375,12 @@ nv50_outp_atomic_check_view(struct drm_encoder *encoder,
}
static void
-nv50_outp_atomic_fix_depth(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state)
+nv50_outp_atomic_fix_depth(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
{
struct nv50_head_atom *asyh = nv50_head_atom(crtc_state);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_connector *connector = conn_state->connector;
struct drm_display_mode *mode = &asyh->state.adjusted_mode;
unsigned int max_rate, mode_rate;
@@ -398,6 +400,36 @@ nv50_outp_atomic_fix_depth(struct drm_encoder *encoder, struct drm_crtc_state *c
asyh->or.bpc -= 2;
}
break;
+ case DCB_OUTPUT_TMDS:
+ if (!connector->display_info.is_hdmi)
+ break;
+
+ max_rate = nouveau_connector_get_tmds_link_bandwidth(connector);
+ asyh->or.bpc = 8;
+
+ if (conn_state->max_requested_bpc >= 16 &&
+ connector->display_info.edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_48) {
+ mode_rate = DIV_ROUND_UP(mode->clock * 16, 8);
+ if (mode_rate <= max_rate)
+ asyh->or.bpc = 16;
+ }
+
+ if (asyh->or.bpc == 8 && conn_state->max_requested_bpc >= 12 &&
+ connector->display_info.edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36) {
+ mode_rate = DIV_ROUND_UP(mode->clock * 12, 8);
+ if (mode_rate <= max_rate)
+ asyh->or.bpc = 12;
+ }
+
+ if (asyh->or.bpc == 8 && conn_state->max_requested_bpc >= 10 &&
+ connector->display_info.edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) {
+ mode_rate = DIV_ROUND_UP(mode->clock * 10, 8);
+ if (mode_rate <= max_rate)
+ asyh->or.bpc = 10;
+ }
+
+ conn_state->max_bpc = asyh->or.bpc;
+ break;
default:
break;
}
@@ -422,7 +454,7 @@ nv50_outp_atomic_check(struct drm_encoder *encoder,
asyh->or.bpc = connector->display_info.bpc;
/* We might have to reduce the bpc */
- nv50_outp_atomic_fix_depth(encoder, crtc_state);
+ nv50_outp_atomic_fix_depth(encoder, crtc_state, conn_state);
return 0;
}
@@ -1776,6 +1808,27 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_commit *st
switch (nv_encoder->dcb->type) {
case DCB_OUTPUT_TMDS:
+ switch (asyh->or.bpc) {
+ case 16:
+ depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_48_444;
+ break;
+ case 12:
+ depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_36_444;
+ break;
+ case 10:
+ depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_30_444;
+ break;
+ case 8:
+ depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_24_444;
+ break;
+ case 6:
+ depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_18_444;
+ break;
+ default:
+ depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT;
+ break;
+ }
+
if (disp->disp->object.oclass != NV50_DISP &&
nv_connector->base.display_info.is_hdmi)
nv50_hdmi_enable(encoder, nv_crtc, nv_connector, state, mode, hda);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c
index 2bcb3790..f42d6642 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c
@@ -39,6 +39,12 @@ headc37d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
* improved later for deep colour etc.
*/
switch (asyh->or.depth) {
+ case 9:
+ depth = 8;
+ break;
+ case 8:
+ depth = 7;
+ break;
case 6: depth = 5; break;
case 5: depth = 4; break;
case 2: depth = 1; break;
diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c
index 3f8ba495..f1e9f02d 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c
@@ -52,6 +52,12 @@ headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
* improved later for deep colour etc.
*/
switch (asyh->or.depth) {
+ case 9:
+ depth = 8;
+ break;
+ case 8:
+ depth = 7;
+ break;
case 6: depth = 5; break;
case 5: depth = 4; break;
case 2: depth = 1; break;
diff --git a/drivers/gpu/drm/nouveau/dispnv50/headca7d.c b/drivers/gpu/drm/nouveau/dispnv50/headca7d.c
index 678cbc78..437633ff 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/headca7d.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/headca7d.c
@@ -35,6 +35,12 @@ headca7d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
int ret;
switch (asyh->or.depth) {
+ case 9:
+ depth = NVCA7D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_48_444;
+ break;
+ case 8:
+ depth = NVCA7D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_36_444;
+ break;
case 6:
depth = NVCA7D_HEAD_SET_CONTROL_OUTPUT_RESOURCE_PIXEL_DEPTH_BPP_30_444;
break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index cc3caf8d..b452e73f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -298,6 +298,9 @@ nouveau_conn_attach_properties(struct drm_connector *connector)
drm_object_attach_property(&connector->base, dev->mode_config.
dvi_i_subconnector_property, 0);
+ if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)
+ drm_connector_attach_max_bpc_property(connector, 8, 16);
+
/* Add overscan compensation options to digital outputs. */
if (disp->underscan_property &&
(connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
@@ -1101,8 +1104,8 @@ nouveau_connector_get_modes(struct drm_connector *connector)
return ret;
}
-static unsigned
-get_tmds_link_bandwidth(struct drm_connector *connector)
+unsigned int
+nouveau_connector_get_tmds_link_bandwidth(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
@@ -1168,7 +1171,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
max_clock = 400000;
break;
case DCB_OUTPUT_TMDS:
- max_clock = get_tmds_link_bandwidth(connector);
+ max_clock = nouveau_connector_get_tmds_link_bandwidth(connector);
break;
case DCB_OUTPUT_ANALOG:
max_clock = nv_encoder->dcb->crtconf.maxfreq;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index eb292d2b..6623b5e1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -216,6 +216,7 @@ extern int nouveau_duallink;
extern int nouveau_hdmimhz;
void nouveau_conn_attach_properties(struct drm_connector *);
+unsigned int nouveau_connector_get_tmds_link_bandwidth(struct drm_connector *connector);
void nouveau_conn_reset(struct drm_connector *);
struct drm_connector_state *
nouveau_conn_atomic_duplicate_state(struct drm_connector *);
--
2.53.0
next prev parent reply other threads:[~2026-09-22 19:21 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20260922192132.114546-1-Capitain_Jack.ref@yahoo.com>
2026-09-22 19:21 ` [PATCH 0/3] drm/nouveau: HDMI Deep Color link depth (30/36/48 bpp) Daniel Campos Ramos
2026-09-22 19:21 ` Daniel Campos Ramos [this message]
2026-09-22 19:39 ` [PATCH 1/3] drm/nouveau: select HDMI deep-color link depth sashiko-bot
2026-09-22 22:26 ` Daniel Campos Ramos
2026-09-22 19:21 ` [PATCH 2/3] drm/nouveau: pass HDMI GCP deep-color state through NVIF Daniel Campos Ramos
2026-09-22 19:29 ` sashiko-bot
2026-09-22 19:21 ` [PATCH 3/3] drm/nouveau: program HDMI deep-color GCP fields Daniel Campos Ramos
2026-09-22 22:07 ` [PATCH 0/3] drm/nouveau: HDMI Deep Color link depth (30/36/48 bpp) Daniel Campos Ramos
2026-09-23 15:48 ` lyude
2026-09-23 16:33 ` Daniel Campos Ramos
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=20260922192132.114546-2-Capitain_Jack@yahoo.com \
--to=capitain_jack@yahoo.com \
--cc=dakr@kernel.org \
--cc=dri-devel@lists.freedesktop.org \
--cc=lyude@redhat.com \
--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