[v2,20/27] drm/rockchip: inno_hdmi: Correctly setup HDMI quantization range
Commit Message
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>
---
changes in v2:
- made rgb_limited_range part of the new custom connector state
drivers/gpu/drm/rockchip/inno_hdmi.c | 60 +++++++++++++++++++---------
1 file changed, 42 insertions(+), 18 deletions(-)
Comments
On Sat, Dec 16, 2023 at 05:26:31PM +0100, Alex Bee wrote:
> @@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
> else
> inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
>
> + inno_conn_state->rgb_limited_range =
> + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
> +
> /* Mute video and audio output */
> hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
> v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
This needs to be done at atomic_check time: the expectation is that by
the time you commit the state, everything is prepared for it.
Maxime
Am 18.12.23 um 10:05 schrieb Maxime Ripard:
> On Sat, Dec 16, 2023 at 05:26:31PM +0100, Alex Bee wrote:
>> @@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
>> else
>> inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
>>
>> + inno_conn_state->rgb_limited_range =
>> + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
>> +
>> /* Mute video and audio output */
>> hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>> v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
> This needs to be done at atomic_check time: the expectation is that by
> the time you commit the state, everything is prepared for it.
OK. I guess that also applies to the other members of
inno_hdmi_connector_state (former hdmi_data) and was wrong all the time.
Alex
> Maxime
On Mon, Dec 18, 2023 at 01:37:47PM +0100, Alex Bee wrote:
>
> Am 18.12.23 um 10:05 schrieb Maxime Ripard:
> > On Sat, Dec 16, 2023 at 05:26:31PM +0100, Alex Bee wrote:
> > > @@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
> > > else
> > > inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
> > > + inno_conn_state->rgb_limited_range =
> > > + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
> > > +
> > > /* Mute video and audio output */
> > > hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
> > > v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
> > This needs to be done at atomic_check time: the expectation is that by
> > the time you commit the state, everything is prepared for it.
> OK. I guess that also applies to the other members of
> inno_hdmi_connector_state (former hdmi_data) and was wrong all the time.
Yeah, this will apply to all the members of inno_hdmi_connector_state indeed
Maxime
@@ -54,6 +54,7 @@ struct inno_hdmi_connector_state {
unsigned int enc_out_format;
unsigned int colorimetry;
unsigned long tmds_rate;
+ bool rgb_limited_range;
};
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -293,6 +294,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);
}
@@ -320,29 +333,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;
+ }
}
}
@@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
else
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
+ inno_conn_state->rgb_limited_range =
+ drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
+
/* Mute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));