2 * Copyright 2018 Advanced Micro Devices, Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
26 #include "amdgpu_mode.h"
27 #include "amdgpu_dm.h"
28 #include "modules/color/color_gamma.h"
30 #define MAX_DRM_LUT_VALUE 0xFFFF
33 * Initialize the color module.
35 * We're not using the full color module, only certain components.
36 * Only call setup functions for components that we need.
38 void amdgpu_dm_init_color_mod(void)
40 setup_x_points_distribution();
45 * Return true if the given lut is a linear mapping of values, i.e. it acts
48 * It is considered linear if the lut represents:
49 * f(a) = (0xFF00/MAX_COLOR_LUT_ENTRIES-1)a; for integer a in
50 * [0, MAX_COLOR_LUT_ENTRIES)
52 static bool __is_lut_linear(struct drm_color_lut *lut, uint32_t size)
58 for (i = 0; i < size; i++) {
59 /* All color values should equal */
60 if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
63 expected = i * MAX_DRM_LUT_VALUE / (size-1);
65 /* Allow a +/-1 error. */
66 delta = lut[i].red - expected;
67 if (delta < -1 || 1 < delta)
74 * Convert the drm_color_lut to dc_gamma. The conversion depends on the size
75 * of the lut - whether or not it's legacy.
77 static void __drm_lut_to_dc_gamma(struct drm_color_lut *lut,
78 struct dc_gamma *gamma,
85 for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) {
86 r = drm_color_lut_extract(lut[i].red, 16);
87 g = drm_color_lut_extract(lut[i].green, 16);
88 b = drm_color_lut_extract(lut[i].blue, 16);
90 gamma->entries.red[i] = dal_fixed31_32_from_int(r);
91 gamma->entries.green[i] = dal_fixed31_32_from_int(g);
92 gamma->entries.blue[i] = dal_fixed31_32_from_int(b);
98 for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
99 r = drm_color_lut_extract(lut[i].red, 16);
100 g = drm_color_lut_extract(lut[i].green, 16);
101 b = drm_color_lut_extract(lut[i].blue, 16);
103 gamma->entries.red[i] = dal_fixed31_32_from_fraction(r, MAX_DRM_LUT_VALUE);
104 gamma->entries.green[i] = dal_fixed31_32_from_fraction(g, MAX_DRM_LUT_VALUE);
105 gamma->entries.blue[i] = dal_fixed31_32_from_fraction(b, MAX_DRM_LUT_VALUE);
110 * amdgpu_dm_set_regamma_lut: Set regamma lut for the given CRTC.
111 * @crtc: amdgpu_dm crtc state
113 * Update the underlying dc_stream_state's output transfer function (OTF) in
114 * preparation for hardware commit. If no lut is specified by user, we default
118 * 0 on success, -ENOMEM if memory cannot be allocated to calculate the OTF.
120 int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc)
122 struct drm_property_blob *blob = crtc->base.gamma_lut;
123 struct dc_stream_state *stream = crtc->stream;
124 struct drm_color_lut *lut;
126 struct dc_gamma *gamma;
127 enum dc_transfer_func_type old_type = stream->out_transfer_func->type;
132 /* By default, use the SRGB predefined curve.*/
133 stream->out_transfer_func->type = TF_TYPE_PREDEFINED;
134 stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
138 lut = (struct drm_color_lut *)blob->data;
139 lut_size = blob->length / sizeof(struct drm_color_lut);
141 if (__is_lut_linear(lut, lut_size)) {
142 /* Set to bypass if lut is set to linear */
143 stream->out_transfer_func->type = TF_TYPE_BYPASS;
144 stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
148 gamma = dc_create_gamma();
152 gamma->num_entries = lut_size;
153 if (gamma->num_entries == MAX_COLOR_LEGACY_LUT_ENTRIES)
154 gamma->type = GAMMA_RGB_256;
155 else if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES)
156 gamma->type = GAMMA_CS_TFM_1D;
158 /* Invalid lut size */
159 dc_gamma_release(&gamma);
163 /* Convert drm_lut into dc_gamma */
164 __drm_lut_to_dc_gamma(lut, gamma, gamma->type == GAMMA_RGB_256);
166 /* Call color module to translate into something DC understands. Namely
167 * a transfer function.
169 stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
170 ret = mod_color_calculate_regamma_params(stream->out_transfer_func,
172 dc_gamma_release(&gamma);
174 stream->out_transfer_func->type = old_type;
175 DRM_ERROR("Out of memory when calculating regamma params\n");
183 * amdgpu_dm_set_ctm: Set the color transform matrix for the given CRTC.
184 * @crtc: amdgpu_dm crtc state
186 * Update the underlying dc_stream_state's gamut remap matrix in preparation
187 * for hardware commit. If no matrix is specified by user, gamut remap will be
190 void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc)
193 struct drm_property_blob *blob = crtc->base.ctm;
194 struct dc_stream_state *stream = crtc->stream;
195 struct drm_color_ctm *ctm;
200 stream->gamut_remap_matrix.enable_remap = false;
204 stream->gamut_remap_matrix.enable_remap = true;
205 ctm = (struct drm_color_ctm *)blob->data;
207 * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating
208 * with homogeneous coordinates, augment the matrix with 0's.
210 * The format provided is S31.32, using signed-magnitude representation.
211 * Our fixed31_32 is also S31.32, but is using 2's complement. We have
212 * to convert from signed-magnitude to 2's complement.
214 for (i = 0; i < 12; i++) {
215 /* Skip 4th element */
217 stream->gamut_remap_matrix.matrix[i] = dal_fixed31_32_zero;
221 /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
222 val = ctm->matrix[i - (i/4)];
223 /* If negative, convert to 2's complement. */
224 if (val & (1ULL << 63))
225 val = -(val & ~(1ULL << 63));
227 stream->gamut_remap_matrix.matrix[i].value = val;
233 * amdgpu_dm_set_degamma_lut: Set degamma lut for the given CRTC.
234 * @crtc: amdgpu_dm crtc state
236 * Update the underlying dc_stream_state's input transfer function (ITF) in
237 * preparation for hardware commit. If no lut is specified by user, we default
240 * Currently, we only support degamma bypass, or preprogrammed SRGB degamma.
241 * Programmable degamma is not supported, and an attempt to do so will return
245 * 0 on success, -EINVAL if custom degamma curve is given.
247 int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state,
248 struct dc_plane_state *dc_plane_state)
250 struct drm_property_blob *blob = crtc_state->degamma_lut;
251 struct drm_color_lut *lut;
254 /* Default to SRGB */
255 dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
256 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
260 lut = (struct drm_color_lut *)blob->data;
261 if (__is_lut_linear(lut, MAX_COLOR_LUT_ENTRIES)) {
262 dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
263 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
267 /* Otherwise, assume SRGB, since programmable degamma is not
270 dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
271 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;