1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23#include <linux/uaccess.h>
24
25#include <drm/drm_atomic.h>
26#include <drm/drm_color_mgmt.h>
27#include <drm/drm_crtc.h>
28#include <drm/drm_device.h>
29#include <drm/drm_drv.h>
30#include <drm/drm_print.h>
31
32#include "drm_crtc_internal.h"
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130u64 drm_color_ctm_s31_32_to_qm_n(u64 user_input, u32 m, u32 n)
131{
132 u64 mag = (user_input & ~BIT_ULL(63)) >> (32 - n);
133 bool negative = !!(user_input & BIT_ULL(63));
134 s64 val;
135
136 WARN_ON(m > 32 || n > 32);
137
138 val = clamp_val(mag, 0, negative ?
139 BIT_ULL(n + m - 1) : BIT_ULL(n + m - 1) - 1);
140
141 return negative ? -val : val;
142}
143EXPORT_SYMBOL(drm_color_ctm_s31_32_to_qm_n);
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
161 uint degamma_lut_size,
162 bool has_ctm,
163 uint gamma_lut_size)
164{
165 struct drm_device *dev = crtc->dev;
166 struct drm_mode_config *config = &dev->mode_config;
167
168 if (degamma_lut_size) {
169 drm_object_attach_property(&crtc->base,
170 config->degamma_lut_property, 0);
171 drm_object_attach_property(&crtc->base,
172 config->degamma_lut_size_property,
173 degamma_lut_size);
174 }
175
176 if (has_ctm)
177 drm_object_attach_property(&crtc->base,
178 config->ctm_property, 0);
179
180 if (gamma_lut_size) {
181 drm_object_attach_property(&crtc->base,
182 config->gamma_lut_property, 0);
183 drm_object_attach_property(&crtc->base,
184 config->gamma_lut_size_property,
185 gamma_lut_size);
186 }
187}
188EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
189
190
191
192
193
194
195
196
197
198
199
200
201
202int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
203 int gamma_size)
204{
205 uint16_t *r_base, *g_base, *b_base;
206 int i;
207
208 crtc->gamma_size = gamma_size;
209
210 crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
211 GFP_KERNEL);
212 if (!crtc->gamma_store) {
213 crtc->gamma_size = 0;
214 return -ENOMEM;
215 }
216
217 r_base = crtc->gamma_store;
218 g_base = r_base + gamma_size;
219 b_base = g_base + gamma_size;
220 for (i = 0; i < gamma_size; i++) {
221 r_base[i] = i << 8;
222 g_base[i] = i << 8;
223 b_base[i] = i << 8;
224 }
225
226
227 return 0;
228}
229EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
230
231
232
233
234
235
236
237
238static bool drm_crtc_supports_legacy_gamma(struct drm_crtc *crtc)
239{
240 u32 gamma_id = crtc->dev->mode_config.gamma_lut_property->base.id;
241 u32 degamma_id = crtc->dev->mode_config.degamma_lut_property->base.id;
242
243 if (!crtc->gamma_size)
244 return false;
245
246 if (crtc->funcs->gamma_set)
247 return true;
248
249 return !!(drm_mode_obj_find_prop_id(&crtc->base, gamma_id) ||
250 drm_mode_obj_find_prop_id(&crtc->base, degamma_id));
251}
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271static int drm_crtc_legacy_gamma_set(struct drm_crtc *crtc,
272 u16 *red, u16 *green, u16 *blue,
273 u32 size,
274 struct drm_modeset_acquire_ctx *ctx)
275{
276 struct drm_device *dev = crtc->dev;
277 struct drm_atomic_state *state;
278 struct drm_crtc_state *crtc_state;
279 struct drm_property_blob *blob;
280 struct drm_color_lut *blob_data;
281 u32 gamma_id = dev->mode_config.gamma_lut_property->base.id;
282 u32 degamma_id = dev->mode_config.degamma_lut_property->base.id;
283 bool use_gamma_lut;
284 int i, ret = 0;
285 bool replaced;
286
287 if (crtc->funcs->gamma_set)
288 return crtc->funcs->gamma_set(crtc, red, green, blue, size, ctx);
289
290 if (drm_mode_obj_find_prop_id(&crtc->base, gamma_id))
291 use_gamma_lut = true;
292 else if (drm_mode_obj_find_prop_id(&crtc->base, degamma_id))
293 use_gamma_lut = false;
294 else
295 return -ENODEV;
296
297 state = drm_atomic_state_alloc(crtc->dev);
298 if (!state)
299 return -ENOMEM;
300
301 blob = drm_property_create_blob(dev,
302 sizeof(struct drm_color_lut) * size,
303 NULL);
304 if (IS_ERR(blob)) {
305 ret = PTR_ERR(blob);
306 blob = NULL;
307 goto fail;
308 }
309
310
311 blob_data = blob->data;
312 for (i = 0; i < size; i++) {
313 blob_data[i].red = red[i];
314 blob_data[i].green = green[i];
315 blob_data[i].blue = blue[i];
316 }
317
318 state->acquire_ctx = ctx;
319 crtc_state = drm_atomic_get_crtc_state(state, crtc);
320 if (IS_ERR(crtc_state)) {
321 ret = PTR_ERR(crtc_state);
322 goto fail;
323 }
324
325
326 replaced = drm_property_replace_blob(&crtc_state->degamma_lut,
327 use_gamma_lut ? NULL : blob);
328 replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
329 replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
330 use_gamma_lut ? blob : NULL);
331 crtc_state->color_mgmt_changed |= replaced;
332
333 ret = drm_atomic_commit(state);
334
335fail:
336 drm_atomic_state_put(state);
337 drm_property_blob_put(blob);
338 return ret;
339}
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355int drm_mode_gamma_set_ioctl(struct drm_device *dev,
356 void *data, struct drm_file *file_priv)
357{
358 struct drm_mode_crtc_lut *crtc_lut = data;
359 struct drm_crtc *crtc;
360 void *r_base, *g_base, *b_base;
361 int size;
362 struct drm_modeset_acquire_ctx ctx;
363 int ret = 0;
364
365 if (!drm_core_check_feature(dev, DRIVER_MODESET))
366 return -EOPNOTSUPP;
367
368 crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
369 if (!crtc)
370 return -ENOENT;
371
372 if (!drm_crtc_supports_legacy_gamma(crtc))
373 return -ENOSYS;
374
375
376 if (crtc_lut->gamma_size != crtc->gamma_size)
377 return -EINVAL;
378
379 DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
380
381 size = crtc_lut->gamma_size * (sizeof(uint16_t));
382 r_base = crtc->gamma_store;
383 if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
384 ret = -EFAULT;
385 goto out;
386 }
387
388 g_base = r_base + size;
389 if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
390 ret = -EFAULT;
391 goto out;
392 }
393
394 b_base = g_base + size;
395 if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
396 ret = -EFAULT;
397 goto out;
398 }
399
400 ret = drm_crtc_legacy_gamma_set(crtc, r_base, g_base, b_base,
401 crtc->gamma_size, &ctx);
402
403out:
404 DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
405 return ret;
406
407}
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424int drm_mode_gamma_get_ioctl(struct drm_device *dev,
425 void *data, struct drm_file *file_priv)
426{
427 struct drm_mode_crtc_lut *crtc_lut = data;
428 struct drm_crtc *crtc;
429 void *r_base, *g_base, *b_base;
430 int size;
431 int ret = 0;
432
433 if (!drm_core_check_feature(dev, DRIVER_MODESET))
434 return -EOPNOTSUPP;
435
436 crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
437 if (!crtc)
438 return -ENOENT;
439
440
441 if (crtc_lut->gamma_size != crtc->gamma_size)
442 return -EINVAL;
443
444 drm_modeset_lock(&crtc->mutex, NULL);
445 size = crtc_lut->gamma_size * (sizeof(uint16_t));
446 r_base = crtc->gamma_store;
447 if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
448 ret = -EFAULT;
449 goto out;
450 }
451
452 g_base = r_base + size;
453 if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
454 ret = -EFAULT;
455 goto out;
456 }
457
458 b_base = g_base + size;
459 if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
460 ret = -EFAULT;
461 goto out;
462 }
463out:
464 drm_modeset_unlock(&crtc->mutex);
465 return ret;
466}
467
468static const char * const color_encoding_name[] = {
469 [DRM_COLOR_YCBCR_BT601] = "ITU-R BT.601 YCbCr",
470 [DRM_COLOR_YCBCR_BT709] = "ITU-R BT.709 YCbCr",
471 [DRM_COLOR_YCBCR_BT2020] = "ITU-R BT.2020 YCbCr",
472};
473
474static const char * const color_range_name[] = {
475 [DRM_COLOR_YCBCR_FULL_RANGE] = "YCbCr full range",
476 [DRM_COLOR_YCBCR_LIMITED_RANGE] = "YCbCr limited range",
477};
478
479
480
481
482
483
484
485
486const char *drm_get_color_encoding_name(enum drm_color_encoding encoding)
487{
488 if (WARN_ON(encoding >= ARRAY_SIZE(color_encoding_name)))
489 return "unknown";
490
491 return color_encoding_name[encoding];
492}
493
494
495
496
497
498
499
500
501const char *drm_get_color_range_name(enum drm_color_range range)
502{
503 if (WARN_ON(range >= ARRAY_SIZE(color_range_name)))
504 return "unknown";
505
506 return color_range_name[range];
507}
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523int drm_plane_create_color_properties(struct drm_plane *plane,
524 u32 supported_encodings,
525 u32 supported_ranges,
526 enum drm_color_encoding default_encoding,
527 enum drm_color_range default_range)
528{
529 struct drm_device *dev = plane->dev;
530 struct drm_property *prop;
531 struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX,
532 DRM_COLOR_RANGE_MAX)];
533 int i, len;
534
535 if (WARN_ON(supported_encodings == 0 ||
536 (supported_encodings & -BIT(DRM_COLOR_ENCODING_MAX)) != 0 ||
537 (supported_encodings & BIT(default_encoding)) == 0))
538 return -EINVAL;
539
540 if (WARN_ON(supported_ranges == 0 ||
541 (supported_ranges & -BIT(DRM_COLOR_RANGE_MAX)) != 0 ||
542 (supported_ranges & BIT(default_range)) == 0))
543 return -EINVAL;
544
545 len = 0;
546 for (i = 0; i < DRM_COLOR_ENCODING_MAX; i++) {
547 if ((supported_encodings & BIT(i)) == 0)
548 continue;
549
550 enum_list[len].type = i;
551 enum_list[len].name = color_encoding_name[i];
552 len++;
553 }
554
555 prop = drm_property_create_enum(dev, 0, "COLOR_ENCODING",
556 enum_list, len);
557 if (!prop)
558 return -ENOMEM;
559 plane->color_encoding_property = prop;
560 drm_object_attach_property(&plane->base, prop, default_encoding);
561 if (plane->state)
562 plane->state->color_encoding = default_encoding;
563
564 len = 0;
565 for (i = 0; i < DRM_COLOR_RANGE_MAX; i++) {
566 if ((supported_ranges & BIT(i)) == 0)
567 continue;
568
569 enum_list[len].type = i;
570 enum_list[len].name = color_range_name[i];
571 len++;
572 }
573
574 prop = drm_property_create_enum(dev, 0, "COLOR_RANGE",
575 enum_list, len);
576 if (!prop)
577 return -ENOMEM;
578 plane->color_range_property = prop;
579 drm_object_attach_property(&plane->base, prop, default_range);
580 if (plane->state)
581 plane->state->color_range = default_range;
582
583 return 0;
584}
585EXPORT_SYMBOL(drm_plane_create_color_properties);
586
587
588
589
590
591
592
593
594
595
596
597
598int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests)
599{
600 const struct drm_color_lut *entry;
601 int i;
602
603 if (!lut || !tests)
604 return 0;
605
606 entry = lut->data;
607 for (i = 0; i < drm_color_lut_size(lut); i++) {
608 if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
609 if (entry[i].red != entry[i].blue ||
610 entry[i].red != entry[i].green) {
611 DRM_DEBUG_KMS("All LUT entries must have equal r/g/b\n");
612 return -EINVAL;
613 }
614 }
615
616 if (i > 0 && tests & DRM_COLOR_LUT_NON_DECREASING) {
617 if (entry[i].red < entry[i - 1].red ||
618 entry[i].green < entry[i - 1].green ||
619 entry[i].blue < entry[i - 1].blue) {
620 DRM_DEBUG_KMS("LUT entries must never decrease.\n");
621 return -EINVAL;
622 }
623 }
624 }
625
626 return 0;
627}
628EXPORT_SYMBOL(drm_color_lut_check);
629