aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.15/950-0635-drm-vc4-hdmi-Support-HDMI-YUV-output.patch
diff options
context:
space:
mode:
authorÁlvaro Fernández Rojas <noltari@gmail.com>2022-05-16 23:40:32 +0200
committerÁlvaro Fernández Rojas <noltari@gmail.com>2022-05-17 15:11:22 +0200
commit20ea6adbf199097c4f5f591ffee088340630dae4 (patch)
treed6719d95e136611a1c25bbf7789652d6d402779d /target/linux/bcm27xx/patches-5.15/950-0635-drm-vc4-hdmi-Support-HDMI-YUV-output.patch
parentbca05bd072180dc38ef740b37ded9572a6db1981 (diff)
downloadupstream-20ea6adbf199097c4f5f591ffee088340630dae4.tar.gz
upstream-20ea6adbf199097c4f5f591ffee088340630dae4.tar.bz2
upstream-20ea6adbf199097c4f5f591ffee088340630dae4.zip
bcm27xx: add support for linux v5.15
Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones <mj8263788@gmail.com> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0635-drm-vc4-hdmi-Support-HDMI-YUV-output.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.15/950-0635-drm-vc4-hdmi-Support-HDMI-YUV-output.patch578
1 files changed, 578 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0635-drm-vc4-hdmi-Support-HDMI-YUV-output.patch b/target/linux/bcm27xx/patches-5.15/950-0635-drm-vc4-hdmi-Support-HDMI-YUV-output.patch
new file mode 100644
index 0000000000..72c79c3eed
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.15/950-0635-drm-vc4-hdmi-Support-HDMI-YUV-output.patch
@@ -0,0 +1,578 @@
+From ef94081204ede8c11a28b3c3713c54fee6bc6fea Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 4 Dec 2020 17:12:06 +0100
+Subject: [PATCH] drm/vc4: hdmi: Support HDMI YUV output
+
+In addition to the RGB444 output, the BCM2711 HDMI controller supports
+the YUV444 and YUV422 output formats.
+
+Let's add support for them in the driver, but still use RGB as the
+preferred format.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 289 ++++++++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 14 ++
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 6 +
+ drivers/gpu/drm/vc4/vc4_regs.h | 16 ++
+ 4 files changed, 309 insertions(+), 16 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -102,15 +102,30 @@
+
+ #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000)
+
++static const char * const output_format_str[] = {
++ [VC4_HDMI_OUTPUT_RGB] = "RGB",
++ [VC4_HDMI_OUTPUT_YUV420] = "YUV 4:2:0",
++ [VC4_HDMI_OUTPUT_YUV422] = "YUV 4:2:2",
++ [VC4_HDMI_OUTPUT_YUV444] = "YUV 4:4:4",
++};
++
++static const char *vc4_hdmi_output_fmt_str(enum vc4_hdmi_output_format fmt)
++{
++ if (fmt >= ARRAY_SIZE(output_format_str))
++ return "invalid";
++
++ return output_format_str[fmt];
++}
+
+ static unsigned long long
+ vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
+- unsigned int bpc);
++ unsigned int bpc, enum vc4_hdmi_output_format fmt);
+
+ static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode,
+- unsigned int bpc)
++ unsigned int bpc,
++ enum vc4_hdmi_output_format fmt)
+ {
+- unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc);
++ unsigned long long clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
+
+ return clock > HDMI_14_MAX_TMDS_CLK;
+ }
+@@ -284,7 +299,7 @@ static int vc4_hdmi_connector_get_modes(
+ struct drm_display_mode *mode;
+
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+- if (vc4_hdmi_mode_needs_scrambling(mode, 8)) {
++ if (vc4_hdmi_mode_needs_scrambling(mode, 8, VC4_HDMI_OUTPUT_RGB)) {
+ drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
+ drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
+ }
+@@ -341,6 +356,7 @@ static void vc4_hdmi_connector_reset(str
+
+ new_state->base.max_bpc = 8;
+ new_state->base.max_requested_bpc = 8;
++ new_state->output_format = VC4_HDMI_OUTPUT_RGB;
+ drm_atomic_helper_connector_tv_reset(connector);
+ }
+
+@@ -357,6 +373,7 @@ vc4_hdmi_connector_duplicate_state(struc
+
+ new_state->pixel_rate = vc4_state->pixel_rate;
+ new_state->output_bpc = vc4_state->output_bpc;
++ new_state->output_format = vc4_state->output_format;
+ __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
+
+ return &new_state->base;
+@@ -510,11 +527,38 @@ static void vc4_hdmi_write_infoframe(str
+ DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
+ }
+
++static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
++ enum vc4_hdmi_output_format fmt)
++{
++ switch (fmt) {
++ case VC4_HDMI_OUTPUT_RGB:
++ frame->colorspace = HDMI_COLORSPACE_RGB;
++ break;
++
++ case VC4_HDMI_OUTPUT_YUV420:
++ frame->colorspace = HDMI_COLORSPACE_YUV420;
++ break;
++
++ case VC4_HDMI_OUTPUT_YUV422:
++ frame->colorspace = HDMI_COLORSPACE_YUV422;
++ break;
++
++ case VC4_HDMI_OUTPUT_YUV444:
++ frame->colorspace = HDMI_COLORSPACE_YUV444;
++ break;
++
++ default:
++ break;
++ }
++}
++
+ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct drm_connector *connector = &vc4_hdmi->connector;
+ struct drm_connector_state *cstate = connector->state;
++ struct vc4_hdmi_connector_state *vc4_state =
++ conn_state_to_vc4_hdmi_conn_state(cstate);
+ const struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
+ union hdmi_infoframe frame;
+ int ret;
+@@ -534,6 +578,7 @@ static void vc4_hdmi_set_avi_infoframe(s
+ HDMI_QUANTIZATION_RANGE_FULL :
+ HDMI_QUANTIZATION_RANGE_LIMITED);
+ drm_hdmi_avi_infoframe_colorimetry(&frame.avi, cstate);
++ vc4_hdmi_avi_infoframe_colorspace(&frame.avi, vc4_state->output_format);
+ drm_hdmi_avi_infoframe_bars(&frame.avi, cstate);
+
+ vc4_hdmi_write_infoframe(encoder, &frame);
+@@ -636,7 +681,9 @@ static void vc4_hdmi_enable_scrambling(s
+ if (!vc4_hdmi_supports_scrambling(encoder, mode))
+ return;
+
+- if (!vc4_hdmi_mode_needs_scrambling(mode, vc4_hdmi->output_bpc))
++ if (!vc4_hdmi_mode_needs_scrambling(mode,
++ vc4_hdmi->output_bpc,
++ vc4_hdmi->output_format))
+ return;
+
+ drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true);
+@@ -824,6 +871,38 @@ static const u16 vc5_hdmi_csc_full_rgb_t
+ { 0x0000, 0x0000, 0x1b80, 0x0400 },
+ };
+
++/*
++ * Conversion between Full Range RGB and Full Range YUV422 using the
++ * BT.709 Colorspace
++ *
++ * [ 0.212639 0.715169 0.072192 0 ]
++ * [ -0.117208 -0.394207 0.511416 128 ]
++ * [ 0.511416 -0.464524 -0.046891 128 ]
++ *
++ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
++ */
++static const u16 vc5_hdmi_csc_full_rgb_to_full_yuv422_bt709[3][4] = {
++ { 0x06ce, 0x16e3, 0x024f, 0x0000 },
++ { 0xfc41, 0xf364, 0x105e, 0x2000 },
++ { 0x105e, 0xf124, 0xfe81, 0x2000 },
++};
++
++/*
++ * Conversion between Full Range RGB and Full Range YUV444 using the
++ * BT.709 Colorspace
++ *
++ * [ -0.117208 -0.394207 0.511416 128 ]
++ * [ 0.511416 -0.464524 -0.046891 128 ]
++ * [ 0.212639 0.715169 0.072192 0 ]
++ *
++ * Matrix is signed 2p13 fixed point, with signed 9p6 offsets
++ */
++static const u16 vc5_hdmi_csc_full_rgb_to_full_yuv444_bt709[3][4] = {
++ { 0xfc41, 0xf364, 0x105e, 0x2000 },
++ { 0x105e, 0xf124, 0xfe81, 0x2000 },
++ { 0x06ce, 0x16e3, 0x024f, 0x0000 },
++};
++
+ static void vc5_hdmi_set_csc_coeffs(struct vc4_hdmi *vc4_hdmi,
+ const u16 coeffs[3][4])
+ {
+@@ -841,19 +920,53 @@ static void vc5_hdmi_csc_setup(struct vc
+ struct drm_connector_state *state,
+ const struct drm_display_mode *mode)
+ {
++ struct vc4_hdmi_connector_state *vc4_state =
++ conn_state_to_vc4_hdmi_conn_state(state);
+ unsigned long flags;
++ u32 if_cfg = 0;
++ u32 if_xbar = 0x543210;
++ u32 csc_chan_ctl = 0;
+ u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
+ VC5_MT_CP_CSC_CTL_MODE);
+
+ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+
+- HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, 0x354021);
++ switch (vc4_state->output_format) {
++ case VC4_HDMI_OUTPUT_YUV444:
++ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_full_yuv444_bt709);
++ break;
++
++ case VC4_HDMI_OUTPUT_YUV422:
++ csc_ctl |= VC4_SET_FIELD(VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD,
++ VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422) |
++ VC5_MT_CP_CSC_CTL_USE_444_TO_422 |
++ VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION;
+
+- if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode))
+- vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb);
+- else
+- vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity);
++ csc_chan_ctl |= VC4_SET_FIELD(VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE,
++ VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP);
++
++ if_cfg |= VC4_SET_FIELD(VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY,
++ VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422);
++
++ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_full_yuv422_bt709);
++ break;
+
++ case VC4_HDMI_OUTPUT_RGB:
++ if_xbar = 0x354021;
++
++ if (!vc4_hdmi_is_full_range_rgb(vc4_hdmi, mode))
++ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_to_limited_rgb);
++ else
++ vc5_hdmi_set_csc_coeffs(vc4_hdmi, vc5_hdmi_csc_full_rgb_unity);
++ break;
++
++ default:
++ break;
++ }
++
++ HDMI_WRITE(HDMI_VEC_INTERFACE_CFG, if_cfg);
++ HDMI_WRITE(HDMI_VEC_INTERFACE_XBAR, if_xbar);
++ HDMI_WRITE(HDMI_CSC_CHANNEL_CTL, csc_chan_ctl);
+ HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
+
+ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+@@ -979,6 +1092,15 @@ static void vc5_hdmi_set_timings(struct
+ break;
+ }
+
++ /*
++ * YCC422 is always 36-bit and not considered deep colour so
++ * doesn't signal in GCP
++ */
++ if (vc4_state->output_format == VC4_HDMI_OUTPUT_YUV422) {
++ gcp = 4;
++ gcp_en = false;
++ }
++
+ reg = HDMI_READ(HDMI_DEEP_COLOR_CONFIG_1);
+ reg &= ~(VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK |
+ VC5_HDMI_DEEP_COLOR_CONFIG_1_COLOR_DEPTH_MASK);
+@@ -1258,12 +1380,97 @@ static void vc4_hdmi_encoder_atomic_mode
+
+ mutex_lock(&vc4_hdmi->mutex);
+ vc4_hdmi->output_bpc = vc4_state->output_bpc;
++ vc4_hdmi->output_format = vc4_state->output_format;
+ memcpy(&vc4_hdmi->saved_adjusted_mode,
+ &crtc_state->adjusted_mode,
+ sizeof(vc4_hdmi->saved_adjusted_mode));
+ mutex_unlock(&vc4_hdmi->mutex);
+ }
+
++static bool
++vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi *vc4_hdmi,
++ const struct drm_display_info *info,
++ const struct drm_display_mode *mode,
++ unsigned int format, unsigned int bpc)
++{
++ struct drm_device *dev = vc4_hdmi->connector.dev;
++ u8 vic = drm_match_cea_mode(mode);
++
++ if (vic == 1 && bpc != 8) {
++ drm_dbg(dev, "VIC1 requires a bpc of 8, got %u\n", bpc);
++ return false;
++ }
++
++ if (!info->is_hdmi &&
++ (format != VC4_HDMI_OUTPUT_RGB || bpc != 8)) {
++ drm_dbg(dev, "DVI Monitors require an RGB output at 8 bpc\n");
++ return false;
++ }
++
++ switch (format) {
++ case VC4_HDMI_OUTPUT_RGB:
++ drm_dbg(dev, "RGB Format, checking the constraints.\n");
++
++ if (!(info->color_formats & DRM_COLOR_FORMAT_RGB444))
++ return false;
++
++ if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
++ drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
++ return false;
++ }
++
++ if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
++ drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
++ return false;
++ }
++
++ drm_dbg(dev, "RGB format supported in that configuration.\n");
++
++ return true;
++
++ case VC4_HDMI_OUTPUT_YUV422:
++ drm_dbg(dev, "YUV422 format, checking the constraints.\n");
++
++ if (!(info->color_formats & DRM_COLOR_FORMAT_YCRCB422)) {
++ drm_dbg(dev, "Sink doesn't support YUV422.\n");
++ return false;
++ }
++
++ if (bpc != 12) {
++ drm_dbg(dev, "YUV422 only supports 12 bpc.\n");
++ return false;
++ }
++
++ drm_dbg(dev, "YUV422 format supported in that configuration.\n");
++
++ return true;
++
++ case VC4_HDMI_OUTPUT_YUV444:
++ drm_dbg(dev, "YUV444 format, checking the constraints.\n");
++
++ if (!(info->color_formats & DRM_COLOR_FORMAT_YCRCB444)) {
++ drm_dbg(dev, "Sink doesn't support YUV444.\n");
++ return false;
++ }
++
++ if (bpc == 10 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30)) {
++ drm_dbg(dev, "10 BPC but sink doesn't support Deep Color 30.\n");
++ return false;
++ }
++
++ if (bpc == 12 && !(info->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36)) {
++ drm_dbg(dev, "12 BPC but sink doesn't support Deep Color 36.\n");
++ return false;
++ }
++
++ drm_dbg(dev, "YUV444 format supported in that configuration.\n");
++
++ return true;
++ }
++
++ return false;
++}
++
+ static enum drm_mode_status
+ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
+ unsigned long long clock)
+@@ -1285,13 +1492,17 @@ vc4_hdmi_encoder_clock_valid(const struc
+
+ static unsigned long long
+ vc4_hdmi_encoder_compute_mode_clock(const struct drm_display_mode *mode,
+- unsigned int bpc)
++ unsigned int bpc,
++ enum vc4_hdmi_output_format fmt)
+ {
+ unsigned long long clock = mode->crtc_clock * 1000;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ clock = clock * 2;
+
++ if (fmt == VC4_HDMI_OUTPUT_YUV422)
++ bpc = 8;
++
+ return clock * bpc / 8;
+ }
+
+@@ -1299,11 +1510,11 @@ static int
+ vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi *vc4_hdmi,
+ struct vc4_hdmi_connector_state *vc4_state,
+ const struct drm_display_mode *mode,
+- unsigned int bpc)
++ unsigned int bpc, unsigned int fmt)
+ {
+ unsigned long long clock;
+
+- clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc);
++ clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
+ if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, clock) != MODE_OK)
+ return -EINVAL;
+
+@@ -1313,10 +1524,55 @@ vc4_hdmi_encoder_compute_clock(const str
+ }
+
+ static int
++vc4_hdmi_encoder_compute_format(const struct vc4_hdmi *vc4_hdmi,
++ struct vc4_hdmi_connector_state *vc4_state,
++ const struct drm_display_mode *mode,
++ unsigned int bpc)
++{
++ struct drm_device *dev = vc4_hdmi->connector.dev;
++ const struct drm_connector *connector = &vc4_hdmi->connector;
++ const struct drm_display_info *info = &connector->display_info;
++ unsigned int format;
++
++ drm_dbg(dev, "Trying with an RGB output\n");
++
++ format = VC4_HDMI_OUTPUT_RGB;
++ if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
++ int ret;
++
++ ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
++ mode, bpc, format);
++ if (!ret) {
++ vc4_state->output_format = format;
++ return 0;
++ }
++ }
++
++ drm_dbg(dev, "Failed, Trying with an YUV422 output\n");
++
++ format = VC4_HDMI_OUTPUT_YUV422;
++ if (vc4_hdmi_sink_supports_format_bpc(vc4_hdmi, info, mode, format, bpc)) {
++ int ret;
++
++ ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
++ mode, bpc, format);
++ if (!ret) {
++ vc4_state->output_format = format;
++ return 0;
++ }
++ }
++
++ drm_dbg(dev, "Failed. No Format Supported for that bpc count.\n");
++
++ return -EINVAL;
++}
++
++static int
+ vc4_hdmi_encoder_compute_config(const struct vc4_hdmi *vc4_hdmi,
+ struct vc4_hdmi_connector_state *vc4_state,
+ const struct drm_display_mode *mode)
+ {
++ struct drm_device *dev = vc4_hdmi->connector.dev;
+ struct drm_connector_state *conn_state = &vc4_state->base;
+ unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, 12);
+ unsigned int bpc;
+@@ -1325,17 +1581,18 @@ vc4_hdmi_encoder_compute_config(const st
+ for (bpc = max_bpc; bpc >= 8; bpc -= 2) {
+ drm_dbg(dev, "Trying with a %d bpc output\n", bpc);
+
+- ret = vc4_hdmi_encoder_compute_clock(vc4_hdmi, vc4_state,
+- mode, bpc);
++ ret = vc4_hdmi_encoder_compute_format(vc4_hdmi, vc4_state,
++ mode, bpc);
+ if (ret)
+ continue;
+
+ vc4_state->output_bpc = bpc;
+
+ drm_dbg(dev,
+- "Mode %ux%u @ %uHz: Found configuration: bpc: %u, clock: %llu\n",
++ "Mode %ux%u @ %uHz: Found configuration: bpc: %u, fmt: %s, clock: %llu\n",
+ mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
+ vc4_state->output_bpc,
++ vc4_hdmi_output_fmt_str(vc4_state->output_format),
+ vc4_state->pixel_rate);
+
+ break;
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -121,6 +121,13 @@ struct vc4_hdmi_audio {
+ bool streaming;
+ };
+
++enum vc4_hdmi_output_format {
++ VC4_HDMI_OUTPUT_RGB,
++ VC4_HDMI_OUTPUT_YUV422,
++ VC4_HDMI_OUTPUT_YUV444,
++ VC4_HDMI_OUTPUT_YUV420,
++};
++
+ /* General HDMI hardware state. */
+ struct vc4_hdmi {
+ struct vc4_hdmi_audio audio;
+@@ -227,6 +234,12 @@ struct vc4_hdmi {
+ */
+ unsigned int output_bpc;
+
++ /**
++ * @output_format: Copy of @vc4_connector_state.output_format
++ * for use outside of KMS hooks. Protected by @mutex.
++ */
++ enum vc4_hdmi_output_format output_format;
++
+ /* VC5 debugfs regset */
+ struct debugfs_regset32 cec_regset;
+ struct debugfs_regset32 csc_regset;
+@@ -254,6 +267,7 @@ struct vc4_hdmi_connector_state {
+ struct drm_connector_state base;
+ unsigned long long pixel_rate;
+ unsigned int output_bpc;
++ enum vc4_hdmi_output_format output_format;
+ };
+
+ static inline struct vc4_hdmi_connector_state *
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -54,6 +54,7 @@ enum vc4_hdmi_field {
+ HDMI_CSC_24_23,
+ HDMI_CSC_32_31,
+ HDMI_CSC_34_33,
++ HDMI_CSC_CHANNEL_CTL,
+ HDMI_CSC_CTL,
+
+ /*
+@@ -119,6 +120,7 @@ enum vc4_hdmi_field {
+ HDMI_TX_PHY_POWERDOWN_CTL,
+ HDMI_TX_PHY_RESET_CTL,
+ HDMI_TX_PHY_TMDS_CLK_WORD_SEL,
++ HDMI_VEC_INTERFACE_CFG,
+ HDMI_VEC_INTERFACE_XBAR,
+ HDMI_VERTA0,
+ HDMI_VERTA1,
+@@ -246,6 +248,7 @@ static const struct vc4_hdmi_register __
+ VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4),
+
+ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec),
+ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
+
+ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
+@@ -291,6 +294,7 @@ static const struct vc4_hdmi_register __
+ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
+ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
+ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++ VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c),
+ };
+
+ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = {
+@@ -327,6 +331,7 @@ static const struct vc4_hdmi_register __
+ VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4),
+
+ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0ec),
+ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0),
+
+ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
+@@ -372,6 +377,7 @@ static const struct vc4_hdmi_register __
+ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
+ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
+ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++ VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c),
+ };
+
+ static inline
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -796,11 +796,27 @@ enum {
+ # define VC4_HD_CSC_CTL_RGB2YCC BIT(1)
+ # define VC4_HD_CSC_CTL_ENABLE BIT(0)
+
++# define VC5_MT_CP_CSC_CTL_USE_444_TO_422 BIT(6)
++# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_MASK \
++ VC4_MASK(5, 4)
++# define VC5_MT_CP_CSC_CTL_FILTER_MODE_444_TO_422_STANDARD \
++ 3
++# define VC5_MT_CP_CSC_CTL_USE_RNG_SUPPRESSION BIT(3)
+ # define VC5_MT_CP_CSC_CTL_ENABLE BIT(2)
+ # define VC5_MT_CP_CSC_CTL_MODE_MASK VC4_MASK(1, 0)
+
++# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_MASK \
++ VC4_MASK(7, 6)
++# define VC5_MT_CP_CHANNEL_CTL_OUTPUT_REMAP_LEGACY_STYLE \
++ 2
++
+ # define VC4_DVP_HT_CLOCK_STOP_PIXEL BIT(1)
+
++# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_MASK \
++ VC4_MASK(3, 2)
++# define VC5_DVP_HT_VEC_INTERFACE_CFG_SEL_422_FORMAT_422_LEGACY \
++ 2
++
+ /* HVS display list information. */
+ #define HVS_BOOTLOADER_DLIST_END 32
+