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 <[email protected]> --- 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
