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@xxxxxxx> --- .../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