Implement the 3D LUT interface, convert and pass the data for amdgpu
driver.

Note: A patchset "IGT tests for pre-blending 3D LUT interfaces" for this
proposal is sent to IGT mailing list.

Signed-off-by: Alex Hung <alex.h...@amd.com>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  13 ++
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |   1 +
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 181 ++++++++++++++++++
 3 files changed, 195 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 7094578a683f..10e6dc5c8552 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5656,6 +5656,19 @@ static int fill_dc_plane_attributes(struct amdgpu_device 
*adev,
                dc_plane_state->in_transfer_func->type = TF_TYPE_HWPWL;
        }
 
+       /* 3D LUT from userspace */
+       if (plane_state->color_mgmt_changed) {
+               if (plane_state->lut_3d && dc_plane_state->lut3d_func) {
+                       ret = amdgpu_dm_fill_3dlut_data(plane_state, 
&dc_plane_state->lut3d_func->lut_3d);
+                       if (!ret)
+                               
dc_plane_state->lut3d_func->state.bits.initialized = 1;
+                       else
+                               return ret;
+               } else {
+                       /* TODO disable 3D LUT */
+               }
+       }
+
        return 0;
 }
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index 667957087ccf..644c5ff6ee9a 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -726,6 +726,7 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state 
*crtc,
                                      struct dc_plane_state *dc_plane_state);
 
 void amdgpu_dm_fill_pwl_data(struct drm_property_blob *lut_blob, struct 
pwl_params *lut_params, struct drm_color_lut_range *pwl_definition, int 
pwl_size);
+int amdgpu_dm_fill_3dlut_data(const struct drm_plane_state *plane_state, 
struct tetrahedral_params *param);
 void amdgpu_dm_update_connector_after_detect(
                struct amdgpu_dm_connector *aconnector);
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index ae633fe52525..705852bf63e7 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -22,6 +22,7 @@
  * Authors: AMD
  *
  */
+ #include <linux/videodev2.h>
 #include "amdgpu.h"
 #include "amdgpu_mode.h"
 #include "amdgpu_dm.h"
@@ -469,6 +470,186 @@ int amdgpu_dm_verify_lut_sizes(const struct 
drm_crtc_state *crtc_state)
        return 0;
 }
 
+#define R_3DLUT        0
+#define G_3DLUT        1
+#define B_3DLUT        2
+
+static __u16 extract_rgb_value(void *lut_3d, __u32 color_format, __u8 color)
+{
+       __u64 val = *(__u64 *) lut_3d;
+
+       switch (color_format) {
+       case DRM_FORMAT_XRGB16161616:
+               if (color == R_3DLUT)
+                       return val & 0xFFFF;
+               else if (color == G_3DLUT)
+                       return (val >> 16) & 0xFFFF;
+               else if (color == B_3DLUT)
+                       return (val >> 32) & 0xFFFF;
+               break;
+       case DRM_FORMAT_XBGR16161616:
+               if (color == B_3DLUT)
+                       return val & 0xFFFF;
+               else if (color == G_3DLUT)
+                       return (val >> 16) & 0xFFFF;
+               else if (color == R_3DLUT)
+                       return (val >> 32) & 0xFFFF;
+               break;
+       case DRM_FORMAT_XRGB8888:
+               if (color == R_3DLUT)
+                       return val & 0xFF;
+               else if (color == G_3DLUT)
+                       return (val >> 8) & 0xFF;
+               else if (color == B_3DLUT)
+                       return (val >> 16) & 0xFF;
+               break;
+       case DRM_FORMAT_XBGR8888:
+               if (color == B_3DLUT)
+                       return val & 0xFF;
+               else if (color == G_3DLUT)
+                       return (val >> 8) & 0xFF;
+               else if (color == R_3DLUT)
+                       return (val >> 16) & 0xFF;
+                       break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static bool extract_rgb_data(const struct drm_plane_state *plane_state, struct 
drm_mode_3dlut_mode *mode, __u16 *lut_data)
+{
+       __u16 i, lut_volume;
+       void *lut_3d = plane_state->lut_3d->data;
+       __u32 cfmt = mode->color_format;
+
+       /* copy RGB accordingly */
+       lut_volume = mode->lut_size * mode->lut_size * mode->lut_size;
+       for (i = 0; i < lut_volume; i += 3) {
+               lut_data[i] = extract_rgb_value(lut_3d, cfmt, R_3DLUT);
+               lut_data[i+1] = extract_rgb_value(lut_3d, cfmt, G_3DLUT);
+               lut_data[i+2] = extract_rgb_value(lut_3d, cfmt, B_3DLUT);
+
+               if (cfmt == DRM_FORMAT_XRGB16161616 || cfmt == 
DRM_FORMAT_XBGR16161616)
+                       lut_3d += sizeof(__u64);
+               else if (cfmt == DRM_FORMAT_XRGB8888 || cfmt == 
DRM_FORMAT_XBGR8888)
+                       lut_3d += sizeof(__u32);
+               else
+                       return false;
+       }
+       return true;
+}
+
+static void convert_3dlut_to_tetrahedral_params(struct dc_rgb *rgb,
+       bool is_17x17x17, bool is_12_bits, struct tetrahedral_params *params)
+{
+       struct dc_rgb *lut0;
+       struct dc_rgb *lut1;
+       struct dc_rgb *lut2;
+       struct dc_rgb *lut3;
+       int i, lut_i;
+
+       int num_values;
+
+       if (is_17x17x17 == false) {
+               lut0 = params->tetrahedral_9.lut0;
+               lut1 = params->tetrahedral_9.lut1;
+               lut2 = params->tetrahedral_9.lut2;
+               lut3 = params->tetrahedral_9.lut3;
+               num_values = 729;
+       } else {
+               lut0 = params->tetrahedral_17.lut0;
+               lut1 = params->tetrahedral_17.lut1;
+               lut2 = params->tetrahedral_17.lut2;
+               lut3 = params->tetrahedral_17.lut3;
+               num_values = 4913;
+       }
+
+       params->use_12bits = is_12_bits;
+       params->use_tetrahedral_9 = !is_17x17x17;
+
+       for (lut_i = 0, i = 0; i < num_values - 4; lut_i++, i += 4) {
+               lut0[lut_i].red   = rgb[i].red;
+               lut0[lut_i].green = rgb[i].green;
+               lut0[lut_i].blue  = rgb[i].blue;
+
+               lut1[lut_i].red   = rgb[i + 1].red;
+               lut1[lut_i].green = rgb[i + 1].green;
+               lut1[lut_i].blue  = rgb[i + 1].blue;
+
+               lut2[lut_i].red   = rgb[i + 2].red;
+               lut2[lut_i].green = rgb[i + 2].green;
+               lut2[lut_i].blue  = rgb[i + 2].blue;
+
+               lut3[lut_i].red   = rgb[i + 3].red;
+               lut3[lut_i].green = rgb[i + 3].green;
+               lut3[lut_i].blue  = rgb[i + 3].blue;
+       }
+
+       lut0[lut_i].red      = rgb[i].red;
+       lut0[lut_i].green    = rgb[i].green;
+       lut0[lut_i].blue     = rgb[i].blue;
+}
+
+/* only use for 17x17x17 */
+bool convert_to_tetrahedral(unsigned short rgb_lib[17*17*17*3], struct 
tetrahedral_params *params)
+{
+       bool ret = false;
+       struct dc_rgb *rgb_area = NULL;
+       int ind = 0;
+       int ind_lut = 0;
+       int nir, nig, nib;
+
+       rgb_area = kvcalloc(17 * 17 * 17, sizeof(struct dc_rgb), GFP_KERNEL);
+       if (rgb_area == NULL)
+               goto release;
+
+       memset(rgb_area, 0, sizeof(17 * 17 * 17 * sizeof(struct dc_rgb)));
+
+       for (nib = 0; nib < 17; nib++) {
+               for (nig = 0; nig < 17; nig++) {
+                       for (nir = 0; nir < 17; nir++) {
+                               ind_lut = 3 * (nib + 17*nig + 289*nir);
+
+                               rgb_area[ind].red = rgb_lib[ind_lut + 0];
+                               rgb_area[ind].green = rgb_lib[ind_lut + 1];
+                               rgb_area[ind].blue = rgb_lib[ind_lut + 2];
+                               ind++;
+                       }
+               }
+       }
+       convert_3dlut_to_tetrahedral_params(rgb_area, true, true, params);
+       kvfree(rgb_area);
+       ret = true;
+
+release:
+       return ret;
+}
+
+int amdgpu_dm_fill_3dlut_data(const struct drm_plane_state *plane_state, 
struct tetrahedral_params *param)
+{
+       const struct drm_mode_3dlut_mode *mode = &lut_3d_mode_17_12bit;
+       unsigned short *lut_data;
+
+       lut_data = kmalloc(mode->lut_size * mode->lut_size * mode->lut_size * 
sizeof(__u16) * 3, GFP_KERNEL);
+       if (!extract_rgb_data(plane_state, mode, lut_data))
+               return -EINVAL;
+
+       if (!convert_to_tetrahedral(lut_data, param))
+               return -EINVAL;
+
+       kfree(lut_data);
+
+       if (mode->lut_size == 17)
+               param->use_tetrahedral_9 = false;
+
+       if (mode->bit_depth == 12)
+               param->use_12bits = true;
+
+       return 0;
+}
+
 /**
  * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream.
  * @crtc: amdgpu_dm crtc state
-- 
2.37.3

Reply via email to