From: Alex Hung <alex.hung@xxxxxxx> It is to be used to enable HDR by allowing userpace to create and pass 3D LUTs to kernel and hardware. 1. new drm_colorop_type: DRM_COLOROP_3D_LUT. 2. 3D LUT modes define hardware capabilities to userspace applications. 3. mode index points to current 3D LUT mode in lut_3d_modes. Signed-off-by: Alex Hung <alex.hung@xxxxxxx> --- drivers/gpu/drm/drm_atomic.c | 21 ++++++++ drivers/gpu/drm/drm_atomic_uapi.c | 17 +++++++ drivers/gpu/drm/drm_colorop.c | 69 +++++++++++++++++++++++++ include/drm/drm_colorop.h | 42 ++++++++++++++++ include/uapi/drm/drm_mode.h | 84 +++++++++++++++++++++++++++++++ 5 files changed, 233 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 29f8a8f402f2..58f0ef8b2d1d 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -783,6 +783,9 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, const struct drm_colorop_state *state) { struct drm_colorop *colorop = state->colorop; + struct drm_property_blob *modes = state->lut_3d_modes; + struct drm_mode_3dlut_mode *mode_3dlut; + int i; drm_printf(p, "colorop[%u]:\n", colorop->base.id); drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type)); @@ -805,6 +808,24 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, case DRM_COLOROP_MULTIPLIER: drm_printf(p, "\tmultiplier=%llu\n", state->multiplier); break; + case DRM_COLOROP_3D_LUT: + mode_3dlut = (struct drm_mode_3dlut_mode *) modes->data; + + drm_printf(p, "\tlut_3d_modes blob id=%d\n", modes ? modes->base.id : 0); + for (i = 0; i < modes->length / sizeof(struct drm_mode_3dlut_mode); i++) { + drm_printf(p, "\t lut_size=%d\n", mode_3dlut[i].lut_size); + drm_printf(p, "\t lut_strides=%d %d %d\n", mode_3dlut[i].lut_stride[0], + mode_3dlut[i].lut_stride[1], + mode_3dlut[i].lut_stride[2]); + drm_printf(p, "\t interpolation=%s\n", + drm_get_colorop_lut3d_interpolation_name(mode_3dlut[i].interpolation)); + drm_printf(p, "\t color_depth=%d\n", mode_3dlut[i].color_depth); + drm_printf(p, "\t color_format=%X\n", mode_3dlut[i].color_format); + drm_printf(p, "\t traversal_order=%X\n", mode_3dlut[i].traversal_order); + } + drm_printf(p, "\tlut_3d_mode_index=%d\n", state->lut_3d_mode_index); + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 57029e5938f6..e978b1bf021f 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -695,9 +695,11 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_property *property, uint64_t val) { + struct drm_mode_3dlut_mode *modes; ssize_t elem_size = -1; ssize_t size = -1; bool replaced = false; + uint32_t index; switch (colorop->type) { case DRM_COLOROP_1D_LUT: @@ -706,6 +708,15 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, case DRM_COLOROP_CTM_3X4: size = sizeof(struct drm_color_ctm_3x4); break; + case DRM_COLOROP_3D_LUT: + index = state->lut_3d_mode_index; + if (index >= (state->lut_3d_modes->length / sizeof(struct drm_mode_3dlut_mode))) + return -EINVAL; + + modes = (struct drm_mode_3dlut_mode *) state->lut_3d_modes->data; + size = modes[index].lut_stride[0] * modes[index].lut_stride[1] * modes[index].lut_stride[2] * + sizeof(struct drm_color_lut); + break; default: /* should never get here */ return -EINVAL; @@ -729,6 +740,8 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, state->curve_1d_type = val; } else if (property == colorop->multiplier_property) { state->multiplier = val; + } else if (property == colorop->lut_3d_mode_index_property) { + state->lut_3d_mode_index = val; } else if (property == colorop->data_property) { return drm_atomic_color_set_data_property(colorop, state, property, val); @@ -756,6 +769,10 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->curve_1d_type; } else if (property == colorop->multiplier_property) { *val = state->multiplier; + } else if (property == colorop->lut_3d_modes_property) { + *val = (state->lut_3d_modes) ? state->lut_3d_modes->base.id : 0; + } else if (property == colorop->lut_3d_mode_index_property) { + *val = state->lut_3d_mode_index; } else if (property == colorop->size_property) { *val = state->size; } else if (property == colorop->data_property) { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index df0266734639..fd1cd934df48 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -66,6 +66,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" }, { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}, { DRM_COLOROP_MULTIPLIER, "Multiplier"}, + { DRM_COLOROP_3D_LUT, "3D LUT"}, }; static const char * const colorop_curve_1d_type_names[] = { @@ -348,6 +349,53 @@ int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, } EXPORT_SYMBOL(drm_colorop_mult_init); +int drm_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, struct drm_mode_3dlut_mode *mode_3dlut, + size_t num, bool allow_bypass) +{ + struct drm_property_blob *blob; + struct drm_property *prop; + int ret; + + ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_3D_LUT, allow_bypass); + if (ret) + return ret; + + /* lut_3d_modes */ + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "3DLUT_MODES", 0); + if (!prop) + return -ENOMEM; + + colorop->lut_3d_modes_property = prop; + + + blob = drm_property_create_blob(colorop->dev, num * sizeof(struct drm_mode_3dlut_mode), + mode_3dlut); + if (IS_ERR(blob)) + return PTR_ERR(blob); + + drm_object_attach_property(&colorop->base, colorop->lut_3d_modes_property, blob ? blob->base.id : 0); + drm_colorop_reset(colorop); + + drm_property_replace_blob(&colorop->state->lut_3d_modes, blob); + + /* lut_3d_modes index */ + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "3DLUT_MODE_INDEX", 0, num - 1); + if (!prop) + return -ENOMEM; + + colorop->lut_3d_mode_index_property = prop; + drm_object_attach_property(&colorop->base, colorop->lut_3d_mode_index_property, 0); + + /* data */ + ret = drm_colorop_create_data_prop(dev, colorop); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_colorop_3dlut_init); + static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop, struct drm_colorop_state *state) { @@ -440,7 +488,13 @@ static const char * const colorop_type_name[] = { [DRM_COLOROP_1D_LUT] = "1D Curve Custom LUT", [DRM_COLOROP_CTM_3X4] = "3x4 Matrix", [DRM_COLOROP_MULTIPLIER] = "Multiplier", + [DRM_COLOROP_3D_LUT] = "3D LUT", }; + +static const char * const colorop_lu3d_interpolation_name[] = { + [DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL] = "Tetrahedral", +}; + static const char * const colorop_lut1d_interpolation_name[] = { [DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR] = "Linear", }; @@ -476,6 +530,21 @@ const char *drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_inte return colorop_lut1d_interpolation_name[type]; } +/** + * drm_get_colorop_lut3d_interpolation_name - return a string for interpolation type + * @type: interpolation type to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ +const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type) +{ + if (WARN_ON(type >= ARRAY_SIZE(colorop_lu3d_interpolation_name))) + return "unknown"; + + return colorop_lu3d_interpolation_name[type]; +} + /** * drm_colorop_set_next_property - sets the next pointer * @colorop: drm colorop diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index b8c1c4da3444..bf5117f30c80 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -147,6 +147,29 @@ struct drm_colorop_state { */ uint32_t size; + /** + * @lut_3d_modes: + * + * Mode blob for displaying a list of supported 3dlut modes. + * + * To setup a 3D LUT, lut_3d_modes, lut_3d_modes and data are expected + * to be used in the following sequence: + * + * 1. device driver sets a list of supported drm_mode_3dlut_mode in "lut_3d_modes". + * 2. userspace reads "lut_3d_modes" to determines an appropriate mode. + * 3. userspace sets "lut_3d_mode_index" pointing the selected mode. + * 4. userspace passes a 3D LUT via "data" + * 5. usersapce commits to device driver + */ + struct drm_property_blob *lut_3d_modes; + + /** + * @lut_3d_mode_index: + * + * A zero-based index pointing to current lut_3d_mode. + */ + uint16_t lut_3d_mode_index; + /** * @data: * @@ -288,6 +311,21 @@ struct drm_colorop { */ struct drm_property *size_property; + /** + * @lut_3d_modes_property: + * + * 3DLUT mode property used to convert the framebuffer's colors + * to non-linear gamma. + */ + struct drm_property *lut_3d_modes_property; + + /** + * @lut_3d_mode_index_property: + * + * 3DLUT mode index property for choosing 3D LUT mode. + */ + struct drm_property *lut_3d_mode_index_property; + /** * @data_property: * @@ -343,6 +381,9 @@ int drm_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop struct drm_plane *plane, bool allow_bypass); int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, struct drm_plane *plane, bool allow_bypass); +int drm_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, struct drm_mode_3dlut_mode *mode_3dlut, + size_t num, bool allow_bypass); struct drm_colorop_state * drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop); @@ -393,6 +434,7 @@ const char *drm_get_colorop_type_name(enum drm_colorop_type type); */ const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type type); const char *drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_interpolation_type type); +const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type); void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 5ef87cb5b242..290c2e32f692 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -913,6 +913,90 @@ enum drm_colorop_type { * property. */ DRM_COLOROP_MULTIPLIER, + /** + * @DRM_COLOROP_3D_LUT: + * + * A 3D LUT of &drm_color_lut entries, + * packed into a blob via the DATA property. The driver's expected + * LUT size is advertised via the SIZE property. + */ + DRM_COLOROP_3D_LUT, +}; + +/** + * enum drm_colorop_lut3d_interpolation_type - type of 3DLUT interpolation + * + */ +enum drm_colorop_lut3d_interpolation_type { + /** + * @DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL: + * + * Tetrahedral 3DLUT interpolation + */ + DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, +}; + +/** + * enum drm_colorop_lut3d_traversal_order - traversal order of the 3D LUT + * + * This enum describes the order of traversal of 3DLUT elements. + */ +enum drm_colorop_lut3d_traversal_order { + /** + * @DRM_COLOROP_LUT3D_TRAVERSAL_RGB: + * + * the LUT elements are traversed like so: + * for R in range 0..n + * for G in range 0..n + * for B in range 0..n + * color = lut3d[R][G][B] + */ + DRM_COLOROP_LUT3D_TRAVERSAL_RGB, + /** + * @DRM_COLOROP_LUT3D_TRAVERSAL_BGR: + * + * the LUT elements are traversed like so: + * for R in range 0..n + * for G in range 0..n + * for B in range 0..n + * color = lut3d[B][G][R] + */ + DRM_COLOROP_LUT3D_TRAVERSAL_BGR, +}; + +/** + * struct drm_mode_3dlut_mode - 3D LUT mode + * + * The mode describes the supported and selected format of a 3DLUT. + */ +struct drm_mode_3dlut_mode { + /** + * @lut_size: 3D LUT size - can be 9, 17 or 33 + */ + __u16 lut_size; + /** + * @lut_stride: dimensions of 3D LUT. Must be larger than lut_size + */ + __u16 lut_stride[3]; + /** + * @interpolation: interpolation algorithm for 3D LUT. See drm_colorop_lut3d_interpolation_type + */ + __u16 interpolation; + /** + * @color_depth: color depth - can be 8, 10 or 12 + */ + __u16 color_depth; + /** + * @color_format: color format specified by fourcc values + * ex. DRM_FORMAT_XRGB16161616 - color in order of RGB, each is 16bit. + */ + __u32 color_format; + /** + * @traversal_order: + * + * Traversal order when parsing/writing the 3D LUT. See enum drm_colorop_lut3d_traversal_order + */ + __u16 traversal_order; }; /** -- 2.46.2