From: Andri Yngvason <an...@yngvason.is>

This includes YUV420 as well YUV444 and Auto. Auto will fallback to RGB
to keep things sane and still working.

Signed-off-by: Werner Sembach <w...@tuxedocomputers.com>
Signed-off-by: Andri Yngvason <an...@yngvason.is>
Signed-off-by: Marius Vlad <marius.v...@collabora.com>
Co-Developed-by: Andri Yngvason <an...@yngvason.is>
Co-Developed-by: Marius Vlad <marius.v...@collabora.com>
---
 .../gpu/drm/i915/display/intel_connector.c    | 21 +++++++
 .../gpu/drm/i915/display/intel_connector.h    |  2 +
 .../drm/i915/display/intel_display_types.h    |  1 +
 drivers/gpu/drm/i915/display/intel_dp.c       | 60 +++++++++++++++++--
 drivers/gpu/drm/i915/display/intel_dp.h       |  5 ++
 drivers/gpu/drm/i915/display/intel_dp_mst.c   | 58 +++++++++++++++++-
 drivers/gpu/drm/i915/display/intel_hdmi.c     | 58 ++++++++++++++++--
 7 files changed, 192 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_connector.c 
b/drivers/gpu/drm/i915/display/intel_connector.c
index 42c923f416b3..4f76c91ed614 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.c
+++ b/drivers/gpu/drm/i915/display/intel_connector.c
@@ -39,6 +39,13 @@
 #include "intel_hdcp.h"
 #include "intel_panel.h"
 
+static const u32 supported_colorformats =
+       DRM_COLOR_FORMAT_AUTO |
+       DRM_COLOR_FORMAT_RGB444 |
+       DRM_COLOR_FORMAT_YCBCR444 |
+       DRM_COLOR_FORMAT_YCBCR420;
+
+
 static void intel_connector_modeset_retry_work_fn(struct work_struct *work)
 {
        struct intel_connector *connector = container_of(work, 
typeof(*connector),
@@ -325,6 +332,13 @@ intel_attach_hdmi_colorspace_property(struct drm_connector 
*connector)
                drm_connector_attach_colorspace_property(connector);
 }
 
+void
+intel_attach_hdmi_colorformat_property(struct drm_connector *connector)
+{
+       if (!drm_mode_create_hdmi_color_format_property(connector, 
supported_colorformats))
+               drm_connector_attach_color_format_property(connector);
+}
+
 void
 intel_attach_dp_colorspace_property(struct drm_connector *connector)
 {
@@ -332,6 +346,13 @@ intel_attach_dp_colorspace_property(struct drm_connector 
*connector)
                drm_connector_attach_colorspace_property(connector);
 }
 
+void
+intel_attach_dp_colorformat_property(struct drm_connector *connector)
+{
+       if (!drm_mode_create_dp_color_format_property(connector, 
supported_colorformats))
+               drm_connector_attach_color_format_property(connector);
+}
+
 void
 intel_attach_scaling_mode_property(struct drm_connector *connector)
 {
diff --git a/drivers/gpu/drm/i915/display/intel_connector.h 
b/drivers/gpu/drm/i915/display/intel_connector.h
index aafb25a814fa..7191df21b48b 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.h
+++ b/drivers/gpu/drm/i915/display/intel_connector.h
@@ -31,7 +31,9 @@ void intel_attach_force_audio_property(struct drm_connector 
*connector);
 void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
 void intel_attach_aspect_ratio_property(struct drm_connector *connector);
 void intel_attach_hdmi_colorspace_property(struct drm_connector *connector);
+void intel_attach_hdmi_colorformat_property(struct drm_connector *connector);
 void intel_attach_dp_colorspace_property(struct drm_connector *connector);
+void intel_attach_dp_colorformat_property(struct drm_connector *connector);
 void intel_attach_scaling_mode_property(struct drm_connector *connector);
 void intel_connector_queue_modeset_retry_work(struct intel_connector 
*connector);
 void intel_connector_cancel_modeset_retry_work(struct intel_connector 
*connector);
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h 
b/drivers/gpu/drm/i915/display/intel_display_types.h
index ce45261c4a8f..4128547c3396 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -924,6 +924,7 @@ enum intel_output_format {
        INTEL_OUTPUT_FORMAT_RGB,
        INTEL_OUTPUT_FORMAT_YCBCR420,
        INTEL_OUTPUT_FORMAT_YCBCR444,
+       INTEL_OUTPUT_FORMAT_AUTO,
 };
 
 /* Used by dp and fdi links */
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c 
b/drivers/gpu/drm/i915/display/intel_dp.c
index 7976fec88606..1df0ec28dde2 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -1090,6 +1090,7 @@ static bool source_can_output(struct intel_dp *intel_dp,
        struct intel_display *display = to_intel_display(intel_dp);
 
        switch (format) {
+       case INTEL_OUTPUT_FORMAT_AUTO:
        case INTEL_OUTPUT_FORMAT_RGB:
                return true;
 
@@ -1159,7 +1160,7 @@ dfp_can_convert(struct intel_dp *intel_dp,
        return false;
 }
 
-static enum intel_output_format
+enum intel_output_format
 intel_dp_output_format(struct intel_connector *connector,
                       enum intel_output_format sink_format)
 {
@@ -1194,6 +1195,7 @@ intel_dp_output_format(struct intel_connector *connector,
        return output_format;
 }
 
+
 int intel_dp_min_bpp(enum intel_output_format output_format)
 {
        if (output_format == INTEL_OUTPUT_FORMAT_RGB)
@@ -3030,6 +3032,22 @@ static bool intel_dp_has_audio(struct intel_encoder 
*encoder,
                return intel_conn_state->force_audio == HDMI_AUDIO_ON;
 }
 
+static u32
+intel_output_format_to_drm_color_format(enum intel_output_format input)
+{
+       switch (input) {
+       case INTEL_OUTPUT_FORMAT_AUTO:
+               return DRM_COLOR_FORMAT_AUTO;
+       case INTEL_OUTPUT_FORMAT_RGB:
+               return DRM_COLOR_FORMAT_RGB444;
+       case INTEL_OUTPUT_FORMAT_YCBCR444:
+               return DRM_COLOR_FORMAT_YCBCR444;
+       case INTEL_OUTPUT_FORMAT_YCBCR420:
+               return DRM_COLOR_FORMAT_YCBCR420;
+       }
+       return 0;
+}
+
 static int
 intel_dp_compute_output_format(struct intel_encoder *encoder,
                               struct intel_crtc_state *crtc_state,
@@ -3041,17 +3059,32 @@ intel_dp_compute_output_format(struct intel_encoder 
*encoder,
        struct intel_connector *connector = intel_dp->attached_connector;
        const struct drm_display_info *info = &connector->base.display_info;
        const struct drm_display_mode *adjusted_mode = 
&crtc_state->hw.adjusted_mode;
-       bool ycbcr_420_only;
+       bool ycbcr_420_output = false;
+       bool ycbcr_444_output = false;
        int ret;
+       u32 intel_convert_color_fmt = 0;
+
+       if ((conn_state->color_format == DRM_COLOR_FORMAT_YCBCR420 &&
+           drm_mode_is_420_also(&connector->base.display_info, adjusted_mode)) 
||
+           drm_mode_is_420_only(info, adjusted_mode)) {
+               ycbcr_420_output = true;
+       } else if (conn_state->color_format == DRM_COLOR_FORMAT_YCBCR444) {
+               ycbcr_444_output = true;
+       }
+
+
+       if (ycbcr_420_output)
+               crtc_state->sink_format = INTEL_OUTPUT_FORMAT_YCBCR420;
+       else if (ycbcr_444_output)
+               crtc_state->sink_format = INTEL_OUTPUT_FORMAT_YCBCR444;
+       else
+               crtc_state->sink_format = INTEL_OUTPUT_FORMAT_RGB;
 
-       ycbcr_420_only = drm_mode_is_420_only(info, adjusted_mode);
 
-       if (ycbcr_420_only && !connector->base.ycbcr_420_allowed) {
+       if (ycbcr_420_output && !connector->base.ycbcr_420_allowed) {
                drm_dbg_kms(display->drm,
                            "YCbCr 4:2:0 mode but YCbCr 4:2:0 output not 
possible. Falling back to RGB.\n");
                crtc_state->sink_format = INTEL_OUTPUT_FORMAT_RGB;
-       } else {
-               crtc_state->sink_format = intel_dp_sink_format(connector, 
adjusted_mode);
        }
 
        crtc_state->output_format = intel_dp_output_format(connector, 
crtc_state->sink_format);
@@ -3071,6 +3104,18 @@ intel_dp_compute_output_format(struct intel_encoder 
*encoder,
                                                   respect_downstream_limits);
        }
 
+       intel_convert_color_fmt =
+               
intel_output_format_to_drm_color_format(crtc_state->sink_format);
+       if (conn_state->color_format &&
+           conn_state->color_format != intel_convert_color_fmt &&
+           conn_state->color_format != DRM_COLOR_FORMAT_AUTO) {
+               drm_dbg_kms(display->drm,
+                           "Unsupported color format %s (0x%x), sink has 
0x%x\n",
+                           
drm_get_color_format_name(conn_state->color_format),\
+                           conn_state->color_format, intel_convert_color_fmt);
+               ret = -EINVAL;
+       }
+
        return ret;
 }
 
@@ -6442,12 +6487,15 @@ intel_dp_add_properties(struct intel_dp *intel_dp, 
struct drm_connector *_connec
        else if (DISPLAY_VER(display) >= 5)
                drm_connector_attach_max_bpc_property(&connector->base, 6, 12);
 
+
        /* Register HDMI colorspace for case of lspcon */
        if 
(intel_bios_encoder_is_lspcon(dp_to_dig_port(intel_dp)->base.devdata)) {
                drm_connector_attach_content_type_property(&connector->base);
                intel_attach_hdmi_colorspace_property(&connector->base);
+               intel_attach_hdmi_colorformat_property(&connector->base);
        } else {
                intel_attach_dp_colorspace_property(&connector->base);
+               intel_attach_dp_colorformat_property(&connector->base);
        }
 
        if (intel_dp_has_gamut_metadata_dip(&dp_to_dig_port(intel_dp)->base))
diff --git a/drivers/gpu/drm/i915/display/intel_dp.h 
b/drivers/gpu/drm/i915/display/intel_dp.h
index 0657f5681196..0d6bdcc70b41 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.h
+++ b/drivers/gpu/drm/i915/display/intel_dp.h
@@ -199,6 +199,11 @@ bool intel_dp_compute_config_limits(struct intel_dp 
*intel_dp,
                                    bool dsc,
                                    struct link_config_limits *limits);
 
+enum intel_output_format
+intel_dp_output_format(struct intel_connector *connector,
+               enum intel_output_format sink_format);
+
+
 void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector);
 bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder);
 
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c 
b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index 74497c9a0554..34db7ee00d63 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -92,6 +92,23 @@
  * registers.
  */
 
+static u32
+intel_output_format_to_drm_color_format(enum intel_output_format input)
+{
+       switch (input) {
+       case INTEL_OUTPUT_FORMAT_AUTO:
+               return DRM_COLOR_FORMAT_AUTO;
+       case INTEL_OUTPUT_FORMAT_RGB:
+               return DRM_COLOR_FORMAT_RGB444;
+       case INTEL_OUTPUT_FORMAT_YCBCR444:
+               return DRM_COLOR_FORMAT_YCBCR444;
+       case INTEL_OUTPUT_FORMAT_YCBCR420:
+               return DRM_COLOR_FORMAT_YCBCR420;
+       }
+
+       return 0;
+}
+
 /* From fake MST stream encoder to primary encoder */
 static struct intel_encoder *to_primary_encoder(struct intel_encoder *encoder)
 {
@@ -640,10 +657,14 @@ static int mst_stream_compute_config(struct intel_encoder 
*encoder,
                to_intel_connector(conn_state->connector);
        const struct drm_display_mode *adjusted_mode =
                &pipe_config->hw.adjusted_mode;
+       const struct drm_display_info *info =
+               &connector->base.display_info;
        struct link_config_limits limits;
        bool dsc_needed, joiner_needs_dsc;
        int num_joined_pipes;
        int ret = 0;
+       bool ycbcr_420_output = false;
+       u32 intel_convert_color_fmt = 0;
 
        if (pipe_config->fec_enable &&
            !intel_dp_supports_fec(intel_dp, connector, pipe_config))
@@ -658,10 +679,38 @@ static int mst_stream_compute_config(struct intel_encoder 
*encoder,
        if (num_joined_pipes > 1)
                pipe_config->joiner_pipes = GENMASK(crtc->pipe + 
num_joined_pipes - 1, crtc->pipe);
 
-       pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
-       pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
+       if ((conn_state->color_format == DRM_COLOR_FORMAT_YCBCR420 &&
+           drm_mode_is_420_also(&connector->base.display_info, adjusted_mode)) 
||
+           drm_mode_is_420_only(info, adjusted_mode)) {
+               ycbcr_420_output = true;
+       }
+
+       if (ycbcr_420_output && !connector->base.ycbcr_420_allowed) {
+               drm_dbg_kms(display->drm,
+                           "YCbCr 4:2:0 mode but YCbCr 4:2:0 output not 
possible. Falling back to RGB.\n");
+               pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
+       }
+
+       if (ycbcr_420_output)
+               pipe_config->sink_format = INTEL_OUTPUT_FORMAT_YCBCR420;
+       else
+               pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
+
+       pipe_config->output_format = intel_dp_output_format(connector, 
pipe_config->sink_format);
        pipe_config->has_pch_encoder = false;
 
+       intel_convert_color_fmt =
+               
intel_output_format_to_drm_color_format(pipe_config->sink_format);
+       if (conn_state->color_format &&
+           conn_state->color_format != intel_convert_color_fmt &&
+           conn_state->color_format != DRM_COLOR_FORMAT_AUTO) {
+               drm_dbg_kms(display->drm,
+                           "Unsupported color format %s (0x%x), sink has 
0x%x\n",
+                           
drm_get_color_format_name(conn_state->color_format),\
+                           conn_state->color_format, intel_convert_color_fmt);
+               ret = -EINVAL;
+       }
+
        joiner_needs_dsc = intel_dp_joiner_needs_dsc(display, num_joined_pipes);
 
        dsc_needed = joiner_needs_dsc || intel_dp->force_dsc_en ||
@@ -1647,6 +1696,11 @@ static int mst_topology_add_connector_properties(struct 
intel_dp *intel_dp,
        if (connector->base.max_bpc_property)
                drm_connector_attach_max_bpc_property(&connector->base, 6, 12);
 
+       connector->base.color_format_property =
+               intel_dp->attached_connector->base.color_format_property;
+       if (connector->base.color_format_property)
+               intel_attach_dp_colorformat_property(&connector->base);
+
        return drm_connector_set_path_property(&connector->base, pathprop);
 }
 
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c 
b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 9961ff259298..016edb1ab248 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -2243,6 +2243,23 @@ intel_hdmi_output_format(const struct intel_crtc_state 
*crtc_state)
        return crtc_state->sink_format;
 }
 
+static u32
+intel_output_format_to_drm_color_format(enum intel_output_format input)
+{
+       switch (input) {
+       case INTEL_OUTPUT_FORMAT_AUTO:
+               return DRM_COLOR_FORMAT_AUTO;
+       case INTEL_OUTPUT_FORMAT_RGB:
+               return DRM_COLOR_FORMAT_RGB444;
+       case INTEL_OUTPUT_FORMAT_YCBCR444:
+               return DRM_COLOR_FORMAT_YCBCR444;
+       case INTEL_OUTPUT_FORMAT_YCBCR420:
+               return DRM_COLOR_FORMAT_YCBCR420;
+       }
+
+       return 0;
+}
+
 static int intel_hdmi_compute_output_format(struct intel_encoder *encoder,
                                            struct intel_crtc_state *crtc_state,
                                            const struct drm_connector_state 
*conn_state,
@@ -2252,13 +2269,29 @@ static int intel_hdmi_compute_output_format(struct 
intel_encoder *encoder,
        struct intel_connector *connector = 
to_intel_connector(conn_state->connector);
        const struct drm_display_mode *adjusted_mode = 
&crtc_state->hw.adjusted_mode;
        const struct drm_display_info *info = &connector->base.display_info;
-       bool ycbcr_420_only = drm_mode_is_420_only(info, adjusted_mode);
+       bool ycbcr_420_output = false;
+       bool ycbcr_444_output = false;
+       u32 intel_convert_color_fmt = 0;
        int ret;
 
-       crtc_state->sink_format =
-               intel_hdmi_sink_format(crtc_state, connector, ycbcr_420_only);
+       if ((conn_state->color_format == DRM_COLOR_FORMAT_YCBCR420 &&
+            drm_mode_is_420_also(&connector->base.display_info, 
adjusted_mode)) ||
+            drm_mode_is_420_only(info, adjusted_mode)) {
+               ycbcr_420_output = true;
+       } else if (conn_state->color_format == DRM_COLOR_FORMAT_YCBCR444) {
+               ycbcr_444_output = true;
+       }
+
+       if (ycbcr_420_output)
+               crtc_state->sink_format = INTEL_OUTPUT_FORMAT_YCBCR420;
+       else if (ycbcr_444_output)
+               crtc_state->sink_format = INTEL_OUTPUT_FORMAT_YCBCR444;
+       else
+               crtc_state->sink_format = intel_hdmi_sink_format(crtc_state,
+                                                                connector,
+                                                                
ycbcr_420_output);
 
-       if (ycbcr_420_only && crtc_state->sink_format != 
INTEL_OUTPUT_FORMAT_YCBCR420) {
+       if (ycbcr_420_output && crtc_state->sink_format != 
INTEL_OUTPUT_FORMAT_YCBCR420) {
                drm_dbg_kms(display->drm,
                            "YCbCr 4:2:0 mode but YCbCr 4:2:0 output not 
possible. Falling back to RGB.\n");
                crtc_state->sink_format = INTEL_OUTPUT_FORMAT_RGB;
@@ -2278,6 +2311,19 @@ static int intel_hdmi_compute_output_format(struct 
intel_encoder *encoder,
                ret = intel_hdmi_compute_clock(encoder, crtc_state, 
respect_downstream_limits);
        }
 
+       intel_convert_color_fmt =
+               
intel_output_format_to_drm_color_format(crtc_state->sink_format);
+       if (conn_state->color_format &&
+           conn_state->color_format != intel_convert_color_fmt &&
+           conn_state->color_format != DRM_COLOR_FORMAT_AUTO) {
+               drm_dbg_kms(display->drm,
+                           "Unsupported color format %s (0x%x), current sink 
color format 0x%x\n",
+                           drm_get_color_format_name(conn_state->color_format),
+                           conn_state->color_format,
+                           intel_convert_color_fmt);
+               ret = -EINVAL;
+       }
+
        return ret;
 }
 
@@ -2668,8 +2714,10 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, 
struct drm_connector *_
        if (DISPLAY_VER(display) >= 10)
                
drm_connector_attach_hdr_output_metadata_property(&connector->base);
 
-       if (!HAS_GMCH(display))
+       if (!HAS_GMCH(display)) {
                drm_connector_attach_max_bpc_property(&connector->base, 8, 12);
+               intel_attach_hdmi_colorformat_property(&connector->base);
+       }
 }
 
 /*
-- 
2.47.2

Reply via email to