diff --git a/libweston/backend-drm/colorops.c b/libweston/backend-drm/colorops.c new file mode 100644 index 000000000..5cd663bf2 --- /dev/null +++ b/libweston/backend-drm/colorops.c @@ -0,0 +1,243 @@ +/* + * Copyright © 2025-2026 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include "colorops.h" +#include "color-properties.h" +#include "drm-internal.h" +#include "shared/string-helpers.h" +#include "shared/weston-assert.h" +#include "shared/xalloc.h" + +static void +drm_colorop_destroy(struct drm_colorop *colorop) +{ + wl_list_remove(&colorop->link); + drm_property_info_free(colorop->props, WDRM_COLOROP__COUNT); + + free(colorop); +} + +static struct drm_colorop * +drm_colorop_create(struct drm_color_pipeline *pipeline, uint32_t colorop_id, + uint32_t *next_colorop_id) +{ + struct drm_device *device = pipeline->plane->device; + drmModeObjectPropertiesPtr props_drm; + struct drm_colorop *colorop; + + *next_colorop_id = 0; + + props_drm = drmModeObjectGetProperties(device->kms_device->fd, colorop_id, + DRM_MODE_OBJECT_COLOROP); + if (!props_drm) + return NULL; + + colorop = xzalloc(sizeof(*colorop)); + + wl_list_insert(pipeline->colorop_list.prev, &colorop->link); + + colorop->id = colorop_id; + colorop->pipeline = pipeline; + + drm_property_info_populate(device, colorop_props, colorop->props, + WDRM_COLOROP__COUNT, props_drm); + + colorop->type = drm_property_get_value(&colorop->props[WDRM_COLOROP_TYPE], + props_drm, WDRM_COLOROP_TYPE__COUNT); + if (colorop->type == WDRM_COLOROP_TYPE__COUNT) { + drm_colorop_destroy(colorop); + drmModeFreeObjectProperties(props_drm); + return NULL; + } + + colorop->size = drm_property_get_value(&colorop->props[WDRM_COLOROP_SIZE], + props_drm, 0); + if (colorop->size == 0 && (colorop->type == WDRM_COLOROP_TYPE_1D_LUT || + colorop->type == WDRM_COLOROP_TYPE_3D_LUT)) { + drm_colorop_destroy(colorop); + drmModeFreeObjectProperties(props_drm); + return NULL; + } + + colorop->can_bypass = (colorop->props[WDRM_COLOROP_BYPASS].prop_id != 0); + + *next_colorop_id = + drm_property_get_value(&colorop->props[WDRM_COLOROP_NEXT], + props_drm, 0); + + drmModeFreeObjectProperties(props_drm); + + return colorop; +} + +static const char * +drm_colorop_type_to_str(struct drm_colorop *colorop) +{ + return colorop->props[WDRM_COLOROP_TYPE].enum_values[colorop->type].name; +} + +static void +drm_color_pipeline_print(struct drm_color_pipeline *pipeline, FILE *fp) +{ + struct drm_colorop *colorop; + struct drm_property_info *curve_props; + const char *type; + const char *sep = " "; + unsigned int i; + + if (!fp) + return; + + fprintf(fp, "[colorop] color pipeline %u (owned by plane %u):\n", + pipeline->id, pipeline->plane->plane_id); + + wl_list_for_each(colorop, &pipeline->colorop_list, link) { + type = drm_colorop_type_to_str(colorop); + + fprintf(fp, "%s[colorop] id %u, type %s, can bypass? %s", + sep, colorop->id, type, yesno(colorop->can_bypass)); + + if (colorop->type == WDRM_COLOROP_TYPE_1D_CURVE) { + curve_props = &colorop->props[WDRM_COLOROP_CURVE_1D]; + for (i = 0; i < curve_props->num_enum_values; i++) { + if (curve_props->enum_values[i].valid) + fprintf(fp, " [%s]", + curve_props->enum_values[i].name); + } + } + + fprintf(fp, "\n"); + } +} + +/** + * Populates the color pipelines of a DRM plane. + * + * This does nothing if the driver does not support color pipelines. + * + * @param plane The DRM plane whose pipelines this populates. + * @param plane_props The DRM plane's props. + */ +void +drm_plane_populate_color_pipelines(struct drm_plane *plane, + drmModeObjectPropertiesPtr plane_props) +{ + struct weston_compositor *compositor = plane->base.compositor; + struct drm_device *device = plane->device; + struct drm_backend *b = device->backend; + FILE *dbg = weston_log_scope_stream(b->debug); + drmModePropertyRes *color_pipeline_props; + uint32_t pipeline_i; + unsigned int i; + + if (plane->props[WDRM_PLANE_COLOR_PIPELINE].prop_id == 0) + return; + + color_pipeline_props = + drmModeGetProperty(device->kms_device->fd, + plane->props[WDRM_PLANE_COLOR_PIPELINE].prop_id); + if (!color_pipeline_props) { + drm_debug(b, "failed to get color pipeline property for plane %u\n", + plane->plane_id); + return; + } + + plane->num_color_pipelines = 0; + for (i = 0; (int)i < color_pipeline_props->count_enums; i++) { + if (color_pipeline_props->enums[i].value != 0) + plane->num_color_pipelines++; + } + plane->pipelines = xzalloc(plane->num_color_pipelines * + sizeof(*plane->pipelines)); + plane->pipeline_props_id = color_pipeline_props->prop_id; + + /* Populate pipelines. */ + pipeline_i = 0; + for (i = 0; (int)i < color_pipeline_props->count_enums; i++) { + struct drm_color_pipeline *pipeline; + struct drm_colorop *colorop; + uint32_t colorop_id, next_colorop_id; + + /* First colorop. */ + colorop_id = color_pipeline_props->enums[i].value; + if (colorop_id == 0) + continue; + + pipeline = &plane->pipelines[pipeline_i++]; + + pipeline->plane = plane; + wl_list_init(&pipeline->colorop_list); + + /* Id of the pipeline is the same of its first colorop. */ + pipeline->id = colorop_id; + + while (colorop_id != 0) { + colorop = drm_colorop_create(pipeline, colorop_id, &next_colorop_id); + if (!colorop) { + drm_debug(b, "[colorop] failed to create colorop for id %u, destroying color pipelines for plane %u\n", + colorop_id, plane->plane_id); + drm_plane_release_color_pipelines(plane); + goto out; + } + colorop_id = next_colorop_id; + } + + weston_assert_list_not_empty(compositor, &pipeline->colorop_list); + if (dbg) { + drm_color_pipeline_print(pipeline, dbg); + fflush(dbg); + } + } + weston_assert_u32_eq(b->compositor, + plane->num_color_pipelines, pipeline_i); + +out: + drmModeFreeProperty(color_pipeline_props); +} + +/** + * Release the color pipelines of a drm plane. + * + * @param plane The drm plane whose pipelines should be released. + */ +void +drm_plane_release_color_pipelines(struct drm_plane *plane) +{ + struct drm_color_pipeline *pipeline; + struct drm_colorop *colorop, *tmp; + unsigned int i; + + for (i = 0; i < plane->num_color_pipelines; i++) { + pipeline = &plane->pipelines[i]; + wl_list_for_each_safe(colorop, tmp, &pipeline->colorop_list, link) + drm_colorop_destroy(colorop); + } + + plane->num_color_pipelines = 0; + free(plane->pipelines); + plane->pipelines = NULL; +} diff --git a/libweston/backend-drm/colorops.h b/libweston/backend-drm/colorops.h new file mode 100644 index 000000000..d3c858915 --- /dev/null +++ b/libweston/backend-drm/colorops.h @@ -0,0 +1,77 @@ +/* + * Copyright © 2025-2026 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "drm-internal.h" +#include "drm-kms-enums.h" + +struct drm_colorop { + struct drm_color_pipeline *pipeline; + struct wl_list link; /* drm_pipeline::colorop_list */ + + enum wdrm_colorop_type type; + + uint32_t id; + + /* Some colorop's can be bypassed. */ + bool can_bypass; + + /* Only useful for 1D and 3D LUT colorop's. */ + uint32_t size; + + /* Holds the properties for the colorop. */ + struct drm_property_info props[WDRM_COLOROP__COUNT]; +}; + +struct drm_color_pipeline { + struct drm_plane *plane; + struct wl_list colorop_list; /* drm_colorop::link */ + uint32_t id; +}; + +#if CAN_OFFLOAD_COLOR_PIPELINE + +void +drm_plane_populate_color_pipelines(struct drm_plane *plane, + drmModeObjectPropertiesPtr plane_props); + +void +drm_plane_release_color_pipelines(struct drm_plane *plane); + +#else /* CAN_OFFLOAD_COLOR_PIPELINE */ + +static inline void +drm_plane_populate_color_pipelines(struct drm_plane *plane, + drmModeObjectPropertiesPtr plane_props) +{ +} + +static inline void +drm_plane_release_color_pipelines(struct drm_plane *plane) +{ +} + +#endif /* CAN_OFFLOAD_COLOR_PIPELINE */ diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 7d2600c07..21a244413 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -27,6 +27,8 @@ * SOFTWARE. */ +#pragma once + #include "config.h" #include @@ -227,6 +229,8 @@ struct drm_device { bool aspect_ratio_supported; + bool color_pipeline_supported; + int32_t cursor_width; int32_t cursor_height; @@ -491,6 +495,10 @@ struct drm_plane { struct wl_list link; struct weston_drm_format_array formats; + + uint32_t pipeline_props_id; + uint32_t num_color_pipelines; + struct drm_color_pipeline *pipelines; }; struct drm_plane_handle { @@ -869,6 +877,7 @@ extern struct drm_property_enum_info content_protection_enums[]; extern struct drm_property_enum_info hdcp_content_type_enums[]; extern const struct drm_property_info connector_props[]; extern const struct drm_property_info crtc_props[]; +extern const struct drm_property_info colorop_props[]; int init_kms_caps(struct drm_device *device); diff --git a/libweston/backend-drm/drm-kms-enums.h b/libweston/backend-drm/drm-kms-enums.h index ad9a11148..04e85d8b7 100644 --- a/libweston/backend-drm/drm-kms-enums.h +++ b/libweston/backend-drm/drm-kms-enums.h @@ -57,6 +57,7 @@ enum wdrm_plane_property { WDRM_PLANE_ROTATION, WDRM_PLANE_ALPHA, WDRM_PLANE_COLOR_ENCODING, + WDRM_PLANE_COLOR_PIPELINE, WDRM_PLANE_COLOR_RANGE, WDRM_PLANE__COUNT }; @@ -95,6 +96,20 @@ enum wdrm_plane_color_encoding { }; #define WDRM_PLANE_COLOR_ENCODING_DEFAULT WDRM_PLANE_COLOR_ENCODING_BT709 +/** + * Possible values for the WDRM_PLANE_COLOR_PIPELINE property. + * + * This property is special: the enum values are not deterministic. Each enum + * value corresponds to the id of a colorop created at runtime by the KMS + * driver. drm_property_info_populate() expects well-known values for enum + * properties, so a dummy value is defined to allow it to populate plane->props + * correctly. + */ +enum wdrm_plane_color_pipeline { + WDRM_PLANE_COLOR_PIPELINE_DUMMY = 0, + WDRM_PLANE_COLOR_PIPELINE__COUNT +}; + /** * Possible values for the WDRM_PLANE_COLOR_RANGE property. */ @@ -105,6 +120,65 @@ enum wdrm_plane_color_range { }; #define WDRM_PLANE_COLOR_RANGE_DEFAULT WDRM_PLANE_COLOR_RANGE_LIMITED +/** + * List of properties attached to a DRM colorop. + */ +enum wdrm_colorop_property { + WDRM_COLOROP_TYPE = 0, + WDRM_COLOROP_NEXT, + WDRM_COLOROP_BYPASS, + WDRM_COLOROP_SIZE, + WDRM_COLOROP_DATA, + WDRM_COLOROP_MULTIPLIER, + WDRM_COLOROP_LUT1D_INTERPOLATION, + WDRM_COLOROP_LUT3D_INTERPOLATION, + WDRM_COLOROP_CURVE_1D, + WDRM_COLOROP__COUNT, +}; + +/** + * Possible values for the WDRM_COLOROP_TYPE property. + */ +enum wdrm_colorop_type { + WDRM_COLOROP_TYPE_1D_CURVE = 0, + WDRM_COLOROP_TYPE_1D_LUT, + WDRM_COLOROP_TYPE_CTM_3X4, + WDRM_COLOROP_TYPE_MULTIPLIER, + WDRM_COLOROP_TYPE_3D_LUT, + WDRM_COLOROP_TYPE__COUNT, +}; + +/** + * Possible values for the WDRM_COLOROP_CURVE_1D property. + */ +enum wdrm_colorop_curve_1d { + WDRM_COLOROP_CURVE_1D_SRGB_EOTF = 0, + WDRM_COLOROP_CURVE_1D_SRGB_INV_EOTF, + WDRM_COLOROP_CURVE_1D_PQ_125_EOTF, + WDRM_COLOROP_CURVE_1D_PQ_125_INV_EOTF, + WDRM_COLOROP_CURVE_1D_BT2020_INV_OETF, + WDRM_COLOROP_CURVE_1D_BT2020_OETF, + WDRM_COLOROP_CURVE_1D_GAMMA_22, + WDRM_COLOROP_CURVE_1D_GAMMA_22_INV, + WDRM_COLOROP_CURVE_1D__COUNT, +}; + +/** + * Possible values for the WDRM_COLOROP_LUT1D_INTERPOLATION property. + */ +enum wdrm_colorop_lut1d_interpolation { + WDRM_COLOROP_LUT1D_INTERPOLATION_LINEAR = 0, + WDRM_COLOROP_LUT1D_INTERPOLATION__COUNT, +}; + +/** + * Possible values for the WDRM_COLOROP_LUT3D_INTERPOLATION property. + */ +enum wdrm_colorop_lut3d_interpolation { + WDRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL = 0, + WDRM_COLOROP_LUT3D_INTERPOLATION__COUNT, +}; + /** * List of properties attached to a DRM connector */ diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 71d0de518..ad114b0ac 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -51,6 +51,7 @@ #include #include #include +#include "colorops.h" #include "drm-internal.h" #include "shared/hash.h" #include "shared/helpers.h" @@ -1485,6 +1486,8 @@ drm_plane_create(struct drm_device *device, const drmModePlane *kplane) goto err; } + drm_plane_populate_color_pipelines(plane, props); + drmModeFreeObjectProperties(props); if (plane->type == WDRM_PLANE_TYPE__COUNT) @@ -1588,6 +1591,7 @@ drm_plane_destroy(struct drm_plane *plane) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); drm_plane_state_free(plane->state_cur, true); drm_property_info_free(plane->props, WDRM_PLANE__COUNT); + drm_plane_release_color_pipelines(plane); weston_plane_release(&plane->base); weston_drm_format_array_fini(&plane->formats); wl_list_remove(&plane->link); diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 073f797d8..36e247480 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -91,6 +91,12 @@ struct drm_property_enum_info plane_color_encoding_enums[] = { }, }; +struct drm_property_enum_info plane_color_pipeline_enums[] = { + [WDRM_PLANE_COLOR_PIPELINE_DUMMY] = { + .name = "dummy", + }, +}; + struct drm_property_enum_info plane_color_range_enums[] = { [WDRM_PLANE_COLOR_RANGE_LIMITED] = { .name = "YCbCr limited range", @@ -131,6 +137,11 @@ const struct drm_property_info plane_props[] = { .enum_values = plane_color_encoding_enums, .num_enum_values = WDRM_PLANE_COLOR_ENCODING__COUNT, }, + [WDRM_PLANE_COLOR_PIPELINE] = { + .name = "COLOR_PIPELINE", + .enum_values = plane_color_pipeline_enums, + .num_enum_values = WDRM_PLANE_COLOR_PIPELINE__COUNT, + }, [WDRM_PLANE_COLOR_RANGE] = { .name = "COLOR_RANGE", .enum_values = plane_color_range_enums, @@ -138,6 +149,61 @@ const struct drm_property_info plane_props[] = { }, }; +static struct drm_property_enum_info colorop_type_enums[] = { + [WDRM_COLOROP_TYPE_1D_CURVE] = { .name = "1D Curve", }, + [WDRM_COLOROP_TYPE_1D_LUT] = { .name = "1D LUT", }, + [WDRM_COLOROP_TYPE_CTM_3X4] = { .name = "3x4 Matrix", }, + [WDRM_COLOROP_TYPE_MULTIPLIER] = { .name = "Multiplier", }, + [WDRM_COLOROP_TYPE_3D_LUT] = { .name = "3D LUT", }, +}; + +static struct drm_property_enum_info colorop_curve_1d_enums[] = { + [WDRM_COLOROP_CURVE_1D_SRGB_EOTF] = { .name = "sRGB EOTF", }, + [WDRM_COLOROP_CURVE_1D_SRGB_INV_EOTF] = { .name = "sRGB Inverse EOTF", }, + [WDRM_COLOROP_CURVE_1D_PQ_125_EOTF] = { .name = "PQ 125 EOTF", }, + [WDRM_COLOROP_CURVE_1D_PQ_125_INV_EOTF] = { .name = "PQ 125 Inverse EOTF", }, + [WDRM_COLOROP_CURVE_1D_BT2020_INV_OETF] = { .name = "BT.2020 Inverse OETF", }, + [WDRM_COLOROP_CURVE_1D_BT2020_OETF] = { .name = "BT.2020 OETF", }, + [WDRM_COLOROP_CURVE_1D_GAMMA_22] = { .name = "Gamma 2.2", }, + [WDRM_COLOROP_CURVE_1D_GAMMA_22_INV] = { .name = "Gamma 2.2 Inverse", }, +}; + +static struct drm_property_enum_info colorop_lut1d_interpolation_enums[] = { + [WDRM_COLOROP_LUT1D_INTERPOLATION_LINEAR] = { .name = "Linear", }, +}; + +static struct drm_property_enum_info colorop_lut3d_interpolation_enums[] = { + [WDRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL] = { .name = "Tetrahedral", }, +}; + +const struct drm_property_info colorop_props[] = { + [WDRM_COLOROP_TYPE] = { + .name = "TYPE", + .enum_values = colorop_type_enums, + .num_enum_values = WDRM_COLOROP_TYPE__COUNT, + }, + [WDRM_COLOROP_NEXT] = { .name = "NEXT", }, + [WDRM_COLOROP_BYPASS] = { .name = "BYPASS", }, + [WDRM_COLOROP_SIZE] = { .name = "SIZE", }, + [WDRM_COLOROP_DATA] = { .name = "DATA", }, + [WDRM_COLOROP_MULTIPLIER] = { .name = "MULTIPLIER", }, + [WDRM_COLOROP_CURVE_1D] = { + .name = "CURVE_1D_TYPE", + .enum_values = colorop_curve_1d_enums, + .num_enum_values = WDRM_COLOROP_CURVE_1D__COUNT, + }, + [WDRM_COLOROP_LUT1D_INTERPOLATION] = { + .name = "LUT1D_INTERPOLATION", + .enum_values = colorop_lut1d_interpolation_enums, + .num_enum_values = WDRM_COLOROP_LUT1D_INTERPOLATION__COUNT, + }, + [WDRM_COLOROP_LUT3D_INTERPOLATION] = { + .name = "LUT3D_INTERPOLATION", + .enum_values = colorop_lut3d_interpolation_enums, + .num_enum_values = WDRM_COLOROP_LUT3D_INTERPOLATION__COUNT, + }, +}; + struct drm_property_enum_info dpms_state_enums[] = { [WDRM_DPMS_STATE_OFF] = { .name = "Off", @@ -2079,6 +2145,13 @@ init_kms_caps(struct drm_device *device) drmSetClientCap(device->kms_device->fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); +#ifdef DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE + ret = drmSetClientCap(device->kms_device->fd, DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE, 1); + device->color_pipeline_supported = (ret == 0); +#else + device->color_pipeline_supported = false; +#endif + ret = drmGetCap(device->kms_device->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap); if (ret == 0) device->tearing_supported = cap; diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index 5a5320420..210c40572 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -32,6 +32,10 @@ srcs_drm = [ presentation_time_server_protocol_h, ] +if (libdrm_supports_color_pipeline) + srcs_drm += 'colorops.c' +endif + deps_drm = [ dep_egl, # optional dep_vulkan, # optional diff --git a/libweston/meson.build b/libweston/meson.build index b2a8cac8d..9fc9006ad 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -208,6 +208,10 @@ if get_option('backend-drm') dep_session_helper = declare_dependency(link_with: lib_session_helper) endif +libdrm_version = dep_libdrm.version() +libdrm_supports_color_pipeline = libdrm_version.version_compare('>=2.4.130') +config_h.set10('CAN_OFFLOAD_COLOR_PIPELINE', libdrm_supports_color_pipeline) + lib_libinput_backend = static_library( 'libinput-backend', [