1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "amdgpu.h"
26#include "amdgpu_mode.h"
27#include "amdgpu_dm.h"
28#include "dc.h"
29#include "modules/color/color_gamma.h"
30#include "basics/conversion.h"
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72#define MAX_DRM_LUT_VALUE 0xFFFF
73
74
75
76
77
78
79
80void amdgpu_dm_init_color_mod(void)
81{
82 setup_x_points_distribution();
83}
84
85
86static const struct drm_color_lut *
87__extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size)
88{
89 *size = blob ? drm_color_lut_size(blob) : 0;
90 return blob ? (struct drm_color_lut *)blob->data : NULL;
91}
92
93
94
95
96
97
98
99
100
101static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
102{
103 int i;
104 uint32_t expected;
105 int delta;
106
107 for (i = 0; i < size; i++) {
108
109 if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
110 return false;
111
112 expected = i * MAX_DRM_LUT_VALUE / (size-1);
113
114
115 delta = lut[i].red - expected;
116 if (delta < -1 || 1 < delta)
117 return false;
118 }
119 return true;
120}
121
122
123
124
125
126static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
127 struct dc_gamma *gamma, bool is_legacy)
128{
129 uint32_t r, g, b;
130 int i;
131
132 if (is_legacy) {
133 for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) {
134 r = drm_color_lut_extract(lut[i].red, 16);
135 g = drm_color_lut_extract(lut[i].green, 16);
136 b = drm_color_lut_extract(lut[i].blue, 16);
137
138 gamma->entries.red[i] = dc_fixpt_from_int(r);
139 gamma->entries.green[i] = dc_fixpt_from_int(g);
140 gamma->entries.blue[i] = dc_fixpt_from_int(b);
141 }
142 return;
143 }
144
145
146 for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
147 r = drm_color_lut_extract(lut[i].red, 16);
148 g = drm_color_lut_extract(lut[i].green, 16);
149 b = drm_color_lut_extract(lut[i].blue, 16);
150
151 gamma->entries.red[i] = dc_fixpt_from_fraction(r, MAX_DRM_LUT_VALUE);
152 gamma->entries.green[i] = dc_fixpt_from_fraction(g, MAX_DRM_LUT_VALUE);
153 gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE);
154 }
155}
156
157
158
159
160
161static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
162 struct fixed31_32 *matrix)
163{
164 int64_t val;
165 int i;
166
167
168
169
170
171
172
173
174
175 for (i = 0; i < 12; i++) {
176
177 if (i % 4 == 3) {
178 matrix[i] = dc_fixpt_zero;
179 continue;
180 }
181
182
183 val = ctm->matrix[i - (i / 4)];
184
185 if (val & (1ULL << 63))
186 val = -(val & ~(1ULL << 63));
187
188 matrix[i].value = val;
189 }
190}
191
192
193static int __set_legacy_tf(struct dc_transfer_func *func,
194 const struct drm_color_lut *lut, uint32_t lut_size,
195 bool has_rom)
196{
197 struct dc_gamma *gamma = NULL;
198 struct calculate_buffer cal_buffer = {0};
199 bool res;
200
201 ASSERT(lut && lut_size == MAX_COLOR_LEGACY_LUT_ENTRIES);
202
203 cal_buffer.buffer_index = -1;
204
205 gamma = dc_create_gamma();
206 if (!gamma)
207 return -ENOMEM;
208
209 gamma->type = GAMMA_RGB_256;
210 gamma->num_entries = lut_size;
211 __drm_lut_to_dc_gamma(lut, gamma, true);
212
213 res = mod_color_calculate_regamma_params(func, gamma, true, has_rom,
214 NULL, &cal_buffer);
215
216 dc_gamma_release(&gamma);
217
218 return res ? 0 : -ENOMEM;
219}
220
221
222static int __set_output_tf(struct dc_transfer_func *func,
223 const struct drm_color_lut *lut, uint32_t lut_size,
224 bool has_rom)
225{
226 struct dc_gamma *gamma = NULL;
227 struct calculate_buffer cal_buffer = {0};
228 bool res;
229
230 ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES);
231
232 cal_buffer.buffer_index = -1;
233
234 gamma = dc_create_gamma();
235 if (!gamma)
236 return -ENOMEM;
237
238 gamma->num_entries = lut_size;
239 __drm_lut_to_dc_gamma(lut, gamma, false);
240
241 if (func->tf == TRANSFER_FUNCTION_LINEAR) {
242
243
244
245
246
247 gamma->type = GAMMA_CUSTOM;
248 res = mod_color_calculate_degamma_params(NULL, func,
249 gamma, true);
250 } else {
251
252
253
254
255 gamma->type = GAMMA_CS_TFM_1D;
256 res = mod_color_calculate_regamma_params(func, gamma, false,
257 has_rom, NULL, &cal_buffer);
258 }
259
260 dc_gamma_release(&gamma);
261
262 return res ? 0 : -ENOMEM;
263}
264
265
266static int __set_input_tf(struct dc_transfer_func *func,
267 const struct drm_color_lut *lut, uint32_t lut_size)
268{
269 struct dc_gamma *gamma = NULL;
270 bool res;
271
272 gamma = dc_create_gamma();
273 if (!gamma)
274 return -ENOMEM;
275
276 gamma->type = GAMMA_CUSTOM;
277 gamma->num_entries = lut_size;
278
279 __drm_lut_to_dc_gamma(lut, gamma, false);
280
281 res = mod_color_calculate_degamma_params(NULL, func, gamma, true);
282 dc_gamma_release(&gamma);
283
284 return res ? 0 : -ENOMEM;
285}
286
287
288
289
290
291
292int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state)
293{
294 const struct drm_color_lut *lut = NULL;
295 uint32_t size = 0;
296
297 lut = __extract_blob_lut(crtc_state->degamma_lut, &size);
298 if (lut && size != MAX_COLOR_LUT_ENTRIES) {
299 DRM_DEBUG_DRIVER(
300 "Invalid Degamma LUT size. Should be %u but got %u.\n",
301 MAX_COLOR_LUT_ENTRIES, size);
302 return -EINVAL;
303 }
304
305 lut = __extract_blob_lut(crtc_state->gamma_lut, &size);
306 if (lut && size != MAX_COLOR_LUT_ENTRIES &&
307 size != MAX_COLOR_LEGACY_LUT_ENTRIES) {
308 DRM_DEBUG_DRIVER(
309 "Invalid Gamma LUT size. Should be %u (or %u for legacy) but got %u.\n",
310 MAX_COLOR_LUT_ENTRIES, MAX_COLOR_LEGACY_LUT_ENTRIES,
311 size);
312 return -EINVAL;
313 }
314
315 return 0;
316}
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
340{
341 struct dc_stream_state *stream = crtc->stream;
342 struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev);
343 bool has_rom = adev->asic_type <= CHIP_RAVEN;
344 struct drm_color_ctm *ctm = NULL;
345 const struct drm_color_lut *degamma_lut, *regamma_lut;
346 uint32_t degamma_size, regamma_size;
347 bool has_regamma, has_degamma;
348 bool is_legacy;
349 int r;
350
351 r = amdgpu_dm_verify_lut_sizes(&crtc->base);
352 if (r)
353 return r;
354
355 degamma_lut = __extract_blob_lut(crtc->base.degamma_lut, °amma_size);
356 regamma_lut = __extract_blob_lut(crtc->base.gamma_lut, ®amma_size);
357
358 has_degamma =
359 degamma_lut && !__is_lut_linear(degamma_lut, degamma_size);
360
361 has_regamma =
362 regamma_lut && !__is_lut_linear(regamma_lut, regamma_size);
363
364 is_legacy = regamma_size == MAX_COLOR_LEGACY_LUT_ENTRIES;
365
366
367 crtc->cm_has_degamma = false;
368 crtc->cm_is_degamma_srgb = false;
369
370
371 if (is_legacy) {
372
373
374
375
376
377
378
379
380
381
382
383 crtc->cm_is_degamma_srgb = true;
384 stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
385 stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
386
387 r = __set_legacy_tf(stream->out_transfer_func, regamma_lut,
388 regamma_size, has_rom);
389 if (r)
390 return r;
391 } else if (has_regamma) {
392
393 stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
394 stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
395
396 r = __set_output_tf(stream->out_transfer_func, regamma_lut,
397 regamma_size, has_rom);
398 if (r)
399 return r;
400 } else {
401
402
403
404
405 stream->out_transfer_func->type = TF_TYPE_BYPASS;
406 stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
407 }
408
409
410
411
412
413
414 crtc->cm_has_degamma = has_degamma;
415
416
417 if (crtc->base.ctm) {
418 ctm = (struct drm_color_ctm *)crtc->base.ctm->data;
419
420
421
422
423
424
425
426
427
428
429 __drm_ctm_to_dc_matrix(ctm, stream->gamut_remap_matrix.matrix);
430
431 stream->gamut_remap_matrix.enable_remap = true;
432 stream->csc_color_matrix.enable_adjustment = false;
433 } else {
434
435 stream->gamut_remap_matrix.enable_remap = false;
436 stream->csc_color_matrix.enable_adjustment = false;
437 }
438
439 return 0;
440}
441
442
443
444
445
446
447
448
449
450
451
452
453int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
454 struct dc_plane_state *dc_plane_state)
455{
456 const struct drm_color_lut *degamma_lut;
457 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
458 uint32_t degamma_size;
459 int r;
460
461
462 switch (dc_plane_state->format) {
463 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr:
464 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb:
465
466 tf = TRANSFER_FUNCTION_BT709;
467 break;
468 default:
469 break;
470 }
471
472 if (crtc->cm_has_degamma) {
473 degamma_lut = __extract_blob_lut(crtc->base.degamma_lut,
474 °amma_size);
475 ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);
476
477 dc_plane_state->in_transfer_func->type =
478 TF_TYPE_DISTRIBUTED_POINTS;
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 if (crtc->cm_is_degamma_srgb)
505 dc_plane_state->in_transfer_func->tf = tf;
506 else
507 dc_plane_state->in_transfer_func->tf =
508 TRANSFER_FUNCTION_LINEAR;
509
510 r = __set_input_tf(dc_plane_state->in_transfer_func,
511 degamma_lut, degamma_size);
512 if (r)
513 return r;
514 } else if (crtc->cm_is_degamma_srgb) {
515
516
517
518
519 dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
520 dc_plane_state->in_transfer_func->tf = tf;
521
522 if (tf != TRANSFER_FUNCTION_SRGB &&
523 !mod_color_calculate_degamma_params(NULL,
524 dc_plane_state->in_transfer_func, NULL, false))
525 return -ENOMEM;
526 } else {
527
528 dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
529 dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
530 }
531
532 return 0;
533}
534