1 /*
2  * Copyright 2018 Advanced Micro Devices, Inc.
3  *
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:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
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.
21  *
22  * Authors: AMD
23  *
24  */
25 
26 #include "amdgpu_mode.h"
27 #include "amdgpu_dm.h"
28 #include "dc.h"
29 #include "modules/color/color_gamma.h"
30 
31 #define MAX_DRM_LUT_VALUE 0xFFFF
32 
33 /*
34  * Initialize the color module.
35  *
36  * We're not using the full color module, only certain components.
37  * Only call setup functions for components that we need.
38  */
39 void amdgpu_dm_init_color_mod(void)
40 {
41 	setup_x_points_distribution();
42 }
43 
44 
45 /*
46  * Return true if the given lut is a linear mapping of values, i.e. it acts
47  * like a bypass LUT.
48  *
49  * It is considered linear if the lut represents:
50  * f(a) = (0xFF00/MAX_COLOR_LUT_ENTRIES-1)a; for integer a in
51  *                                           [0, MAX_COLOR_LUT_ENTRIES)
52  */
53 static bool __is_lut_linear(struct drm_color_lut *lut, uint32_t size)
54 {
55 	int i;
56 	uint32_t expected;
57 	int delta;
58 
59 	for (i = 0; i < size; i++) {
60 		/* All color values should equal */
61 		if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
62 			return false;
63 
64 		expected = i * MAX_DRM_LUT_VALUE / (size-1);
65 
66 		/* Allow a +/-1 error. */
67 		delta = lut[i].red - expected;
68 		if (delta < -1 || 1 < delta)
69 			return false;
70 	}
71 	return true;
72 }
73 
74 /**
75  * Convert the drm_color_lut to dc_gamma. The conversion depends on the size
76  * of the lut - whether or not it's legacy.
77  */
78 static void __drm_lut_to_dc_gamma(struct drm_color_lut *lut,
79 				  struct dc_gamma *gamma,
80 				  bool is_legacy)
81 {
82 	uint32_t r, g, b;
83 	int i;
84 
85 	if (is_legacy) {
86 		for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) {
87 			r = drm_color_lut_extract(lut[i].red, 16);
88 			g = drm_color_lut_extract(lut[i].green, 16);
89 			b = drm_color_lut_extract(lut[i].blue, 16);
90 
91 			gamma->entries.red[i] = dc_fixpt_from_int(r);
92 			gamma->entries.green[i] = dc_fixpt_from_int(g);
93 			gamma->entries.blue[i] = dc_fixpt_from_int(b);
94 		}
95 		return;
96 	}
97 
98 	/* else */
99 	for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
100 		r = drm_color_lut_extract(lut[i].red, 16);
101 		g = drm_color_lut_extract(lut[i].green, 16);
102 		b = drm_color_lut_extract(lut[i].blue, 16);
103 
104 		gamma->entries.red[i] = dc_fixpt_from_fraction(r, MAX_DRM_LUT_VALUE);
105 		gamma->entries.green[i] = dc_fixpt_from_fraction(g, MAX_DRM_LUT_VALUE);
106 		gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE);
107 	}
108 }
109 
110 /**
111  * amdgpu_dm_set_regamma_lut: Set regamma lut for the given CRTC.
112  * @crtc: amdgpu_dm crtc state
113  *
114  * Update the underlying dc_stream_state's output transfer function (OTF) in
115  * preparation for hardware commit. If no lut is specified by user, we default
116  * to SRGB.
117  *
118  * RETURNS:
119  * 0 on success, -ENOMEM if memory cannot be allocated to calculate the OTF.
120  */
121 int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc)
122 {
123 	struct drm_property_blob *blob = crtc->base.gamma_lut;
124 	struct dc_stream_state *stream = crtc->stream;
125 	struct drm_color_lut *lut;
126 	uint32_t lut_size;
127 	struct dc_gamma *gamma;
128 	enum dc_transfer_func_type old_type = stream->out_transfer_func->type;
129 
130 	bool ret;
131 
132 	if (!blob) {
133 		/* By default, use the SRGB predefined curve.*/
134 		stream->out_transfer_func->type = TF_TYPE_PREDEFINED;
135 		stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
136 		return 0;
137 	}
138 
139 	lut = (struct drm_color_lut *)blob->data;
140 	lut_size = blob->length / sizeof(struct drm_color_lut);
141 
142 	gamma = dc_create_gamma();
143 	if (!gamma)
144 		return -ENOMEM;
145 
146 	gamma->num_entries = lut_size;
147 	if (gamma->num_entries == MAX_COLOR_LEGACY_LUT_ENTRIES)
148 		gamma->type = GAMMA_RGB_256;
149 	else if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES)
150 		gamma->type = GAMMA_CS_TFM_1D;
151 	else {
152 		/* Invalid lut size */
153 		dc_gamma_release(&gamma);
154 		return -EINVAL;
155 	}
156 
157 	/* Convert drm_lut into dc_gamma */
158 	__drm_lut_to_dc_gamma(lut, gamma, gamma->type == GAMMA_RGB_256);
159 
160 	/* Call color module to translate into something DC understands. Namely
161 	 * a transfer function.
162 	 */
163 	stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
164 	ret = mod_color_calculate_regamma_params(stream->out_transfer_func,
165 						 gamma, true);
166 	dc_gamma_release(&gamma);
167 	if (!ret) {
168 		stream->out_transfer_func->type = old_type;
169 		DRM_ERROR("Out of memory when calculating regamma params\n");
170 		return -ENOMEM;
171 	}
172 
173 	return 0;
174 }
175 
176 /**
177  * amdgpu_dm_set_ctm: Set the color transform matrix for the given CRTC.
178  * @crtc: amdgpu_dm crtc state
179  *
180  * Update the underlying dc_stream_state's gamut remap matrix in preparation
181  * for hardware commit. If no matrix is specified by user, gamut remap will be
182  * disabled.
183  */
184 void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc)
185 {
186 
187 	struct drm_property_blob *blob = crtc->base.ctm;
188 	struct dc_stream_state *stream = crtc->stream;
189 	struct drm_color_ctm *ctm;
190 	int64_t val;
191 	int i;
192 
193 	if (!blob) {
194 		stream->gamut_remap_matrix.enable_remap = false;
195 		return;
196 	}
197 
198 	stream->gamut_remap_matrix.enable_remap = true;
199 	ctm = (struct drm_color_ctm *)blob->data;
200 	/*
201 	 * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating
202 	 * with homogeneous coordinates, augment the matrix with 0's.
203 	 *
204 	 * The format provided is S31.32, using signed-magnitude representation.
205 	 * Our fixed31_32 is also S31.32, but is using 2's complement. We have
206 	 * to convert from signed-magnitude to 2's complement.
207 	 */
208 	for (i = 0; i < 12; i++) {
209 		/* Skip 4th element */
210 		if (i % 4 == 3) {
211 			stream->gamut_remap_matrix.matrix[i] = dc_fixpt_zero;
212 			continue;
213 		}
214 
215 		/* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
216 		val = ctm->matrix[i - (i/4)];
217 		/* If negative, convert to 2's complement. */
218 		if (val & (1ULL << 63))
219 			val = -(val & ~(1ULL << 63));
220 
221 		stream->gamut_remap_matrix.matrix[i].value = val;
222 	}
223 }
224 
225 
226 /**
227  * amdgpu_dm_set_degamma_lut: Set degamma lut for the given CRTC.
228  * @crtc: amdgpu_dm crtc state
229  *
230  * Update the underlying dc_stream_state's input transfer function (ITF) in
231  * preparation for hardware commit. If no lut is specified by user, we default
232  * to SRGB degamma.
233  *
234  * We support degamma bypass, predefined SRGB, and custom degamma
235  *
236  * RETURNS:
237  * 0 on success
238  * -EINVAL if crtc_state has a degamma_lut of invalid size
239  * -ENOMEM if gamma allocation fails
240  */
241 int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state,
242 			      struct dc_plane_state *dc_plane_state)
243 {
244 	struct drm_property_blob *blob = crtc_state->degamma_lut;
245 	struct drm_color_lut *lut;
246 	uint32_t lut_size;
247 	struct dc_gamma *gamma;
248 	bool ret;
249 
250 	if (!blob) {
251 		/* Default to SRGB */
252 		dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
253 		dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
254 		return 0;
255 	}
256 
257 	lut = (struct drm_color_lut *)blob->data;
258 	if (__is_lut_linear(lut, MAX_COLOR_LUT_ENTRIES)) {
259 		dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
260 		dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
261 		return 0;
262 	}
263 
264 	gamma = dc_create_gamma();
265 	if (!gamma)
266 		return -ENOMEM;
267 
268 	lut_size = blob->length / sizeof(struct drm_color_lut);
269 	gamma->num_entries = lut_size;
270 	if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES)
271 		gamma->type = GAMMA_CUSTOM;
272 	else {
273 		dc_gamma_release(&gamma);
274 		return -EINVAL;
275 	}
276 
277 	__drm_lut_to_dc_gamma(lut, gamma, false);
278 
279 	dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
280 	ret = mod_color_calculate_degamma_params(dc_plane_state->in_transfer_func, gamma, true);
281 	dc_gamma_release(&gamma);
282 	if (!ret) {
283 		dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
284 		DRM_ERROR("Out of memory when calculating degamma params\n");
285 		return -ENOMEM;
286 	}
287 
288 	return 0;
289 }
290 
291