From: Alex Hung <alex.h...@amd.com>

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.h...@amd.com>
---
 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

Reply via email to