drm/rockchip: inno_hdmi: Correctly setup HDMI quantization range
authorAlex Bee <knaerzche@gmail.com>
Fri, 22 Dec 2023 17:42:11 +0000 (18:42 +0100)
committerHeiko Stuebner <heiko@sntech.de>
Fri, 29 Dec 2023 23:38:29 +0000 (00:38 +0100)
The display controller will always give full range RGB regardless of the
mode set, but HDMI requires certain modes to be transmitted in limited
range RGB. This is especially required for HDMI sinks which do not support
non-standard quantization ranges.

This enables color space conversion for those modes and sets the
quantization range accordingly in the AVI infoframe.

Signed-off-by: Alex Bee <knaerzche@gmail.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20231222174220.55249-21-knaerzche@gmail.com
drivers/gpu/drm/rockchip/inno_hdmi.c

index c6a4b228ea93d7a1bc46b9bde12772f6015891c4..fbf73f6b99ca8bb03a59f0ac7f0b615450fa352a 100644 (file)
@@ -52,6 +52,7 @@ struct inno_hdmi_connector_state {
        struct drm_connector_state      base;
        unsigned int                    enc_out_format;
        unsigned int                    colorimetry;
+       bool                            rgb_limited_range;
 };
 
 static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -269,6 +270,18 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
        else
                frame.avi.colorspace = HDMI_COLORSPACE_RGB;
 
+       if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
+               drm_hdmi_avi_infoframe_quant_range(&frame.avi,
+                                                  connector, mode,
+                                                  inno_conn_state->rgb_limited_range ?
+                                                  HDMI_QUANTIZATION_RANGE_LIMITED :
+                                                  HDMI_QUANTIZATION_RANGE_FULL);
+       } else {
+               frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
+               frame.avi.ycc_quantization_range =
+                       HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+       }
+
        return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_AVI);
 }
 
@@ -296,29 +309,37 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
        hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
 
        if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
-               value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
-               hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
-
-               hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
-                         m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
-                         v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
-                         v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
-               return 0;
-       }
-
-       if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
-               if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
-                       csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+               if (inno_conn_state->rgb_limited_range) {
+                       csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT;
                        auto_csc = AUTO_CSC_DISABLE;
                        c0_c2_change = C0_C2_CHANGE_DISABLE;
                        csc_enable = v_CSC_ENABLE;
+
+               } else {
+                       value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+                       hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+                       hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+                                 m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+                                 v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+                                 v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+                       return 0;
                }
        } else {
-               if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
-                       csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
-                       auto_csc = AUTO_CSC_DISABLE;
-                       c0_c2_change = C0_C2_CHANGE_DISABLE;
-                       csc_enable = v_CSC_ENABLE;
+               if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+                       if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
+                               csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+                               auto_csc = AUTO_CSC_DISABLE;
+                               c0_c2_change = C0_C2_CHANGE_DISABLE;
+                               csc_enable = v_CSC_ENABLE;
+                       }
+               } else {
+                       if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
+                               csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+                               auto_csc = AUTO_CSC_DISABLE;
+                               c0_c2_change = C0_C2_CHANGE_DISABLE;
+                               csc_enable = v_CSC_ENABLE;
+                       }
                }
        }
 
@@ -472,6 +493,8 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
                inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
 
        inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB;
+       inno_conn_state->rgb_limited_range =
+               drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
 
        return 0;
 }
@@ -558,6 +581,7 @@ static void inno_hdmi_connector_reset(struct drm_connector *connector)
 
        inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
        inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB;
+       inno_conn_state->rgb_limited_range = false;
 }
 
 static struct drm_connector_state *