Add a new general DRM property named "color format" which can be used by userspace to request the display driver to output a particular color format.
Possible string values for the new enum property are: - "AUTO" (setup by default, driver internally picks the color format) - "RGB" - "YUV 4:4:4" - "YUV 4:2:2" - "YUV 4:2:0" Drivers should advertise from this list the formats they support in an optimistic best-case scenario. EDID data from the sink can then be used in the kernel's atomic check phase to restrict this set of formats, as well as by userspace to make a correct choice in the first place. Co-developed-by: Werner Sembach <[email protected]> Signed-off-by: Werner Sembach <[email protected]> Co-developed-by: Andri Yngvason <[email protected]> Signed-off-by: Andri Yngvason <[email protected]> Signed-off-by: Marius Vlad <[email protected]> Reviewed-by: Maxime Ripard <[email protected]> Signed-off-by: Nicolas Frattaroli <[email protected]> --- Documentation/gpu/drm-kms.rst | 6 ++ drivers/gpu/drm/drm_atomic_helper.c | 5 ++ drivers/gpu/drm/drm_atomic_uapi.c | 11 +++ drivers/gpu/drm/drm_connector.c | 155 ++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 108 +++++++++++++++++++++++++ 5 files changed, 285 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 2292e65f044c..da5133b672d3 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -599,6 +599,12 @@ Color Management Properties .. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c :doc: overview +Color Format Property +--------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_connector.c + :doc: Color format + Tile Group Property ------------------- diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2e7e8ed8ad7f..0de9748a82c6 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -737,6 +737,11 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, if (old_connector_state->max_requested_bpc != new_connector_state->max_requested_bpc) new_crtc_state->connectors_changed = true; + + if (old_connector_state->color_format != + new_connector_state->color_format) + new_crtc_state->connectors_changed = true; + } if (funcs->atomic_check) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 5bd5bf6661df..dee510c85e59 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -935,6 +935,15 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, state->privacy_screen_sw_state = val; } else if (property == connector->broadcast_rgb_property) { state->hdmi.broadcast_rgb = val; + } else if (property == connector->color_format_property) { + if (val > INT_MAX || !drm_connector_color_format_valid(val)) { + drm_dbg_atomic(connector->dev, + "[CONNECTOR:%d:%s] unknown color format %llu\n", + connector->base.id, connector->name, val); + return -EINVAL; + } + + state->color_format = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -1020,6 +1029,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->privacy_screen_sw_state; } else if (property == connector->broadcast_rgb_property) { *val = state->hdmi.broadcast_rgb; + } else if (property == connector->color_format_property) { + *val = state->color_format; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 47dc53c4a738..1c4f3ff7d84f 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1388,6 +1388,18 @@ static const u32 hdmi_colorspaces = BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65) | BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER); +static const u32 hdmi_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + +static const u32 dp_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + /* * As per DP 1.4a spec, 2.2.5.7.5 VSC SDP Payload for Pixel Encoding/Colorimetry * Format Table 2-120 @@ -2940,6 +2952,149 @@ int drm_connector_attach_colorspace_property(struct drm_connector *connector) } EXPORT_SYMBOL(drm_connector_attach_colorspace_property); +/** + * DOC: Color format + * + * The connector "color format" property allows userspace to request a specific + * color model on the output of the connector. Not all values listed by the + * property are guaranteed to work for every sink; rather, it is an optimistic + * listing of color formats that the source could output depending on + * circumstances. + * + * Whether it actually can output a certain color format is determined during + * the atomic check phase. Consequently, a userspace application that sets the + * color format to a value other than "AUTO" should check whether its atomic + * commit succeeded. + * + * Possible values for "color format": + * + * "AUTO": + * The driver or display protocol helpers should pick a suitable color + * format. All implementations of a specific display protocol will behave + * the same way with "AUTO", but different display protocols do not + * necessarily have the same "AUTO" semantics. + * + * For HDMI connectors, "AUTO" picks RGB, but falls back to YUV 4:2:0 if + * the bandwidth required for full-scale RGB is not available, or the mode + * is YUV 4:2:0-only, as long as the mode, source, and sink all support + * YUV 4:2:0. + * "RGB": + * RGB output format. The quantization range (limited/full) depends on the + * value of the "Broadcast RGB" property if it is present on the connector. + * "YUV 4:4:4": + * YUV 4:4:4 (a.k.a. YCbCr 4:4:4) output format. Chroma is not subsampled. + * The quantization range defaults to limited. + * "YUV 4:2:2": + * YUV 4:2:2 (a.k.a. YCbCr 4:2:2) output format. Chroma has half the + * horizontal resolution of Luma. The quantization range defaults to + * limited. + * "YUV 4:2:0": + * YUV 4:2:0 (a.k.a. YCbCr 4:2:0) output format. Chroma has half the + * horizontal and vertical resolution of Luma. The quantization range + * defaults to limited. + * + * A sink may only support some color formats in specific modes and at specific + * bit depths. The atomic modesetting API should be used to set a working + * configuration in one go, as an unsupported combination of parameters is + * rejected. + */ + +/** + * drm_connector_attach_color_format_property - create and attach color format property + * @connector: connector to create the color format property on + * @supported_color_formats: bitmask of bit-shifted &enum drm_output_color_format + * values the connector supports + * + * Called by a driver to create a color format property. The property is + * attached to the connector automatically on success. + * + * @supported_color_formats should only include color formats the connector + * type can actually support. + * + * Returns: + * 0 on success, negative errno on error + */ +int drm_connector_attach_color_format_property(struct drm_connector *connector, + unsigned long supported_color_formats) +{ + struct drm_device *dev = connector->dev; + struct drm_prop_enum_list enum_list[DRM_CONNECTOR_COLOR_FORMAT_COUNT]; + unsigned int i = 0; + unsigned long fmt; + + if (connector->color_format_property) + return 0; + + if (!supported_color_formats) { + drm_err(dev, "No supported color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + if (supported_color_formats & ~GENMASK(DRM_OUTPUT_COLOR_FORMAT_COUNT - 1, 0)) { + drm_err(dev, "Unknown color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_HDMIB: + if (supported_color_formats & ~hdmi_colorformats) { + drm_err(dev, "Color formats not allowed for HDMI on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + if (supported_color_formats & ~dp_colorformats) { + drm_err(dev, "Color formats not allowed for DP on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + } + + enum_list[0].name = "AUTO"; + enum_list[0].type = DRM_CONNECTOR_COLOR_FORMAT_AUTO; + + for_each_set_bit(fmt, &supported_color_formats, DRM_OUTPUT_COLOR_FORMAT_COUNT) { + switch (fmt) { + case DRM_OUTPUT_COLOR_FORMAT_RGB444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_RGB444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR422: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR420: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + break; + default: + drm_warn(dev, "Unknown supported format %ld on [CONNECTOR:%d:%s]\n", + fmt, connector->base.id, connector->name); + continue; + } + enum_list[i].name = drm_hdmi_connector_get_output_format_name(fmt); + } + + connector->color_format_property = + drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "color format", + enum_list, i + 1); + + if (!connector->color_format_property) + return -ENOMEM; + + drm_object_attach_property(&connector->base, connector->color_format_property, + DRM_CONNECTOR_COLOR_FORMAT_AUTO); + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_color_format_property); + /** * drm_connector_atomic_hdr_metadata_equal - checks if the hdr metadata changed * @old_state: old connector state to compare diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f83f28cae207..620f700fbe76 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -571,14 +571,106 @@ enum drm_colorspace { * YCbCr 4:2:2 output format (ie. with horizontal subsampling) * @DRM_OUTPUT_COLOR_FORMAT_YCBCR420: * YCbCr 4:2:0 output format (ie. with horizontal and vertical subsampling) + * @DRM_OUTPUT_COLOR_FORMAT_COUNT: + * Number of valid output color format values in this enum */ enum drm_output_color_format { DRM_OUTPUT_COLOR_FORMAT_RGB444 = 0, DRM_OUTPUT_COLOR_FORMAT_YCBCR444, DRM_OUTPUT_COLOR_FORMAT_YCBCR422, DRM_OUTPUT_COLOR_FORMAT_YCBCR420, + DRM_OUTPUT_COLOR_FORMAT_COUNT, }; +/** + * enum drm_connector_color_format - Connector Color Format Request + * + * This enum, unlike &enum drm_output_color_format, is used to specify requests + * for a specific color format on a connector through the DRM "color format" + * property. The difference is that it has an "AUTO" value to specify that + * no specific choice has been made. + */ +enum drm_connector_color_format { + /** + * @DRM_CONNECTOR_COLOR_FORMAT_AUTO: The driver or display protocol + * helpers should pick a suitable color format. All implementations of a + * specific display protocol must behave the same way with "AUTO", but + * different display protocols do not necessarily have the same "AUTO" + * semantics. + * + * For HDMI, "AUTO" picks RGB, but falls back to YCbCr 4:2:0 if the + * bandwidth required for full-scale RGB is not available, or the mode + * is YCbCr 4:2:0-only, as long as the mode and output both support + * YCbCr 4:2:0. + * + * For display protocols other than HDMI, the recursive bridge chain + * format selection picks the first chain of bridge formats that works, + * as has already been the case before the introduction of the "color + * format" property. Non-HDMI bridges should therefore either sort their + * bus output formats by preference, or agree on a unified auto format + * selection logic that's implemented in a common state helper (like + * how HDMI does it). + */ + DRM_CONNECTOR_COLOR_FORMAT_AUTO = 0, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_RGB444: RGB output format. The + * quantization range depends on the value of the "Broadcast RGB" + * property if it is present on the connector. + */ + DRM_CONNECTOR_COLOR_FORMAT_RGB444, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: YCbCr 4:4:4 output format (ie. + * not subsampled). Quantization range is "Limited" by default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR444, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: YCbCr 4:2:2 output format (ie. + * with horizontal subsampling). Quantization range is "Limited" by + * default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR422, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: YCbCr 4:2:0 output format (ie. + * with horizontal and vertical subsampling). Quantization range is + * "Limited" by default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR420, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_COUNT: Number of valid connector color + * format values in this enum + */ + DRM_CONNECTOR_COLOR_FORMAT_COUNT, +}; + +/** + * drm_connector_color_format_valid - Validate drm_connector_color_format value + * @fmt: value to check against all values of &enum drm_connector_color_format + * + * Checks whether the passed in value of @fmt is one of the allowable values in + * &enum drm_connector_color_format. + * + * Returns: %true if it's a valid value for the enum, %false otherwise. + */ +static inline bool __pure +drm_connector_color_format_valid(enum drm_connector_color_format fmt) +{ + switch (fmt) { + case DRM_CONNECTOR_COLOR_FORMAT_AUTO: + case DRM_CONNECTOR_COLOR_FORMAT_RGB444: + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: + return true; + default: + return false; + } +} + const char * drm_hdmi_connector_get_output_format_name(enum drm_output_color_format fmt); @@ -1167,6 +1259,13 @@ struct drm_connector_state { */ enum drm_colorspace colorspace; + /** + * @color_format: State variable for Connector property to request + * color format change on Sink. This is most commonly used to switch + * between RGB to YUV and vice-versa. + */ + enum drm_connector_color_format color_format; + /** * @writeback_job: Writeback job for writeback connectors * @@ -2165,6 +2264,12 @@ struct drm_connector { */ struct drm_property *colorspace_property; + /** + * @color_format_property: Connector property to set the suitable + * color format supported by the sink. + */ + struct drm_property *color_format_property; + /** * @path_blob_ptr: * @@ -2648,6 +2753,9 @@ bool drm_connector_has_possible_encoder(struct drm_connector *connector, struct drm_encoder *encoder); const char *drm_get_colorspace_name(enum drm_colorspace colorspace); +int drm_connector_attach_color_format_property(struct drm_connector *connector, + unsigned long supported_color_formats); + /** * drm_for_each_connector_iter - connector_list iterator macro * @connector: &struct drm_connector pointer used as cursor -- 2.53.0
