• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #include <linux/uaccess.h>
24 
25 #include <drm/drm_color_mgmt.h>
26 #include <drm/drm_crtc.h>
27 #include <drm/drm_device.h>
28 #include <drm/drm_drv.h>
29 #include <drm/drm_print.h>
30 
31 #include "drm_crtc_internal.h"
32 
33 /**
34  * DOC: overview
35  *
36  * Color management or color space adjustments is supported through a set of 7
37  * properties on the &drm_crtc object. They are set up by calling
38  * drm_crtc_enable_color_mgmt().
39  *
40  * "DEGAMMA_LUT”:
41  *    Blob property to set the degamma lookup table (LUT) mapping pixel data
42  *    from the framebuffer before it is given to the transformation matrix.
43  *    The data is interpreted as an array of &struct drm_color_lut elements.
44  *    Hardware might choose not to use the full precision of the LUT elements
45  *    nor use all the elements of the LUT (for example the hardware might
46  *    choose to interpolate between LUT[0] and LUT[4]).
47  *
48  *    Setting this to NULL (blob property value set to 0) means a
49  *    linear/pass-thru gamma table should be used. This is generally the
50  *    driver boot-up state too. Drivers can access this blob through
51  *    &drm_crtc_state.degamma_lut.
52  *
53  * “DEGAMMA_LUT_SIZE”:
54  *    Unsinged range property to give the size of the lookup table to be set
55  *    on the DEGAMMA_LUT property (the size depends on the underlying
56  *    hardware). If drivers support multiple LUT sizes then they should
57  *    publish the largest size, and sub-sample smaller sized LUTs (e.g. for
58  *    split-gamma modes) appropriately.
59  *
60  * “CTM”:
61  *    Blob property to set the current transformation matrix (CTM) apply to
62  *    pixel data after the lookup through the degamma LUT and before the
63  *    lookup through the cubic LUT. The data is interpreted as a struct
64  *    &drm_color_ctm.
65  *
66  *    Setting this to NULL (blob property value set to 0) means a
67  *    unit/pass-thru matrix should be used. This is generally the driver
68  *    boot-up state too. Drivers can access the blob for the color conversion
69  *    matrix through &drm_crtc_state.ctm.
70  *
71  * ”CUBIC_LUT”:
72  *    Blob property to set the cubic (3D) lookup table performing color
73  *    mapping after the transformation matrix and before the lookup through
74  *    the gamma LUT. Unlike the degamma and gamma LUTs that map color
75  *    components independently, the 3D LUT converts an input color to an
76  *    output color by indexing into the 3D table using the color components
77  *    as a 3D coordinate. The LUT is subsampled as 8-bit (or more) precision
78  *    would require too much storage space in the hardware, so the precision
79  *    of the color components is reduced before the look up, and the low
80  *    order bits may be used to interpolate between the nearest points in 3D
81  *    space.
82  *
83  *    The data is interpreted as an array of &struct drm_color_lut elements.
84  *    Hardware might choose not to use the full precision of the LUT
85  *    elements.
86  *
87  *    Setting this to NULL (blob property value set to 0) means the output
88  *    color is identical to the input color. This is generally the driver
89  *    boot-up state too. Drivers can access this blob through
90  *    &drm_crtc_state.cubic_lut.
91  *
92  * ”CUBIC_LUT_SIZE”:
93  *    Unsigned range property to give the size of the lookup table to be set
94  *    on the CUBIC_LUT property (the size depends on the underlying hardware).
95  *    If drivers support multiple LUT sizes then they should publish the
96  *    largest size, and sub-sample smaller sized LUTs appropriately.
97  *
98  * “GAMMA_LUT”:
99  *    Blob property to set the gamma lookup table (LUT) mapping pixel data
100  *    after the cubic LUT to data sent to the connector. The data is
101  *    interpreted as an array of &struct drm_color_lut elements. Hardware
102  *    might choose not to use the full precision of the LUT elements nor use
103  *    all the elements of the LUT (for example the hardware might choose to
104  *    interpolate between LUT[0] and LUT[4]).
105  *
106  *    Setting this to NULL (blob property value set to 0) means a
107  *    linear/pass-thru gamma table should be used. This is generally the
108  *    driver boot-up state too. Drivers can access this blob through
109  *    &drm_crtc_state.gamma_lut.
110  *
111  * “GAMMA_LUT_SIZE”:
112  *    Unsigned range property to give the size of the lookup table to be set
113  *    on the GAMMA_LUT property (the size depends on the underlying hardware).
114  *    If drivers support multiple LUT sizes then they should publish the
115  *    largest size, and sub-sample smaller sized LUTs (e.g. for split-gamma
116  *    modes) appropriately.
117  *
118  * There is also support for a legacy gamma table, which is set up by calling
119  * drm_mode_crtc_set_gamma_size(). Drivers which support both should use
120  * drm_atomic_helper_legacy_gamma_set() to alias the legacy gamma ramp with the
121  * "GAMMA_LUT" property above.
122  *
123  * Support for different non RGB color encodings is controlled through
124  * &drm_plane specific COLOR_ENCODING and COLOR_RANGE properties. They
125  * are set up by calling drm_plane_create_color_properties().
126  *
127  * "COLOR_ENCODING"
128  *     Optional plane enum property to support different non RGB
129  *     color encodings. The driver can provide a subset of standard
130  *     enum values supported by the DRM plane.
131  *
132  * "COLOR_RANGE"
133  *     Optional plane enum property to support different non RGB
134  *     color parameter ranges. The driver can provide a subset of
135  *     standard enum values supported by the DRM plane.
136  */
137 
138 /**
139  * drm_color_ctm_s31_32_to_qm_n
140  *
141  * @user_input: input value
142  * @m: number of integer bits, only support m <= 32, include the sign-bit
143  * @n: number of fractional bits, only support n <= 32
144  *
145  * Convert and clamp S31.32 sign-magnitude to Qm.n (signed 2's complement).
146  * The sign-bit BIT(m+n-1) and above are 0 for positive value and 1 for negative
147  * the range of value is [-2^(m-1), 2^(m-1) - 2^-n]
148  *
149  * For example
150  * A Q3.12 format number: see below
151  * - required bit: 3 + 12 = 15bits
152  * - range: [-2^2, 2^2 - 2^−15]
153  *
154  * NOTE: the m can be zero if all bit_precision are used to present fractional
155  *       bits like Q0.32
156  */
drm_color_ctm_s31_32_to_qm_n(u64 user_input,u32 m,u32 n)157 u64 drm_color_ctm_s31_32_to_qm_n(u64 user_input, u32 m, u32 n)
158 {
159     u64 mag = (user_input & ~BIT_ULL(0x3f)) >> (0x20 - n);
160     bool negative = !!(user_input & BIT_ULL(0x3f));
161     s64 val;
162 
163     WARN_ON(m > 0x20 || n > 0x20);
164 
165     val = clamp_val(mag, 0, negative ? BIT_ULL(n + m - 1) : BIT_ULL(n + m - 1) - 1);
166 
167     return negative ? -val : val;
168 }
169 EXPORT_SYMBOL(drm_color_ctm_s31_32_to_qm_n);
170 
171 /**
172  * drm_crtc_enable_color_mgmt - enable color management properties
173  * @crtc: DRM CRTC
174  * @degamma_lut_size: the size of the degamma lut (before CSC)
175  * @has_ctm: whether to attach ctm_property for CSC matrix
176  * @gamma_lut_size: the size of the gamma lut (after CSC)
177  *
178  * This function lets the driver enable the color correction
179  * properties on a CRTC. This includes 3 degamma, csc and gamma
180  * properties that userspace can set and 2 size properties to inform
181  * the userspace of the lut sizes. Each of the properties are
182  * optional. The gamma and degamma properties are only attached if
183  * their size is not 0 and ctm_property is only attached if has_ctm is
184  * true.
185  *
186  * Drivers should use drm_atomic_helper_legacy_gamma_set() to implement the
187  * legacy &drm_crtc_funcs.gamma_set callback.
188  */
drm_crtc_enable_color_mgmt(struct drm_crtc * crtc,uint degamma_lut_size,bool has_ctm,uint gamma_lut_size)189 void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, uint degamma_lut_size, bool has_ctm, uint gamma_lut_size)
190 {
191     struct drm_device *dev = crtc->dev;
192     struct drm_mode_config *config = &dev->mode_config;
193 
194     if (degamma_lut_size) {
195         drm_object_attach_property(&crtc->base, config->degamma_lut_property, 0);
196         drm_object_attach_property(&crtc->base, config->degamma_lut_size_property, degamma_lut_size);
197     }
198 
199     if (has_ctm) {
200         drm_object_attach_property(&crtc->base, config->ctm_property, 0);
201     }
202 
203     if (gamma_lut_size) {
204         drm_object_attach_property(&crtc->base, config->gamma_lut_property, 0);
205         drm_object_attach_property(&crtc->base, config->gamma_lut_size_property, gamma_lut_size);
206     }
207 }
208 EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
209 
210 /**
211  * drm_mode_crtc_set_gamma_size - set the gamma table size
212  * @crtc: CRTC to set the gamma table size for
213  * @gamma_size: size of the gamma table
214  *
215  * Drivers which support gamma tables should set this to the supported gamma
216  * table size when initializing the CRTC. Currently the drm core only supports a
217  * fixed gamma table size.
218  *
219  * Returns:
220  * Zero on success, negative errno on failure.
221  */
drm_mode_crtc_set_gamma_size(struct drm_crtc * crtc,int gamma_size)222 int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size)
223 {
224     uint16_t *r_base, *g_base, *b_base;
225     int i;
226 
227     crtc->gamma_size = gamma_size;
228 
229     crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 0x3, GFP_KERNEL);
230     if (!crtc->gamma_store) {
231         crtc->gamma_size = 0;
232         return -ENOMEM;
233     }
234 
235     r_base = crtc->gamma_store;
236     g_base = r_base + gamma_size;
237     b_base = g_base + gamma_size;
238     for (i = 0; i < gamma_size; i++) {
239         r_base[i] = i << 0x8;
240         g_base[i] = i << 0x8;
241         b_base[i] = i << 0x8;
242     }
243 
244     return 0;
245 }
246 EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
247 
248 /**
249  * drm_mode_gamma_set_ioctl - set the gamma table
250  * @dev: DRM device
251  * @data: ioctl data
252  * @file_priv: DRM file info
253  *
254  * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
255  * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
256  *
257  * Called by the user via ioctl.
258  *
259  * Returns:
260  * Zero on success, negative errno on failure.
261  */
drm_mode_gamma_set_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)262 int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
263 {
264     struct drm_mode_crtc_lut *crtc_lut = data;
265     struct drm_crtc *crtc;
266     void *r_base, *g_base, *b_base;
267     int size;
268     struct drm_modeset_acquire_ctx ctx;
269     int ret = 0;
270 
271     if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
272         return -EOPNOTSUPP;
273     }
274 
275     crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
276     if (!crtc) {
277         return -ENOENT;
278     }
279 
280     if (crtc->funcs->gamma_set == NULL) {
281         return -ENOSYS;
282     }
283 
284     /* memcpy into gamma store */
285     if (crtc_lut->gamma_size != crtc->gamma_size) {
286         return -EINVAL;
287     }
288 
289     DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
290 
291     size = crtc_lut->gamma_size * (sizeof(uint16_t));
292     r_base = crtc->gamma_store;
293     if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
294         ret = -EFAULT;
295         goto out;
296     }
297 
298     g_base = r_base + size;
299     if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
300         ret = -EFAULT;
301         goto out;
302     }
303 
304     b_base = g_base + size;
305     if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
306         ret = -EFAULT;
307         goto out;
308     }
309 
310     ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size, &ctx);
311 
312 out:
313     DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
314     return ret;
315 }
316 
317 /**
318  * drm_mode_gamma_get_ioctl - get the gamma table
319  * @dev: DRM device
320  * @data: ioctl data
321  * @file_priv: DRM file info
322  *
323  * Copy the current gamma table into the storage provided. This also provides
324  * the gamma table size the driver expects, which can be used to size the
325  * allocated storage.
326  *
327  * Called by the user via ioctl.
328  *
329  * Returns:
330  * Zero on success, negative errno on failure.
331  */
drm_mode_gamma_get_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)332 int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
333 {
334     struct drm_mode_crtc_lut *crtc_lut = data;
335     struct drm_crtc *crtc;
336     void *r_base, *g_base, *b_base;
337     int size;
338     int ret = 0;
339 
340     if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
341         return -EOPNOTSUPP;
342     }
343 
344     crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
345     if (!crtc) {
346         return -ENOENT;
347     }
348 
349     /* memcpy into gamma store */
350     if (crtc_lut->gamma_size != crtc->gamma_size) {
351         return -EINVAL;
352     }
353 
354     drm_modeset_lock(&crtc->mutex, NULL);
355     size = crtc_lut->gamma_size * (sizeof(uint16_t));
356     r_base = crtc->gamma_store;
357     if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
358         ret = -EFAULT;
359         goto out;
360     }
361 
362     g_base = r_base + size;
363     if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
364         ret = -EFAULT;
365         goto out;
366     }
367 
368     b_base = g_base + size;
369     if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
370         ret = -EFAULT;
371         goto out;
372     }
373 out:
374     drm_modeset_unlock(&crtc->mutex);
375     return ret;
376 }
377 
378 static const char *const color_encoding_name[] = {
379     [DRM_COLOR_YCBCR_BT601] = "ITU-R BT.601 YCbCr",
380     [DRM_COLOR_YCBCR_BT709] = "ITU-R BT.709 YCbCr",
381     [DRM_COLOR_YCBCR_BT2020] = "ITU-R BT.2020 YCbCr",
382 };
383 
384 static const char *const color_range_name[] = {
385     [DRM_COLOR_YCBCR_FULL_RANGE] = "YCbCr full range",
386     [DRM_COLOR_YCBCR_LIMITED_RANGE] = "YCbCr limited range",
387 };
388 
389 /**
390  * drm_get_color_encoding_name - return a string for color encoding
391  * @encoding: color encoding to compute name of
392  *
393  * In contrast to the other drm_get_*_name functions this one here returns a
394  * const pointer and hence is threadsafe.
395  */
drm_get_color_encoding_name(enum drm_color_encoding encoding)396 const char *drm_get_color_encoding_name(enum drm_color_encoding encoding)
397 {
398     if (WARN_ON(encoding >= ARRAY_SIZE(color_encoding_name))) {
399         return "unknown";
400     }
401 
402     return color_encoding_name[encoding];
403 }
404 
405 /**
406  * drm_get_color_range_name - return a string for color range
407  * @range: color range to compute name of
408  *
409  * In contrast to the other drm_get_*_name functions this one here returns a
410  * const pointer and hence is threadsafe.
411  */
drm_get_color_range_name(enum drm_color_range range)412 const char *drm_get_color_range_name(enum drm_color_range range)
413 {
414     if (WARN_ON(range >= ARRAY_SIZE(color_range_name))) {
415         return "unknown";
416     }
417 
418     return color_range_name[range];
419 }
420 
421 /**
422  * drm_plane_create_color_properties - color encoding related plane properties
423  * @plane: plane object
424  * @supported_encodings: bitfield indicating supported color encodings
425  * @supported_ranges: bitfileld indicating supported color ranges
426  * @default_encoding: default color encoding
427  * @default_range: default color range
428  *
429  * Create and attach plane specific COLOR_ENCODING and COLOR_RANGE
430  * properties to @plane. The supported encodings and ranges should
431  * be provided in supported_encodings and supported_ranges bitmasks.
432  * Each bit set in the bitmask indicates that its number as enum
433  * value is supported.
434  */
drm_plane_create_color_properties(struct drm_plane * plane,u32 supported_encodings,u32 supported_ranges,enum drm_color_encoding default_encoding,enum drm_color_range default_range)435 int drm_plane_create_color_properties(struct drm_plane *plane, u32 supported_encodings, u32 supported_ranges,
436                                       enum drm_color_encoding default_encoding, enum drm_color_range default_range)
437 {
438     struct drm_device *dev = plane->dev;
439     struct drm_property *prop;
440     struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX, DRM_COLOR_RANGE_MAX)];
441     int i, len;
442 
443     if (WARN_ON(supported_encodings == 0 || (supported_encodings & -BIT(DRM_COLOR_ENCODING_MAX)) != 0 ||
444                 (supported_encodings & BIT(default_encoding)) == 0)) {
445         return -EINVAL;
446     }
447 
448     if (WARN_ON(supported_ranges == 0 || (supported_ranges & -BIT(DRM_COLOR_RANGE_MAX)) != 0 ||
449                 (supported_ranges & BIT(default_range)) == 0)) {
450         return -EINVAL;
451     }
452 
453     len = 0;
454     for (i = 0; i < DRM_COLOR_ENCODING_MAX; i++) {
455         if ((supported_encodings & BIT(i)) == 0) {
456             continue;
457         }
458 
459         enum_list[len].type = i;
460         enum_list[len].name = color_encoding_name[i];
461         len++;
462     }
463 
464     prop = drm_property_create_enum(dev, 0, "COLOR_ENCODING", enum_list, len);
465     if (!prop) {
466         return -ENOMEM;
467     }
468     plane->color_encoding_property = prop;
469     drm_object_attach_property(&plane->base, prop, default_encoding);
470     if (plane->state) {
471         plane->state->color_encoding = default_encoding;
472     }
473 
474     len = 0;
475     for (i = 0; i < DRM_COLOR_RANGE_MAX; i++) {
476         if ((supported_ranges & BIT(i)) == 0) {
477             continue;
478         }
479 
480         enum_list[len].type = i;
481         enum_list[len].name = color_range_name[i];
482         len++;
483     }
484 
485     prop = drm_property_create_enum(dev, 0, "COLOR_RANGE", enum_list, len);
486     if (!prop) {
487         return -ENOMEM;
488     }
489     plane->color_range_property = prop;
490     drm_object_attach_property(&plane->base, prop, default_range);
491     if (plane->state) {
492         plane->state->color_range = default_range;
493     }
494 
495     return 0;
496 }
497 EXPORT_SYMBOL(drm_plane_create_color_properties);
498 
499 /**
500  * drm_color_lut_check - check validity of lookup table
501  * @lut: property blob containing LUT to check
502  * @tests: bitmask of tests to run
503  *
504  * Helper to check whether a userspace-provided lookup table is valid and
505  * satisfies hardware requirements.  Drivers pass a bitmask indicating which of
506  * the tests in &drm_color_lut_tests should be performed.
507  *
508  * Returns 0 on success, -EINVAL on failure.
509  */
drm_color_lut_check(const struct drm_property_blob * lut,u32 tests)510 int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests)
511 {
512     const struct drm_color_lut *entry;
513     int i;
514 
515     if (!lut || !tests) {
516         return 0;
517     }
518 
519     entry = lut->data;
520     for (i = 0; i < drm_color_lut_size(lut); i++) {
521         if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
522             if (entry[i].red != entry[i].blue || entry[i].red != entry[i].green) {
523                 DRM_DEBUG_KMS("All LUT entries must have equal r/g/b\n");
524                 return -EINVAL;
525             }
526         }
527 
528         if (i > 0 && (tests & DRM_COLOR_LUT_NON_DECREASING)) {
529             if (entry[i].red < entry[i - 1].red || entry[i].green < entry[i - 1].green ||
530                 entry[i].blue < entry[i - 1].blue) {
531                 DRM_DEBUG_KMS("LUT entries must never decrease.\n");
532                 return -EINVAL;
533             }
534         }
535     }
536 
537     return 0;
538 }
539 EXPORT_SYMBOL(drm_color_lut_check);
540