xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_blend.c (revision ab1f231663464b9db88e94800230b837159ec369)
1 /*	$NetBSD: drm_blend.c,v 1.4 2021/12/19 00:50:01 riastradh Exp $	*/
2 
3 /*
4  * Copyright (C) 2016 Samsung Electronics Co.Ltd
5  * Authors:
6  *	Marek Szyprowski <m.szyprowski@samsung.com>
7  *
8  * DRM core plane blending related functions
9  *
10  * Permission to use, copy, modify, distribute, and sell this software and its
11  * documentation for any purpose is hereby granted without fee, provided that
12  * the above copyright notice appear in all copies and that both that copyright
13  * notice and this permission notice appear in supporting documentation, and
14  * that the name of the copyright holders not be used in advertising or
15  * publicity pertaining to distribution of the software without specific,
16  * written prior permission.  The copyright holders make no representations
17  * about the suitability of this software for any purpose.  It is provided "as
18  * is" without express or implied warranty.
19  *
20  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
21  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
22  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
23  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
24  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
25  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
26  * OF THIS SOFTWARE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: drm_blend.c,v 1.4 2021/12/19 00:50:01 riastradh Exp $");
31 
32 #include <linux/export.h>
33 #include <linux/slab.h>
34 #include <linux/sort.h>
35 
36 #include <drm/drm_atomic.h>
37 #include <drm/drm_blend.h>
38 #include <drm/drm_device.h>
39 #include <drm/drm_print.h>
40 
41 #include "drm_crtc_internal.h"
42 
43 /**
44  * DOC: overview
45  *
46  * The basic plane composition model supported by standard plane properties only
47  * has a source rectangle (in logical pixels within the &drm_framebuffer), with
48  * sub-pixel accuracy, which is scaled up to a pixel-aligned destination
49  * rectangle in the visible area of a &drm_crtc. The visible area of a CRTC is
50  * defined by the horizontal and vertical visible pixels (stored in @hdisplay
51  * and @vdisplay) of the requested mode (stored in &drm_crtc_state.mode). These
52  * two rectangles are both stored in the &drm_plane_state.
53  *
54  * For the atomic ioctl the following standard (atomic) properties on the plane object
55  * encode the basic plane composition model:
56  *
57  * SRC_X:
58  * 	X coordinate offset for the source rectangle within the
59  * 	&drm_framebuffer, in 16.16 fixed point. Must be positive.
60  * SRC_Y:
61  * 	Y coordinate offset for the source rectangle within the
62  * 	&drm_framebuffer, in 16.16 fixed point. Must be positive.
63  * SRC_W:
64  * 	Width for the source rectangle within the &drm_framebuffer, in 16.16
65  * 	fixed point. SRC_X plus SRC_W must be within the width of the source
66  * 	framebuffer. Must be positive.
67  * SRC_H:
68  * 	Height for the source rectangle within the &drm_framebuffer, in 16.16
69  * 	fixed point. SRC_Y plus SRC_H must be within the height of the source
70  * 	framebuffer. Must be positive.
71  * CRTC_X:
72  * 	X coordinate offset for the destination rectangle. Can be negative.
73  * CRTC_Y:
74  * 	Y coordinate offset for the destination rectangle. Can be negative.
75  * CRTC_W:
76  * 	Width for the destination rectangle. CRTC_X plus CRTC_W can extend past
77  * 	the currently visible horizontal area of the &drm_crtc.
78  * CRTC_H:
79  * 	Height for the destination rectangle. CRTC_Y plus CRTC_H can extend past
80  * 	the currently visible vertical area of the &drm_crtc.
81  * FB_ID:
82  * 	Mode object ID of the &drm_framebuffer this plane should scan out.
83  * CRTC_ID:
84  * 	Mode object ID of the &drm_crtc this plane should be connected to.
85  *
86  * Note that the source rectangle must fully lie within the bounds of the
87  * &drm_framebuffer. The destination rectangle can lie outside of the visible
88  * area of the current mode of the CRTC. It must be apprpriately clipped by the
89  * driver, which can be done by calling drm_plane_helper_check_update(). Drivers
90  * are also allowed to round the subpixel sampling positions appropriately, but
91  * only to the next full pixel. No pixel outside of the source rectangle may
92  * ever be sampled, which is important when applying more sophisticated
93  * filtering than just a bilinear one when scaling. The filtering mode when
94  * scaling is unspecified.
95  *
96  * On top of this basic transformation additional properties can be exposed by
97  * the driver:
98  *
99  * alpha:
100  * 	Alpha is setup with drm_plane_create_alpha_property(). It controls the
101  * 	plane-wide opacity, from transparent (0) to opaque (0xffff). It can be
102  * 	combined with pixel alpha.
103  *	The pixel values in the framebuffers are expected to not be
104  *	pre-multiplied by the global alpha associated to the plane.
105  *
106  * rotation:
107  *	Rotation is set up with drm_plane_create_rotation_property(). It adds a
108  *	rotation and reflection step between the source and destination rectangles.
109  *	Without this property the rectangle is only scaled, but not rotated or
110  *	reflected.
111  *
112  *	Possbile values:
113  *
114  *	"rotate-<degrees>":
115  *		Signals that a drm plane is rotated <degrees> degrees in counter
116  *		clockwise direction.
117  *
118  *	"reflect-<axis>":
119  *		Signals that the contents of a drm plane is reflected along the
120  *		<axis> axis, in the same way as mirroring.
121  *
122  *	reflect-x::
123  *
124  *			|o |    | o|
125  *			|  | -> |  |
126  *			| v|    |v |
127  *
128  *	reflect-y::
129  *
130  *			|o |    | ^|
131  *			|  | -> |  |
132  *			| v|    |o |
133  *
134  * zpos:
135  *	Z position is set up with drm_plane_create_zpos_immutable_property() and
136  *	drm_plane_create_zpos_property(). It controls the visibility of overlapping
137  *	planes. Without this property the primary plane is always below the cursor
138  *	plane, and ordering between all other planes is undefined. The positive
139  *	Z axis points towards the user, i.e. planes with lower Z position values
140  *	are underneath planes with higher Z position values. Two planes with the
141  *	same Z position value have undefined ordering. Note that the Z position
142  *	value can also be immutable, to inform userspace about the hard-coded
143  *	stacking of planes, see drm_plane_create_zpos_immutable_property().
144  *
145  * pixel blend mode:
146  *	Pixel blend mode is set up with drm_plane_create_blend_mode_property().
147  *	It adds a blend mode for alpha blending equation selection, describing
148  *	how the pixels from the current plane are composited with the
149  *	background.
150  *
151  *	 Three alpha blending equations are defined:
152  *
153  *	 "None":
154  *		 Blend formula that ignores the pixel alpha::
155  *
156  *			 out.rgb = plane_alpha * fg.rgb +
157  *				 (1 - plane_alpha) * bg.rgb
158  *
159  *	 "Pre-multiplied":
160  *		 Blend formula that assumes the pixel color values
161  *		 have been already pre-multiplied with the alpha
162  *		 channel values::
163  *
164  *			 out.rgb = plane_alpha * fg.rgb +
165  *				 (1 - (plane_alpha * fg.alpha)) * bg.rgb
166  *
167  *	 "Coverage":
168  *		 Blend formula that assumes the pixel color values have not
169  *		 been pre-multiplied and will do so when blending them to the
170  *		 background color values::
171  *
172  *			 out.rgb = plane_alpha * fg.alpha * fg.rgb +
173  *				 (1 - (plane_alpha * fg.alpha)) * bg.rgb
174  *
175  *	 Using the following symbols:
176  *
177  *	 "fg.rgb":
178  *		 Each of the RGB component values from the plane's pixel
179  *	 "fg.alpha":
180  *		 Alpha component value from the plane's pixel. If the plane's
181  *		 pixel format has no alpha component, then this is assumed to be
182  *		 1.0. In these cases, this property has no effect, as all three
183  *		 equations become equivalent.
184  *	 "bg.rgb":
185  *		 Each of the RGB component values from the background
186  *	 "plane_alpha":
187  *		 Plane alpha value set by the plane "alpha" property. If the
188  *		 plane does not expose the "alpha" property, then this is
189  *		 assumed to be 1.0
190  *
191  * Note that all the property extensions described here apply either to the
192  * plane or the CRTC (e.g. for the background color, which currently is not
193  * exposed and assumed to be black).
194  */
195 
196 /**
197  * drm_plane_create_alpha_property - create a new alpha property
198  * @plane: drm plane
199  *
200  * This function creates a generic, mutable, alpha property and enables support
201  * for it in the DRM core. It is attached to @plane.
202  *
203  * The alpha property will be allowed to be within the bounds of 0
204  * (transparent) to 0xffff (opaque).
205  *
206  * Returns:
207  * 0 on success, negative error code on failure.
208  */
drm_plane_create_alpha_property(struct drm_plane * plane)209 int drm_plane_create_alpha_property(struct drm_plane *plane)
210 {
211 	struct drm_property *prop;
212 
213 	prop = drm_property_create_range(plane->dev, 0, "alpha",
214 					 0, DRM_BLEND_ALPHA_OPAQUE);
215 	if (!prop)
216 		return -ENOMEM;
217 
218 	drm_object_attach_property(&plane->base, prop, DRM_BLEND_ALPHA_OPAQUE);
219 	plane->alpha_property = prop;
220 
221 	if (plane->state)
222 		plane->state->alpha = DRM_BLEND_ALPHA_OPAQUE;
223 
224 	return 0;
225 }
226 EXPORT_SYMBOL(drm_plane_create_alpha_property);
227 
228 /**
229  * drm_plane_create_rotation_property - create a new rotation property
230  * @plane: drm plane
231  * @rotation: initial value of the rotation property
232  * @supported_rotations: bitmask of supported rotations and reflections
233  *
234  * This creates a new property with the selected support for transformations.
235  *
236  * Since a rotation by 180° degress is the same as reflecting both along the x
237  * and the y axis the rotation property is somewhat redundant. Drivers can use
238  * drm_rotation_simplify() to normalize values of this property.
239  *
240  * The property exposed to userspace is a bitmask property (see
241  * drm_property_create_bitmask()) called "rotation" and has the following
242  * bitmask enumaration values:
243  *
244  * DRM_MODE_ROTATE_0:
245  * 	"rotate-0"
246  * DRM_MODE_ROTATE_90:
247  * 	"rotate-90"
248  * DRM_MODE_ROTATE_180:
249  * 	"rotate-180"
250  * DRM_MODE_ROTATE_270:
251  * 	"rotate-270"
252  * DRM_MODE_REFLECT_X:
253  * 	"reflect-x"
254  * DRM_MODE_REFLECT_Y:
255  * 	"reflect-y"
256  *
257  * Rotation is the specified amount in degrees in counter clockwise direction,
258  * the X and Y axis are within the source rectangle, i.e.  the X/Y axis before
259  * rotation. After reflection, the rotation is applied to the image sampled from
260  * the source rectangle, before scaling it to fit the destination rectangle.
261  */
drm_plane_create_rotation_property(struct drm_plane * plane,unsigned int rotation,unsigned int supported_rotations)262 int drm_plane_create_rotation_property(struct drm_plane *plane,
263 				       unsigned int rotation,
264 				       unsigned int supported_rotations)
265 {
266 	static const struct drm_prop_enum_list props[] = {
267 		{ __builtin_ffs(DRM_MODE_ROTATE_0) - 1,   "rotate-0" },
268 		{ __builtin_ffs(DRM_MODE_ROTATE_90) - 1,  "rotate-90" },
269 		{ __builtin_ffs(DRM_MODE_ROTATE_180) - 1, "rotate-180" },
270 		{ __builtin_ffs(DRM_MODE_ROTATE_270) - 1, "rotate-270" },
271 		{ __builtin_ffs(DRM_MODE_REFLECT_X) - 1,  "reflect-x" },
272 		{ __builtin_ffs(DRM_MODE_REFLECT_Y) - 1,  "reflect-y" },
273 	};
274 	struct drm_property *prop;
275 
276 	WARN_ON((supported_rotations & DRM_MODE_ROTATE_MASK) == 0);
277 	WARN_ON(!is_power_of_2(rotation & DRM_MODE_ROTATE_MASK));
278 	WARN_ON(rotation & ~supported_rotations);
279 
280 	prop = drm_property_create_bitmask(plane->dev, 0, "rotation",
281 					   props, ARRAY_SIZE(props),
282 					   supported_rotations);
283 	if (!prop)
284 		return -ENOMEM;
285 
286 	drm_object_attach_property(&plane->base, prop, rotation);
287 
288 	if (plane->state)
289 		plane->state->rotation = rotation;
290 
291 	plane->rotation_property = prop;
292 
293 	return 0;
294 }
295 EXPORT_SYMBOL(drm_plane_create_rotation_property);
296 
297 /**
298  * drm_rotation_simplify() - Try to simplify the rotation
299  * @rotation: Rotation to be simplified
300  * @supported_rotations: Supported rotations
301  *
302  * Attempt to simplify the rotation to a form that is supported.
303  * Eg. if the hardware supports everything except DRM_MODE_REFLECT_X
304  * one could call this function like this:
305  *
306  * drm_rotation_simplify(rotation, DRM_MODE_ROTATE_0 |
307  *                       DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_180 |
308  *                       DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_Y);
309  *
310  * to eliminate the DRM_MODE_ROTATE_X flag. Depending on what kind of
311  * transforms the hardware supports, this function may not
312  * be able to produce a supported transform, so the caller should
313  * check the result afterwards.
314  */
drm_rotation_simplify(unsigned int rotation,unsigned int supported_rotations)315 unsigned int drm_rotation_simplify(unsigned int rotation,
316 				   unsigned int supported_rotations)
317 {
318 	if (rotation & ~supported_rotations) {
319 		rotation ^= DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
320 		rotation = (rotation & DRM_MODE_REFLECT_MASK) |
321 		           BIT((ffs(rotation & DRM_MODE_ROTATE_MASK) + 1)
322 		           % 4);
323 	}
324 
325 	return rotation;
326 }
327 EXPORT_SYMBOL(drm_rotation_simplify);
328 
329 /**
330  * drm_plane_create_zpos_property - create mutable zpos property
331  * @plane: drm plane
332  * @zpos: initial value of zpos property
333  * @min: minimal possible value of zpos property
334  * @max: maximal possible value of zpos property
335  *
336  * This function initializes generic mutable zpos property and enables support
337  * for it in drm core. Drivers can then attach this property to planes to enable
338  * support for configurable planes arrangement during blending operation.
339  * Drivers that attach a mutable zpos property to any plane should call the
340  * drm_atomic_normalize_zpos() helper during their implementation of
341  * &drm_mode_config_funcs.atomic_check(), which will update the normalized zpos
342  * values and store them in &drm_plane_state.normalized_zpos. Usually min
343  * should be set to 0 and max to maximal number of planes for given crtc - 1.
344  *
345  * If zpos of some planes cannot be changed (like fixed background or
346  * cursor/topmost planes), driver should adjust min/max values and assign those
347  * planes immutable zpos property with lower or higher values (for more
348  * information, see drm_plane_create_zpos_immutable_property() function). In such
349  * case driver should also assign proper initial zpos values for all planes in
350  * its plane_reset() callback, so the planes will be always sorted properly.
351  *
352  * See also drm_atomic_normalize_zpos().
353  *
354  * The property exposed to userspace is called "zpos".
355  *
356  * Returns:
357  * Zero on success, negative errno on failure.
358  */
drm_plane_create_zpos_property(struct drm_plane * plane,unsigned int zpos,unsigned int min,unsigned int max)359 int drm_plane_create_zpos_property(struct drm_plane *plane,
360 				   unsigned int zpos,
361 				   unsigned int min, unsigned int max)
362 {
363 	struct drm_property *prop;
364 
365 	prop = drm_property_create_range(plane->dev, 0, "zpos", min, max);
366 	if (!prop)
367 		return -ENOMEM;
368 
369 	drm_object_attach_property(&plane->base, prop, zpos);
370 
371 	plane->zpos_property = prop;
372 
373 	if (plane->state) {
374 		plane->state->zpos = zpos;
375 		plane->state->normalized_zpos = zpos;
376 	}
377 
378 	return 0;
379 }
380 EXPORT_SYMBOL(drm_plane_create_zpos_property);
381 
382 /**
383  * drm_plane_create_zpos_immutable_property - create immuttable zpos property
384  * @plane: drm plane
385  * @zpos: value of zpos property
386  *
387  * This function initializes generic immutable zpos property and enables
388  * support for it in drm core. Using this property driver lets userspace
389  * to get the arrangement of the planes for blending operation and notifies
390  * it that the hardware (or driver) doesn't support changing of the planes'
391  * order. For mutable zpos see drm_plane_create_zpos_property().
392  *
393  * The property exposed to userspace is called "zpos".
394  *
395  * Returns:
396  * Zero on success, negative errno on failure.
397  */
drm_plane_create_zpos_immutable_property(struct drm_plane * plane,unsigned int zpos)398 int drm_plane_create_zpos_immutable_property(struct drm_plane *plane,
399 					     unsigned int zpos)
400 {
401 	struct drm_property *prop;
402 
403 	prop = drm_property_create_range(plane->dev, DRM_MODE_PROP_IMMUTABLE,
404 					 "zpos", zpos, zpos);
405 	if (!prop)
406 		return -ENOMEM;
407 
408 	drm_object_attach_property(&plane->base, prop, zpos);
409 
410 	plane->zpos_property = prop;
411 
412 	if (plane->state) {
413 		plane->state->zpos = zpos;
414 		plane->state->normalized_zpos = zpos;
415 	}
416 
417 	return 0;
418 }
419 EXPORT_SYMBOL(drm_plane_create_zpos_immutable_property);
420 
drm_atomic_state_zpos_cmp(const void * a,const void * b)421 static int drm_atomic_state_zpos_cmp(const void *a, const void *b)
422 {
423 	const struct drm_plane_state *sa = *(struct drm_plane_state *const *)a;
424 	const struct drm_plane_state *sb = *(struct drm_plane_state *const *)b;
425 
426 	if (sa->zpos != sb->zpos)
427 		return sa->zpos - sb->zpos;
428 	else
429 		return sa->plane->base.id - sb->plane->base.id;
430 }
431 
drm_atomic_helper_crtc_normalize_zpos(struct drm_crtc * crtc,struct drm_crtc_state * crtc_state)432 static int drm_atomic_helper_crtc_normalize_zpos(struct drm_crtc *crtc,
433 					  struct drm_crtc_state *crtc_state)
434 {
435 	struct drm_atomic_state *state = crtc_state->state;
436 	struct drm_device *dev = crtc->dev;
437 	int total_planes = dev->mode_config.num_total_plane;
438 	struct drm_plane_state **states;
439 	struct drm_plane *plane;
440 	int i, n = 0;
441 	int ret = 0;
442 
443 	DRM_DEBUG_ATOMIC("[CRTC:%d:%s] calculating normalized zpos values\n",
444 			 crtc->base.id, crtc->name);
445 
446 	states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL);
447 	if (!states)
448 		return -ENOMEM;
449 
450 	/*
451 	 * Normalization process might create new states for planes which
452 	 * normalized_zpos has to be recalculated.
453 	 */
454 	drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) {
455 		struct drm_plane_state *plane_state =
456 			drm_atomic_get_plane_state(state, plane);
457 		if (IS_ERR(plane_state)) {
458 			ret = PTR_ERR(plane_state);
459 			goto done;
460 		}
461 		states[n++] = plane_state;
462 		DRM_DEBUG_ATOMIC("[PLANE:%d:%s] processing zpos value %d\n",
463 				 plane->base.id, plane->name,
464 				 plane_state->zpos);
465 	}
466 
467 	sort(states, n, sizeof(*states), drm_atomic_state_zpos_cmp, NULL);
468 
469 	for (i = 0; i < n; i++) {
470 		plane = states[i]->plane;
471 
472 		states[i]->normalized_zpos = i;
473 		DRM_DEBUG_ATOMIC("[PLANE:%d:%s] normalized zpos value %d\n",
474 				 plane->base.id, plane->name, i);
475 	}
476 	crtc_state->zpos_changed = true;
477 
478 done:
479 	kfree(states);
480 	return ret;
481 }
482 
483 /**
484  * drm_atomic_normalize_zpos - calculate normalized zpos values for all crtcs
485  * @dev: DRM device
486  * @state: atomic state of DRM device
487  *
488  * This function calculates normalized zpos value for all modified planes in
489  * the provided atomic state of DRM device.
490  *
491  * For every CRTC this function checks new states of all planes assigned to
492  * it and calculates normalized zpos value for these planes. Planes are compared
493  * first by their zpos values, then by plane id (if zpos is equal). The plane
494  * with lowest zpos value is at the bottom. The &drm_plane_state.normalized_zpos
495  * is then filled with unique values from 0 to number of active planes in crtc
496  * minus one.
497  *
498  * RETURNS
499  * Zero for success or -errno
500  */
drm_atomic_normalize_zpos(struct drm_device * dev,struct drm_atomic_state * state)501 int drm_atomic_normalize_zpos(struct drm_device *dev,
502 			      struct drm_atomic_state *state)
503 {
504 	struct drm_crtc *crtc;
505 	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
506 	struct drm_plane *plane __unused;
507 	struct drm_plane_state *old_plane_state, *new_plane_state;
508 	int i, ret = 0;
509 
510 	for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
511 		crtc = new_plane_state->crtc;
512 		if (!crtc)
513 			continue;
514 		if (old_plane_state->zpos != new_plane_state->zpos) {
515 			new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
516 			new_crtc_state->zpos_changed = true;
517 		}
518 	}
519 
520 	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
521 		if (old_crtc_state->plane_mask != new_crtc_state->plane_mask ||
522 		    new_crtc_state->zpos_changed) {
523 			ret = drm_atomic_helper_crtc_normalize_zpos(crtc,
524 								    new_crtc_state);
525 			if (ret)
526 				return ret;
527 		}
528 	}
529 	return 0;
530 }
531 EXPORT_SYMBOL(drm_atomic_normalize_zpos);
532 
533 /**
534  * drm_plane_create_blend_mode_property - create a new blend mode property
535  * @plane: drm plane
536  * @supported_modes: bitmask of supported modes, must include
537  *		     BIT(DRM_MODE_BLEND_PREMULTI). Current DRM assumption is
538  *		     that alpha is premultiplied, and old userspace can break if
539  *		     the property defaults to anything else.
540  *
541  * This creates a new property describing the blend mode.
542  *
543  * The property exposed to userspace is an enumeration property (see
544  * drm_property_create_enum()) called "pixel blend mode" and has the
545  * following enumeration values:
546  *
547  * "None":
548  *	Blend formula that ignores the pixel alpha.
549  *
550  * "Pre-multiplied":
551  *	Blend formula that assumes the pixel color values have been already
552  *	pre-multiplied with the alpha channel values.
553  *
554  * "Coverage":
555  *	Blend formula that assumes the pixel color values have not been
556  *	pre-multiplied and will do so when blending them to the background color
557  *	values.
558  *
559  * RETURNS:
560  * Zero for success or -errno
561  */
drm_plane_create_blend_mode_property(struct drm_plane * plane,unsigned int supported_modes)562 int drm_plane_create_blend_mode_property(struct drm_plane *plane,
563 					 unsigned int supported_modes)
564 {
565 	struct drm_device *dev = plane->dev;
566 	struct drm_property *prop;
567 	static const struct drm_prop_enum_list props[] = {
568 		{ DRM_MODE_BLEND_PIXEL_NONE, "None" },
569 		{ DRM_MODE_BLEND_PREMULTI, "Pre-multiplied" },
570 		{ DRM_MODE_BLEND_COVERAGE, "Coverage" },
571 	};
572 	unsigned int valid_mode_mask = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
573 				       BIT(DRM_MODE_BLEND_PREMULTI)   |
574 				       BIT(DRM_MODE_BLEND_COVERAGE);
575 	int i;
576 
577 	if (WARN_ON((supported_modes & ~valid_mode_mask) ||
578 		    ((supported_modes & BIT(DRM_MODE_BLEND_PREMULTI)) == 0)))
579 		return -EINVAL;
580 
581 	prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
582 				   "pixel blend mode",
583 				   hweight32(supported_modes));
584 	if (!prop)
585 		return -ENOMEM;
586 
587 	for (i = 0; i < ARRAY_SIZE(props); i++) {
588 		int ret;
589 
590 		if (!(BIT(props[i].type) & supported_modes))
591 			continue;
592 
593 		ret = drm_property_add_enum(prop, props[i].type,
594 					    props[i].name);
595 
596 		if (ret) {
597 			drm_property_destroy(dev, prop);
598 
599 			return ret;
600 		}
601 	}
602 
603 	drm_object_attach_property(&plane->base, prop, DRM_MODE_BLEND_PREMULTI);
604 	plane->blend_mode_property = prop;
605 
606 	return 0;
607 }
608 EXPORT_SYMBOL(drm_plane_create_blend_mode_property);
609