File Index
Files and directories
Sign in
FILE

drm_color_mgmt.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/drm_color_mgmt.c C
  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/export.h>
 24#include <linux/uaccess.h>
 25
 26#include <drm/drm_atomic.h>
 27#include <drm/drm_color_mgmt.h>
 28#include <drm/drm_crtc.h>
 29#include <drm/drm_device.h>
 30#include <drm/drm_drv.h>
 31#include <drm/drm_print.h>
 32#include <kunit/visibility.h>
 33
 34#include "drm_crtc_internal.h"
 35
 36/**
 37 * DOC: overview
 38 *
 39 * Color management or color space adjustments is supported through a set of 5
 40 * properties on the &drm_crtc object. They are set up by calling
 41 * drm_crtc_enable_color_mgmt().
 42 *
 43 * "DEGAMMA_LUT”:
 44 *	Blob property to set the degamma lookup table (LUT) mapping pixel data
 45 *	from the framebuffer before it is given to the transformation matrix.
 46 *	The data is interpreted as an array of &struct drm_color_lut elements.
 47 *	Hardware might choose not to use the full precision of the LUT elements
 48 *	nor use all the elements of the LUT (for example the hardware might
 49 *	choose to interpolate between LUT[0] and LUT[4]).
 50 *
 51 *	Setting this to NULL (blob property value set to 0) means a
 52 *	linear/pass-thru gamma table should be used. This is generally the
 53 *	driver boot-up state too. Drivers can access this blob through
 54 *	&drm_crtc_state.degamma_lut.
 55 *
 56 * “DEGAMMA_LUT_SIZE”:
 57 *	Unsinged range property to give the size of the lookup table to be set
 58 *	on the DEGAMMA_LUT property (the size depends on the underlying
 59 *	hardware). If drivers support multiple LUT sizes then they should
 60 *	publish the largest size, and sub-sample smaller sized LUTs (e.g. for
 61 *	split-gamma modes) appropriately.
 62 *
 63 * “CTM”:
 64 *	Blob property to set the current transformation matrix (CTM) apply to
 65 *	pixel data after the lookup through the degamma LUT and before the
 66 *	lookup through the gamma LUT. The data is interpreted as a struct
 67 *	&drm_color_ctm.
 68 *
 69 *	Setting this to NULL (blob property value set to 0) means a
 70 *	unit/pass-thru matrix should be used. This is generally the driver
 71 *	boot-up state too. Drivers can access the blob for the color conversion
 72 *	matrix through &drm_crtc_state.ctm.
 73 *
 74 * “GAMMA_LUT”:
 75 *	Blob property to set the gamma lookup table (LUT) mapping pixel data
 76 *	after the transformation matrix to data sent to the connector. The
 77 *	data is interpreted as an array of &struct drm_color_lut elements.
 78 *	Hardware might choose not to use the full precision of the LUT elements
 79 *	nor use all the elements of the LUT (for example the hardware might
 80 *	choose to interpolate between LUT[0] and LUT[4]).
 81 *
 82 *	Setting this to NULL (blob property value set to 0) means a
 83 *	linear/pass-thru gamma table should be used. This is generally the
 84 *	driver boot-up state too. Drivers can access this blob through
 85 *	&drm_crtc_state.gamma_lut.
 86 *
 87 *	Note that for mostly historical reasons stemming from Xorg heritage,
 88 *	this is also used to store the color map (also sometimes color lut, CLUT
 89 *	or color palette) for indexed formats like DRM_FORMAT_C8.
 90 *
 91 * “GAMMA_LUT_SIZE”:
 92 *	Unsigned range property to give the size of the lookup table to be set
 93 *	on the GAMMA_LUT property (the size depends on the underlying hardware).
 94 *	If drivers support multiple LUT sizes then they should publish the
 95 *	largest size, and sub-sample smaller sized LUTs (e.g. for split-gamma
 96 *	modes) appropriately.
 97 *
 98 * There is also support for a legacy gamma table, which is set up by calling
 99 * drm_mode_crtc_set_gamma_size(). The DRM core will then alias the legacy gamma
100 * ramp with "GAMMA_LUT" or, if that is unavailable, "DEGAMMA_LUT".
101 *
102 * Support for different non RGB color encodings is controlled through
103 * &drm_plane specific COLOR_ENCODING and COLOR_RANGE properties. They
104 * are set up by calling drm_plane_create_color_properties().
105 *
106 * "COLOR_ENCODING":
107 * 	Optional plane enum property to support different non RGB
108 * 	color encodings. The driver can provide a subset of standard
109 * 	enum values supported by the DRM plane.
110 *
111 * "COLOR_RANGE":
112 * 	Optional plane enum property to support different non RGB
113 * 	color parameter ranges. The driver can provide a subset of
114 * 	standard enum values supported by the DRM plane.
115 */
116
117/**
118 * drm_color_ctm_s31_32_to_qm_n
119 *
120 * @user_input: input value
121 * @m: number of integer bits, only support m <= 32, include the sign-bit
122 * @n: number of fractional bits, only support n <= 32
123 *
124 * Convert and clamp S31.32 sign-magnitude to Qm.n (signed 2's complement).
125 * The sign-bit BIT(m+n-1) and above are 0 for positive value and 1 for negative
126 * the range of value is [-2^(m-1), 2^(m-1) - 2^-n]
127 *
128 * For example
129 * A Q3.12 format number:
130 * - required bit: 3 + 12 = 15bits
131 * - range: [-2^2, 2^2 - 2^−15]
132 *
133 * NOTE: the m can be zero if all bit_precision are used to present fractional
134 *       bits like Q0.32
135 */
136u64 drm_color_ctm_s31_32_to_qm_n(u64 user_input, u32 m, u32 n)
137{
138	u64 mag = (user_input & ~BIT_ULL(63)) >> (32 - n);
139	bool negative = !!(user_input & BIT_ULL(63));
140	s64 val;
141
142	WARN_ON(m > 32 || n > 32);
143
144	val = clamp_val(mag, 0, negative ?
145				BIT_ULL(n + m - 1) : BIT_ULL(n + m - 1) - 1);
146
147	return negative ? -val : val;
148}
149EXPORT_SYMBOL(drm_color_ctm_s31_32_to_qm_n);
150
151/**
152 * drm_crtc_enable_color_mgmt - enable color management properties
153 * @crtc: DRM CRTC
154 * @degamma_lut_size: the size of the degamma lut (before CSC)
155 * @has_ctm: whether to attach ctm_property for CSC matrix
156 * @gamma_lut_size: the size of the gamma lut (after CSC)
157 *
158 * This function lets the driver enable the color correction
159 * properties on a CRTC. This includes 3 degamma, csc and gamma
160 * properties that userspace can set and 2 size properties to inform
161 * the userspace of the lut sizes. Each of the properties are
162 * optional. The gamma and degamma properties are only attached if
163 * their size is not 0 and ctm_property is only attached if has_ctm is
164 * true.
165 */
166void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
167				uint degamma_lut_size,
168				bool has_ctm,
169				uint gamma_lut_size)
170{
171	struct drm_device *dev = crtc->dev;
172	struct drm_mode_config *config = &dev->mode_config;
173
174	if (degamma_lut_size) {
175		drm_object_attach_property(&crtc->base,
176					   config->degamma_lut_property, 0);
177		drm_object_attach_property(&crtc->base,
178					   config->degamma_lut_size_property,
179					   degamma_lut_size);
180	}
181
182	if (has_ctm)
183		drm_object_attach_property(&crtc->base,
184					   config->ctm_property, 0);
185
186	if (gamma_lut_size) {
187		drm_object_attach_property(&crtc->base,
188					   config->gamma_lut_property, 0);
189		drm_object_attach_property(&crtc->base,
190					   config->gamma_lut_size_property,
191					   gamma_lut_size);
192	}
193}
194EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
195
196/**
197 * drm_mode_crtc_set_gamma_size - set the gamma table size
198 * @crtc: CRTC to set the gamma table size for
199 * @gamma_size: size of the gamma table
200 *
201 * Drivers which support gamma tables should set this to the supported gamma
202 * table size when initializing the CRTC. Currently the drm core only supports a
203 * fixed gamma table size.
204 *
205 * Returns:
206 * Zero on success, negative errno on failure.
207 */
208int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
209				 int gamma_size)
210{
211	uint16_t *r_base, *g_base, *b_base;
212	int i;
213
214	crtc->gamma_size = gamma_size;
215
216	crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
217				    GFP_KERNEL);
218	if (!crtc->gamma_store) {
219		crtc->gamma_size = 0;
220		return -ENOMEM;
221	}
222
223	r_base = crtc->gamma_store;
224	g_base = r_base + gamma_size;
225	b_base = g_base + gamma_size;
226	for (i = 0; i < gamma_size; i++) {
227		r_base[i] = i << 8;
228		g_base[i] = i << 8;
229		b_base[i] = i << 8;
230	}
231
232
233	return 0;
234}
235EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
236
237/**
238 * drm_crtc_supports_legacy_gamma - does the crtc support legacy gamma correction table
239 * @crtc: CRTC object
240 *
241 * Returns true/false if the given crtc supports setting the legacy gamma
242 * correction table.
243 */
244static bool drm_crtc_supports_legacy_gamma(struct drm_crtc *crtc)
245{
246	u32 gamma_id = crtc->dev->mode_config.gamma_lut_property->base.id;
247	u32 degamma_id = crtc->dev->mode_config.degamma_lut_property->base.id;
248
249	if (!crtc->gamma_size)
250		return false;
251
252	if (crtc->funcs->gamma_set)
253		return true;
254
255	return !!(drm_mode_obj_find_prop_id(&crtc->base, gamma_id) ||
256		  drm_mode_obj_find_prop_id(&crtc->base, degamma_id));
257}
258
259/**
260 * drm_crtc_legacy_gamma_set - set the legacy gamma correction table
261 * @crtc: CRTC object
262 * @red: red correction table
263 * @green: green correction table
264 * @blue: blue correction table
265 * @size: size of the tables
266 * @ctx: lock acquire context
267 *
268 * Implements support for legacy gamma correction table for drivers
269 * that have set drm_crtc_funcs.gamma_set or that support color management
270 * through the DEGAMMA_LUT/GAMMA_LUT properties. See
271 * drm_crtc_enable_color_mgmt() and the containing chapter for
272 * how the atomic color management and gamma tables work.
273 *
274 * This function sets the gamma using drm_crtc_funcs.gamma_set if set, or
275 * alternatively using crtc color management properties.
276 */
277static int drm_crtc_legacy_gamma_set(struct drm_crtc *crtc,
278				     u16 *red, u16 *green, u16 *blue,
279				     u32 size,
280				     struct drm_modeset_acquire_ctx *ctx)
281{
282	struct drm_device *dev = crtc->dev;
283	struct drm_atomic_state *state;
284	struct drm_crtc_state *crtc_state;
285	struct drm_property_blob *blob;
286	struct drm_color_lut *blob_data;
287	u32 gamma_id = dev->mode_config.gamma_lut_property->base.id;
288	u32 degamma_id = dev->mode_config.degamma_lut_property->base.id;
289	bool use_gamma_lut;
290	int i, ret = 0;
291	bool replaced;
292
293	if (crtc->funcs->gamma_set)
294		return crtc->funcs->gamma_set(crtc, red, green, blue, size, ctx);
295
296	if (drm_mode_obj_find_prop_id(&crtc->base, gamma_id))
297		use_gamma_lut = true;
298	else if (drm_mode_obj_find_prop_id(&crtc->base, degamma_id))
299		use_gamma_lut = false;
300	else
301		return -ENODEV;
302
303	state = drm_atomic_state_alloc(crtc->dev);
304	if (!state)
305		return -ENOMEM;
306
307	blob = drm_property_create_blob(dev,
308					sizeof(struct drm_color_lut) * size,
309					NULL);
310	if (IS_ERR(blob)) {
311		ret = PTR_ERR(blob);
312		blob = NULL;
313		goto fail;
314	}
315
316	/* Prepare GAMMA_LUT with the legacy values. */
317	blob_data = blob->data;
318	for (i = 0; i < size; i++) {
319		blob_data[i].red = red[i];
320		blob_data[i].green = green[i];
321		blob_data[i].blue = blue[i];
322	}
323
324	state->acquire_ctx = ctx;
325	crtc_state = drm_atomic_get_crtc_state(state, crtc);
326	if (IS_ERR(crtc_state)) {
327		ret = PTR_ERR(crtc_state);
328		goto fail;
329	}
330
331	/* Set GAMMA_LUT and reset DEGAMMA_LUT and CTM */
332	replaced = drm_property_replace_blob(&crtc_state->degamma_lut,
333					     use_gamma_lut ? NULL : blob);
334	replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
335	replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
336					      use_gamma_lut ? blob : NULL);
337	crtc_state->color_mgmt_changed |= replaced;
338
339	ret = drm_atomic_commit(state);
340
341fail:
342	drm_atomic_state_put(state);
343	drm_property_blob_put(blob);
344	return ret;
345}
346
347/**
348 * drm_mode_gamma_set_ioctl - set the gamma table
349 * @dev: DRM device
350 * @data: ioctl data
351 * @file_priv: DRM file info
352 *
353 * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
354 * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
355 *
356 * Called by the user via ioctl.
357 *
358 * Returns:
359 * Zero on success, negative errno on failure.
360 */
361int drm_mode_gamma_set_ioctl(struct drm_device *dev,
362			     void *data, struct drm_file *file_priv)
363{
364	struct drm_mode_crtc_lut *crtc_lut = data;
365	struct drm_crtc *crtc;
366	void *r_base, *g_base, *b_base;
367	int size;
368	struct drm_modeset_acquire_ctx ctx;
369	int ret = 0;
370
371	if (!drm_core_check_feature(dev, DRIVER_MODESET))
372		return -EOPNOTSUPP;
373
374	crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
375	if (!crtc)
376		return -ENOENT;
377
378	if (!drm_crtc_supports_legacy_gamma(crtc))
379		return -ENOSYS;
380
381	/* memcpy into gamma store */
382	if (crtc_lut->gamma_size != crtc->gamma_size)
383		return -EINVAL;
384
385	DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
386
387	size = crtc_lut->gamma_size * (sizeof(uint16_t));
388	r_base = crtc->gamma_store;
389	if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
390		ret = -EFAULT;
391		goto out;
392	}
393
394	g_base = r_base + size;
395	if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
396		ret = -EFAULT;
397		goto out;
398	}
399
400	b_base = g_base + size;
401	if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
402		ret = -EFAULT;
403		goto out;
404	}
405
406	ret = drm_crtc_legacy_gamma_set(crtc, r_base, g_base, b_base,
407					crtc->gamma_size, &ctx);
408
409out:
410	DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
411	return ret;
412
413}
414
415/**
416 * drm_mode_gamma_get_ioctl - get the gamma table
417 * @dev: DRM device
418 * @data: ioctl data
419 * @file_priv: DRM file info
420 *
421 * Copy the current gamma table into the storage provided. This also provides
422 * the gamma table size the driver expects, which can be used to size the
423 * allocated storage.
424 *
425 * Called by the user via ioctl.
426 *
427 * Returns:
428 * Zero on success, negative errno on failure.
429 */
430int drm_mode_gamma_get_ioctl(struct drm_device *dev,
431			     void *data, struct drm_file *file_priv)
432{
433	struct drm_mode_crtc_lut *crtc_lut = data;
434	struct drm_crtc *crtc;
435	void *r_base, *g_base, *b_base;
436	int size;
437	int ret = 0;
438
439	if (!drm_core_check_feature(dev, DRIVER_MODESET))
440		return -EOPNOTSUPP;
441
442	crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
443	if (!crtc)
444		return -ENOENT;
445
446	/* memcpy into gamma store */
447	if (crtc_lut->gamma_size != crtc->gamma_size)
448		return -EINVAL;
449
450	drm_modeset_lock(&crtc->mutex, NULL);
451	size = crtc_lut->gamma_size * (sizeof(uint16_t));
452	r_base = crtc->gamma_store;
453	if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
454		ret = -EFAULT;
455		goto out;
456	}
457
458	g_base = r_base + size;
459	if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
460		ret = -EFAULT;
461		goto out;
462	}
463
464	b_base = g_base + size;
465	if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
466		ret = -EFAULT;
467		goto out;
468	}
469out:
470	drm_modeset_unlock(&crtc->mutex);
471	return ret;
472}
473
474static const char * const color_encoding_name[] = {
475	[DRM_COLOR_YCBCR_BT601] = "ITU-R BT.601 YCbCr",
476	[DRM_COLOR_YCBCR_BT709] = "ITU-R BT.709 YCbCr",
477	[DRM_COLOR_YCBCR_BT2020] = "ITU-R BT.2020 YCbCr",
478};
479
480static const char * const color_range_name[] = {
481	[DRM_COLOR_YCBCR_FULL_RANGE] = "YCbCr full range",
482	[DRM_COLOR_YCBCR_LIMITED_RANGE] = "YCbCr limited range",
483};
484
485/**
486 * drm_get_color_encoding_name - return a string for color encoding
487 * @encoding: color encoding to compute name of
488 *
489 * In contrast to the other drm_get_*_name functions this one here returns a
490 * const pointer and hence is threadsafe.
491 */
492const char *drm_get_color_encoding_name(enum drm_color_encoding encoding)
493{
494	if (WARN_ON(encoding >= ARRAY_SIZE(color_encoding_name)))
495		return "unknown";
496
497	return color_encoding_name[encoding];
498}
499EXPORT_SYMBOL_IF_KUNIT(drm_get_color_encoding_name);
500
501/**
502 * drm_get_color_range_name - return a string for color range
503 * @range: color range to compute name of
504 *
505 * In contrast to the other drm_get_*_name functions this one here returns a
506 * const pointer and hence is threadsafe.
507 */
508const char *drm_get_color_range_name(enum drm_color_range range)
509{
510	if (WARN_ON(range >= ARRAY_SIZE(color_range_name)))
511		return "unknown";
512
513	return color_range_name[range];
514}
515EXPORT_SYMBOL_IF_KUNIT(drm_get_color_range_name);
516
517/**
518 * drm_plane_create_color_properties - color encoding related plane properties
519 * @plane: plane object
520 * @supported_encodings: bitfield indicating supported color encodings
521 * @supported_ranges: bitfileld indicating supported color ranges
522 * @default_encoding: default color encoding
523 * @default_range: default color range
524 *
525 * Create and attach plane specific COLOR_ENCODING and COLOR_RANGE
526 * properties to @plane. The supported encodings and ranges should
527 * be provided in supported_encodings and supported_ranges bitmasks.
528 * Each bit set in the bitmask indicates that its number as enum
529 * value is supported.
530 */
531int drm_plane_create_color_properties(struct drm_plane *plane,
532				      u32 supported_encodings,
533				      u32 supported_ranges,
534				      enum drm_color_encoding default_encoding,
535				      enum drm_color_range default_range)
536{
537	struct drm_device *dev = plane->dev;
538	struct drm_property *prop;
539	struct drm_prop_enum_list enum_list[MAX_T(int, DRM_COLOR_ENCODING_MAX,
540						       DRM_COLOR_RANGE_MAX)];
541	int i, len;
542
543	if (WARN_ON(supported_encodings == 0 ||
544		    (supported_encodings & -BIT(DRM_COLOR_ENCODING_MAX)) != 0 ||
545		    (supported_encodings & BIT(default_encoding)) == 0))
546		return -EINVAL;
547
548	if (WARN_ON(supported_ranges == 0 ||
549		    (supported_ranges & -BIT(DRM_COLOR_RANGE_MAX)) != 0 ||
550		    (supported_ranges & BIT(default_range)) == 0))
551		return -EINVAL;
552
553	len = 0;
554	for (i = 0; i < DRM_COLOR_ENCODING_MAX; i++) {
555		if ((supported_encodings & BIT(i)) == 0)
556			continue;
557
558		enum_list[len].type = i;
559		enum_list[len].name = color_encoding_name[i];
560		len++;
561	}
562
563	prop = drm_property_create_enum(dev, 0, "COLOR_ENCODING",
564					enum_list, len);
565	if (!prop)
566		return -ENOMEM;
567	plane->color_encoding_property = prop;
568	drm_object_attach_property(&plane->base, prop, default_encoding);
569	if (plane->state)
570		plane->state->color_encoding = default_encoding;
571
572	len = 0;
573	for (i = 0; i < DRM_COLOR_RANGE_MAX; i++) {
574		if ((supported_ranges & BIT(i)) == 0)
575			continue;
576
577		enum_list[len].type = i;
578		enum_list[len].name = color_range_name[i];
579		len++;
580	}
581
582	prop = drm_property_create_enum(dev, 0, "COLOR_RANGE",
583					enum_list, len);
584	if (!prop)
585		return -ENOMEM;
586	plane->color_range_property = prop;
587	drm_object_attach_property(&plane->base, prop, default_range);
588	if (plane->state)
589		plane->state->color_range = default_range;
590
591	return 0;
592}
593EXPORT_SYMBOL(drm_plane_create_color_properties);
594
595/**
596 * drm_color_lut_check - check validity of lookup table
597 * @lut: property blob containing LUT to check
598 * @tests: bitmask of tests to run
599 *
600 * Helper to check whether a userspace-provided lookup table is valid and
601 * satisfies hardware requirements.  Drivers pass a bitmask indicating which of
602 * the tests in &drm_color_lut_tests should be performed.
603 *
604 * Returns 0 on success, -EINVAL on failure.
605 */
606int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests)
607{
608	const struct drm_color_lut *entry;
609	int i;
610
611	if (!lut || !tests)
612		return 0;
613
614	entry = lut->data;
615	for (i = 0; i < drm_color_lut_size(lut); i++) {
616		if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
617			if (entry[i].red != entry[i].blue ||
618			    entry[i].red != entry[i].green) {
619				DRM_DEBUG_KMS("All LUT entries must have equal r/g/b\n");
620				return -EINVAL;
621			}
622		}
623
624		if (i > 0 && tests & DRM_COLOR_LUT_NON_DECREASING) {
625			if (entry[i].red < entry[i - 1].red ||
626			    entry[i].green < entry[i - 1].green ||
627			    entry[i].blue < entry[i - 1].blue) {
628				DRM_DEBUG_KMS("LUT entries must never decrease.\n");
629				return -EINVAL;
630			}
631		}
632	}
633
634	return 0;
635}
636EXPORT_SYMBOL(drm_color_lut_check);
637
638/*
639 * Gamma-LUT programming
640 */
641
642/**
643 * drm_crtc_load_gamma_888 - Programs gamma ramp for RGB888-like formats
644 * @crtc: The displaying CRTC
645 * @lut: The gamma ramp to program
646 * @set_gamma: Callback for programming the hardware gamma LUT
647 *
648 * Programs the gamma ramp specified in @lut to hardware. The input gamma
649 * ramp must have 256 entries per color component.
650 */
651void drm_crtc_load_gamma_888(struct drm_crtc *crtc, const struct drm_color_lut *lut,
652			     drm_crtc_set_lut_func set_gamma)
653{
654	unsigned int i;
655
656	for (i = 0; i < 256; ++i)
657		set_gamma(crtc, i, lut[i].red, lut[i].green, lut[i].blue);
658}
659EXPORT_SYMBOL(drm_crtc_load_gamma_888);
660
661/**
662 * drm_crtc_load_gamma_565_from_888 - Programs gamma ramp for RGB565-like formats
663 * @crtc: The displaying CRTC
664 * @lut: The gamma ramp to program
665 * @set_gamma: Callback for programming the hardware gamma LUT
666 *
667 * Programs the gamma ramp specified in @lut to hardware. The input gamma
668 * ramp must have 256 entries per color component. The helper interpolates
669 * the individual color components to reduce the number of entries to 5/6/5.
670 */
671void drm_crtc_load_gamma_565_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut,
672				      drm_crtc_set_lut_func set_gamma)
673{
674	unsigned int i;
675	u16 r, g, b;
676
677	for (i = 0; i < 32; ++i) {
678		r = lut[i * 8 + i / 4].red;
679		g = lut[i * 4 + i / 16].green;
680		b = lut[i * 8 + i / 4].blue;
681		set_gamma(crtc, i, r, g, b);
682	}
683	/* Green has one more bit, so add padding with 0 for red and blue. */
684	for (i = 32; i < 64; ++i) {
685		g = lut[i * 4 + i / 16].green;
686		set_gamma(crtc, i, 0, g, 0);
687	}
688}
689EXPORT_SYMBOL(drm_crtc_load_gamma_565_from_888);
690
691/**
692 * drm_crtc_load_gamma_555_from_888 - Programs gamma ramp for RGB555-like formats
693 * @crtc: The displaying CRTC
694 * @lut: The gamma ramp to program
695 * @set_gamma: Callback for programming the hardware gamma LUT
696 *
697 * Programs the gamma ramp specified in @lut to hardware. The input gamma
698 * ramp must have 256 entries per color component. The helper interpolates
699 * the individual color components to reduce the number of entries to 5/5/5.
700 */
701void drm_crtc_load_gamma_555_from_888(struct drm_crtc *crtc, const struct drm_color_lut *lut,
702				      drm_crtc_set_lut_func set_gamma)
703{
704	unsigned int i;
705	u16 r, g, b;
706
707	for (i = 0; i < 32; ++i) {
708		r = lut[i * 8 + i / 4].red;
709		g = lut[i * 8 + i / 4].green;
710		b = lut[i * 8 + i / 4].blue;
711		set_gamma(crtc, i, r, g, b);
712	}
713}
714EXPORT_SYMBOL(drm_crtc_load_gamma_555_from_888);
715
716static void fill_gamma_888(struct drm_crtc *crtc, unsigned int i, u16 r, u16 g, u16 b,
717			   drm_crtc_set_lut_func set_gamma)
718{
719	r = (r << 8) | r;
720	g = (g << 8) | g;
721	b = (b << 8) | b;
722
723	set_gamma(crtc, i, r, g, b);
724}
725
726/**
727 * drm_crtc_fill_gamma_888 - Programs a default gamma ramp for RGB888-like formats
728 * @crtc: The displaying CRTC
729 * @set_gamma: Callback for programming the hardware gamma LUT
730 *
731 * Programs a default gamma ramp to hardware.
732 */
733void drm_crtc_fill_gamma_888(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma)
734{
735	unsigned int i;
736
737	for (i = 0; i < 256; ++i)
738		fill_gamma_888(crtc, i, i, i, i, set_gamma);
739}
740EXPORT_SYMBOL(drm_crtc_fill_gamma_888);
741
742static void fill_gamma_565(struct drm_crtc *crtc, unsigned int i, u16 r, u16 g, u16 b,
743			   drm_crtc_set_lut_func set_gamma)
744{
745	r = (r << 11) | (r << 6) | (r << 1) | (r >> 4);
746	g = (g << 10) | (g << 4) | (g >> 2);
747	b = (b << 11) | (b << 6) | (b << 1) | (b >> 4);
748
749	set_gamma(crtc, i, r, g, b);
750}
751
752/**
753 * drm_crtc_fill_gamma_565 - Programs a default gamma ramp for RGB565-like formats
754 * @crtc: The displaying CRTC
755 * @set_gamma: Callback for programming the hardware gamma LUT
756 *
757 * Programs a default gamma ramp to hardware.
758 */
759void drm_crtc_fill_gamma_565(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma)
760{
761	unsigned int i;
762
763	for (i = 0; i < 32; ++i)
764		fill_gamma_565(crtc, i, i, i, i, set_gamma);
765	/* Green has one more bit, so add padding with 0 for red and blue. */
766	for (i = 32; i < 64; ++i)
767		fill_gamma_565(crtc, i, 0, i, 0, set_gamma);
768}
769EXPORT_SYMBOL(drm_crtc_fill_gamma_565);
770
771static void fill_gamma_555(struct drm_crtc *crtc, unsigned int i, u16 r, u16 g, u16 b,
772			   drm_crtc_set_lut_func set_gamma)
773{
774	r = (r << 11) | (r << 6) | (r << 1) | (r >> 4);
775	g = (g << 11) | (g << 6) | (g << 1) | (g >> 4);
776	b = (b << 11) | (b << 6) | (b << 1) | (r >> 4);
777
778	set_gamma(crtc, i, r, g, b);
779}
780
781/**
782 * drm_crtc_fill_gamma_555 - Programs a default gamma ramp for RGB555-like formats
783 * @crtc: The displaying CRTC
784 * @set_gamma: Callback for programming the hardware gamma LUT
785 *
786 * Programs a default gamma ramp to hardware.
787 */
788void drm_crtc_fill_gamma_555(struct drm_crtc *crtc, drm_crtc_set_lut_func set_gamma)
789{
790	unsigned int i;
791
792	for (i = 0; i < 32; ++i)
793		fill_gamma_555(crtc, i, i, i, i, set_gamma);
794}
795EXPORT_SYMBOL(drm_crtc_fill_gamma_555);
796
797/*
798 * Color-LUT programming
799 */
800
801/**
802 * drm_crtc_load_palette_8 - Programs palette for C8-like formats
803 * @crtc: The displaying CRTC
804 * @lut: The palette to program
805 * @set_palette: Callback for programming the hardware palette
806 *
807 * Programs the palette specified in @lut to hardware. The input palette
808 * must have 256 entries per color component.
809 */
810void drm_crtc_load_palette_8(struct drm_crtc *crtc, const struct drm_color_lut *lut,
811			     drm_crtc_set_lut_func set_palette)
812{
813	unsigned int i;
814
815	for (i = 0; i < 256; ++i)
816		set_palette(crtc, i, lut[i].red, lut[i].green, lut[i].blue);
817}
818EXPORT_SYMBOL(drm_crtc_load_palette_8);
819
820static void fill_palette_332(struct drm_crtc *crtc, u16 r, u16 g, u16 b,
821			     drm_crtc_set_lut_func set_palette)
822{
823	unsigned int i = (r << 5) | (g << 2) | b; /* 8-bit palette index */
824
825	/* Expand R (3-bit) G (3-bit) and B (2-bit) values to 16-bit values */
826	r = (r << 13) | (r << 10) | (r << 7) | (r << 4) | (r << 1) | (r >> 2);
827	g = (g << 13) | (g << 10) | (g << 7) | (g << 4) | (g << 1) | (g >> 2);
828	b = (b << 14) | (b << 12) | (b << 10) | (b << 8) | (b << 6) | (b << 4) | (b << 2) | b;
829
830	set_palette(crtc, i, r, g, b);
831}
832
833/**
834 * drm_crtc_fill_palette_332 - Programs a default palette for R332-like formats
835 * @crtc: The displaying CRTC
836 * @set_palette: Callback for programming the hardware gamma LUT
837 *
838 * Programs an RGB332 palette to hardware.
839 */
840void drm_crtc_fill_palette_332(struct drm_crtc *crtc, drm_crtc_set_lut_func set_palette)
841{
842	unsigned int r, g, b;
843
844	/* Limits of 8-8-4 are the maximum number of values for each channel. */
845	for (r = 0; r < 8; ++r) {
846		for (g = 0; g < 8; ++g) {
847			for (b = 0; b < 4; ++b)
848				fill_palette_332(crtc, r, g, b, set_palette);
849		}
850	}
851}
852EXPORT_SYMBOL(drm_crtc_fill_palette_332);
853
854static void fill_palette_8(struct drm_crtc *crtc, unsigned int i,
855			   drm_crtc_set_lut_func set_palette)
856{
857	u16 Y = (i << 8) | i; // relative luminance
858
859	set_palette(crtc, i, Y, Y, Y);
860}
861
862/**
863 * drm_crtc_fill_palette_8 - Programs a default palette for C8-like formats
864 * @crtc: The displaying CRTC
865 * @set_palette: Callback for programming the hardware gamma LUT
866 *
867 * Programs a default palette to hardware.
868 */
869void drm_crtc_fill_palette_8(struct drm_crtc *crtc, drm_crtc_set_lut_func set_palette)
870{
871	unsigned int i;
872
873	for (i = 0; i < 256; ++i)
874		fill_palette_8(crtc, i, set_palette);
875}
876EXPORT_SYMBOL(drm_crtc_fill_palette_8);
877
878/**
879 * drm_color_lut32_check - check validity of extended lookup table
880 * @lut: property blob containing extended LUT to check
881 * @tests: bitmask of tests to run
882 *
883 * Helper to check whether a userspace-provided extended lookup table is valid and
884 * satisfies hardware requirements.  Drivers pass a bitmask indicating which of
885 * the tests in &drm_color_lut_tests should be performed.
886 *
887 * Returns 0 on success, -EINVAL on failure.
888 */
889int drm_color_lut32_check(const struct drm_property_blob *lut, u32 tests)
890{
891	const struct drm_color_lut32 *entry;
892	int i;
893
894	if (!lut || !tests)
895		return 0;
896
897	entry = lut->data;
898	for (i = 0; i < drm_color_lut32_size(lut); i++) {
899		if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
900			if (entry[i].red != entry[i].blue ||
901			    entry[i].red != entry[i].green) {
902				DRM_DEBUG_KMS("All LUT entries must have equal r/g/b\n");
903				return -EINVAL;
904			}
905		}
906
907		if (i > 0 && tests & DRM_COLOR_LUT_NON_DECREASING) {
908			if (entry[i].red < entry[i - 1].red ||
909			    entry[i].green < entry[i - 1].green ||
910			    entry[i].blue < entry[i - 1].blue) {
911				DRM_DEBUG_KMS("LUT entries must never decrease.\n");
912				return -EINVAL;
913			}
914		}
915	}
916
917	return 0;
918}
919EXPORT_SYMBOL(drm_color_lut32_check);
~/ Read-only file preview.