From 7a406f8a62ff0a3647f96f0cfdb518a99a01bf3f Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Mon, 3 Sep 2018 20:57:36 -0400 Subject: [PATCH] drm/nouveau/disp: add support for setting scdc parameters for high modes When SCDC is supported, make sure that we configure the GPU and monitor to the same parameters. Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 40 ++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 1dbd1dcdcf15..5f163a025e89 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -531,6 +532,7 @@ nv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) static void nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) { + struct nouveau_drm *drm = nouveau_drm(encoder->dev); struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); struct nv50_disp *disp = nv50_disp(encoder->dev); @@ -548,9 +550,12 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) .pwr.rekey = 56, /* binary driver, and tegra, constant */ }; struct nouveau_connector *nv_connector; + struct drm_hdmi_info *hdmi; u32 max_ac_packet; union hdmi_infoframe avi_frame; union hdmi_infoframe vendor_frame; + bool scdc_supported, high_tmds_clock_ratio = false, scrambling = false; + u8 config; int ret; int size; @@ -558,8 +563,11 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; + hdmi = &nv_connector->base.display_info.hdmi; + scdc_supported = hdmi->scdc.supported; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode, - false); + scdc_supported); if (!ret) { /* We have an AVI InfoFrame, populate it to the display */ args.pwr.avi_infoframe_length @@ -582,12 +590,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode) max_ac_packet -= 18; /* constant from tegra */ args.pwr.max_ac_packet = max_ac_packet / 32; + if (hdmi->scdc.scrambling.supported) { + high_tmds_clock_ratio = mode->clock > 340000; + scrambling = high_tmds_clock_ratio || + hdmi->scdc.scrambling.low_rates; + } + + args.pwr.scdc = + NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE * scrambling | + NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 * high_tmds_clock_ratio; + size = sizeof(args.base) + sizeof(args.pwr) + args.pwr.avi_infoframe_length + args.pwr.vendor_infoframe_length; nvif_mthd(&disp->disp->object, 0, &args, size); + nv50_audio_enable(encoder, mode); + + /* If SCDC is supported by the downstream monitor, update + * divider / scrambling settings to what we programmed above. + */ + if (!hdmi->scdc.scrambling.supported) + return; + + ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &config); + if (ret < 0) { + NV_ERROR(drm, "Failure to read SCDC_TMDS_CONFIG: %d\n", ret); + return; + } + config &= ~(SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE); + config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 * high_tmds_clock_ratio; + config |= SCDC_SCRAMBLING_ENABLE * scrambling; + ret = drm_scdc_writeb(nv_encoder->i2c, SCDC_TMDS_CONFIG, config); + if (ret < 0) + NV_ERROR(drm, "Failure to write SCDC_TMDS_CONFIG = 0x%02x: %d\n", + config, ret); } /****************************************************************************** -- 2.30.2