drm/amd/display: Implement color management
authorLeo (Sunpeng) Li <sunpeng.li@amd.com>
Fri, 2 Feb 2018 15:18:05 +0000 (10:18 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 19 Feb 2018 19:20:06 +0000 (14:20 -0500)
Implement color management functionalities within amdgpu_dm_color, and
expose functions within amdgpu_dm.h.

Signed-off-by: Leo (Sunpeng) Li <sunpeng.li@amd.com>
Reviewed-by: Harry Wentland <Harry.Wentland@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c [new file with mode: 0644]

index d7accc2071c49fd4661e127b59d07f0bf2fb23de..af16973f2c412c901746e0e97b78ef545e637374 100644 (file)
@@ -25,7 +25,7 @@
 
 
 
-AMDGPUDM = amdgpu_dm.o amdgpu_dm_irq.o amdgpu_dm_mst_types.o
+AMDGPUDM = amdgpu_dm.o amdgpu_dm_irq.o amdgpu_dm_mst_types.o amdgpu_dm_color.o
 
 ifneq ($(CONFIG_DRM_AMD_DC),)
 AMDGPUDM += amdgpu_dm_services.o amdgpu_dm_helpers.o
index 99ae85edf3f8b1b35493cefb6f80e7cc339d0e8d..5c2c74c3f122fa4aeab8801d1dc57559abdd47c3 100644 (file)
@@ -268,6 +268,11 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc);
 #define amdgpu_dm_crtc_handle_crc_irq(x)
 #endif
 
+int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state,
+                             struct dc_plane_state *dc_plane_state);
+void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc);
+int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc);
+
 extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs;
 
 #endif /* __AMDGPU_DM_H__ */
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
new file mode 100644 (file)
index 0000000..cc3ee07
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2018 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "modules/color/color_gamma.h"
+
+
+#define MAX_LUT_ENTRIES 256
+
+/*
+ * Return true if the given lut is a linear mapping of values, i.e. it acts
+ * like a bypass LUT.
+ *
+ * It is considered linear if the lut represents:
+ * f(a) = (0xFF00/MAX_LUT_ENTRIES-1)a; for integer a in [0, MAX_LUT_ENTRIES)
+ */
+static bool __is_lut_linear(struct drm_color_lut *lut)
+{
+       int i;
+       uint32_t max_os = 0xFF00;
+       uint32_t expected;
+       int delta;
+
+       for (i = 0; i < MAX_LUT_ENTRIES; i++) {
+               /* All color values should equal */
+               if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
+                       return false;
+
+               expected = i * max_os / (MAX_LUT_ENTRIES-1);
+
+               /* Allow a +/-1 error. */
+               delta = lut[i].red - expected;
+               if (delta < -1 || 1 < delta)
+                       return false;
+       }
+       return true;
+}
+
+/**
+ * amdgpu_dm_set_regamma_lut: Set regamma lut for the given CRTC.
+ * @crtc: amdgpu_dm crtc state
+ *
+ * Update the underlying dc_stream_state's output transfer function (OTF) in
+ * preparation for hardware commit. If no lut is specified by user, we default
+ * to SRGB.
+ *
+ * RETURNS:
+ * 0 on success, -ENOMEM if memory cannot be allocated to calculate the OTF.
+ */
+int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc)
+{
+       struct drm_property_blob *blob = crtc->base.gamma_lut;
+       struct dc_stream_state *stream = crtc->stream;
+       struct drm_color_lut *lut;
+       struct dc_gamma *gamma;
+       enum dc_transfer_func_type old_type = stream->out_transfer_func->type;
+
+       uint32_t r, g, b;
+       int i;
+       bool ret;
+
+       if (!blob) {
+               /* By default, use the SRGB predefined curve.*/
+               stream->out_transfer_func->type = TF_TYPE_PREDEFINED;
+               stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
+               return 0;
+       }
+
+       lut = (struct drm_color_lut *)blob->data;
+
+       if (__is_lut_linear(lut)) {
+               /* Set to bypass if lut is set to linear */
+               stream->out_transfer_func->type = TF_TYPE_BYPASS;
+               stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
+               return 0;
+       }
+
+       gamma = dc_create_gamma();
+       if (!gamma)
+               return -ENOMEM;
+
+       gamma->num_entries = MAX_LUT_ENTRIES;
+       gamma->type = GAMMA_RGB_256;
+
+       /* Truncate, and store in dc_gamma for output tf calculation */
+       for (i = 0; i < gamma->num_entries; i++) {
+               r = drm_color_lut_extract(lut[i].red, 16);
+               g = drm_color_lut_extract(lut[i].green, 16);
+               b = drm_color_lut_extract(lut[i].blue, 16);
+
+               gamma->entries.red[i] = dal_fixed31_32_from_int(r);
+               gamma->entries.green[i] = dal_fixed31_32_from_int(g);
+               gamma->entries.blue[i] = dal_fixed31_32_from_int(b);
+       }
+
+       /* Call color module to translate into something DC understands. Namely
+        * a transfer function.
+        */
+       stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
+       ret = mod_color_calculate_regamma_params(stream->out_transfer_func,
+                                                gamma, true);
+       dc_gamma_release(&gamma);
+       if (!ret) {
+               stream->out_transfer_func->type = old_type;
+               DRM_ERROR("Out of memory when calculating regamma params\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/**
+ * amdgpu_dm_set_ctm: Set the color transform matrix for the given CRTC.
+ * @crtc: amdgpu_dm crtc state
+ *
+ * Update the underlying dc_stream_state's gamut remap matrix in preparation
+ * for hardware commit. If no matrix is specified by user, gamut remap will be
+ * disabled.
+ */
+void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc)
+{
+
+       struct drm_property_blob *blob = crtc->base.ctm;
+       struct dc_stream_state *stream = crtc->stream;
+       struct drm_color_ctm *ctm;
+       int i;
+
+       if (!blob) {
+               stream->gamut_remap_matrix.enable_remap = false;
+               return;
+       }
+
+       stream->gamut_remap_matrix.enable_remap = true;
+       ctm = (struct drm_color_ctm *)blob->data;
+       /*
+        * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating
+        * with homogeneous coordinates, augment the matrix with 0's.
+        *
+        * The format provided is S31.32, which is the same as our fixed31_32.
+        */
+       for (i = 0; i < 12; i++) {
+               /* Skip 4th element */
+               if (i % 4 == 3) {
+                       stream->gamut_remap_matrix.matrix[i] = dal_fixed31_32_zero;
+                       continue;
+               }
+               /* csc[i] = ctm[i - floor(i/4)] */
+               stream->gamut_remap_matrix.matrix[i].value = ctm->matrix[i - (i/4)];
+       }
+}
+
+
+/**
+ * amdgpu_dm_set_degamma_lut: Set degamma lut for the given CRTC.
+ * @crtc: amdgpu_dm crtc state
+ *
+ * Update the underlying dc_stream_state's input transfer function (ITF) in
+ * preparation for hardware commit. If no lut is specified by user, we default
+ * to SRGB degamma.
+ *
+ * Currently, we only support degamma bypass, or preprogrammed SRGB degamma.
+ * Programmable degamma is not supported, and an attempt to do so will return
+ * -EINVAL.
+ *
+ * RETURNS:
+ * 0 on success, -EINVAL if custom degamma curve is given.
+ */
+int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state,
+                             struct dc_plane_state *dc_plane_state)
+{
+       struct drm_property_blob *blob = crtc_state->degamma_lut;
+       struct drm_color_lut *lut;
+
+       if (!blob) {
+               /* Default to SRGB */
+               dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
+               dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
+               return 0;
+       }
+
+       lut = (struct drm_color_lut *)blob->data;
+       if (__is_lut_linear(lut)) {
+               dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
+               dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
+               return 0;
+       }
+
+       /* Otherwise, assume SRGB, since programmable degamma is not
+        * supported.
+        */
+       dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
+       dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
+       return -EINVAL;
+}
+