[RFC PATCH 5/5] drm/amd/display: Fill 3D LUT from userspace
Alex Hung
alex.hung at amd.com
Tue Oct 4 21:14:51 UTC 2022
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.hung at 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
More information about the dri-devel
mailing list