1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ |
4 | * Author: Rob Clark <rob.clark@linaro.org> |
5 | */ |
6 | |
7 | #include <drm/drm_atomic.h> |
8 | #include <drm/drm_atomic_helper.h> |
9 | #include <drm/drm_blend.h> |
10 | #include <drm/drm_gem_atomic_helper.h> |
11 | #include <drm/drm_fourcc.h> |
12 | #include <drm/drm_framebuffer.h> |
13 | |
14 | #include "omap_dmm_tiler.h" |
15 | #include "omap_drv.h" |
16 | |
17 | /* |
18 | * plane funcs |
19 | */ |
20 | |
21 | #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) |
22 | |
23 | struct omap_plane_state { |
24 | /* Must be first. */ |
25 | struct drm_plane_state base; |
26 | |
27 | struct omap_hw_overlay *overlay; |
28 | struct omap_hw_overlay *r_overlay; /* right overlay */ |
29 | }; |
30 | |
31 | #define to_omap_plane(x) container_of(x, struct omap_plane, base) |
32 | |
33 | struct omap_plane { |
34 | struct drm_plane base; |
35 | enum omap_plane_id id; |
36 | }; |
37 | |
38 | bool is_omap_plane_dual_overlay(struct drm_plane_state *state) |
39 | { |
40 | struct omap_plane_state *omap_state = to_omap_plane_state(state); |
41 | |
42 | return !!omap_state->r_overlay; |
43 | } |
44 | |
45 | static int omap_plane_prepare_fb(struct drm_plane *plane, |
46 | struct drm_plane_state *new_state) |
47 | { |
48 | if (!new_state->fb) |
49 | return 0; |
50 | |
51 | drm_gem_plane_helper_prepare_fb(plane, state: new_state); |
52 | |
53 | return omap_framebuffer_pin(fb: new_state->fb); |
54 | } |
55 | |
56 | static void omap_plane_cleanup_fb(struct drm_plane *plane, |
57 | struct drm_plane_state *old_state) |
58 | { |
59 | if (old_state->fb) |
60 | omap_framebuffer_unpin(fb: old_state->fb); |
61 | } |
62 | |
63 | static void omap_plane_atomic_update(struct drm_plane *plane, |
64 | struct drm_atomic_state *state) |
65 | { |
66 | struct omap_drm_private *priv = plane->dev->dev_private; |
67 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
68 | plane); |
69 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
70 | plane); |
71 | struct omap_plane_state *new_omap_state; |
72 | struct omap_plane_state *old_omap_state; |
73 | struct omap_overlay_info info, r_info; |
74 | enum omap_plane_id ovl_id, r_ovl_id; |
75 | int ret; |
76 | bool dual_ovl; |
77 | |
78 | new_omap_state = to_omap_plane_state(new_state); |
79 | old_omap_state = to_omap_plane_state(old_state); |
80 | |
81 | dual_ovl = is_omap_plane_dual_overlay(state: new_state); |
82 | |
83 | /* Cleanup previously held overlay if needed */ |
84 | if (old_omap_state->overlay) |
85 | omap_overlay_update_state(priv, overlay: old_omap_state->overlay); |
86 | if (old_omap_state->r_overlay) |
87 | omap_overlay_update_state(priv, overlay: old_omap_state->r_overlay); |
88 | |
89 | if (!new_omap_state->overlay) { |
90 | DBG("[PLANE:%d:%s] no overlay attached" , plane->base.id, plane->name); |
91 | return; |
92 | } |
93 | |
94 | ovl_id = new_omap_state->overlay->id; |
95 | DBG("%s, crtc=%p fb=%p" , plane->name, new_state->crtc, |
96 | new_state->fb); |
97 | |
98 | memset(&info, 0, sizeof(info)); |
99 | info.rotation_type = OMAP_DSS_ROT_NONE; |
100 | info.rotation = DRM_MODE_ROTATE_0; |
101 | info.global_alpha = new_state->alpha >> 8; |
102 | info.zorder = new_state->normalized_zpos; |
103 | if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) |
104 | info.pre_mult_alpha = 1; |
105 | else |
106 | info.pre_mult_alpha = 0; |
107 | info.color_encoding = new_state->color_encoding; |
108 | info.color_range = new_state->color_range; |
109 | |
110 | r_info = info; |
111 | |
112 | /* update scanout: */ |
113 | omap_framebuffer_update_scanout(fb: new_state->fb, state: new_state, info: &info, |
114 | r_info: dual_ovl ? &r_info : NULL); |
115 | |
116 | DBG("%s: %dx%d -> %dx%d (%d)" , |
117 | new_omap_state->overlay->name, info.width, info.height, |
118 | info.out_width, info.out_height, info.screen_width); |
119 | DBG("%d,%d %pad %pad" , info.pos_x, info.pos_y, |
120 | &info.paddr, &info.p_uv_addr); |
121 | |
122 | if (dual_ovl) { |
123 | r_ovl_id = new_omap_state->r_overlay->id; |
124 | /* |
125 | * If the current plane uses 2 hw planes the very next |
126 | * zorder is used by the r_overlay so we just use the |
127 | * main overlay zorder + 1 |
128 | */ |
129 | r_info.zorder = info.zorder + 1; |
130 | |
131 | DBG("%s: %dx%d -> %dx%d (%d)" , |
132 | new_omap_state->r_overlay->name, |
133 | r_info.width, r_info.height, |
134 | r_info.out_width, r_info.out_height, r_info.screen_width); |
135 | DBG("%d,%d %pad %pad" , r_info.pos_x, r_info.pos_y, |
136 | &r_info.paddr, &r_info.p_uv_addr); |
137 | } |
138 | |
139 | /* and finally, update omapdss: */ |
140 | ret = dispc_ovl_setup(dispc: priv->dispc, plane: ovl_id, oi: &info, |
141 | vm: omap_crtc_timings(crtc: new_state->crtc), mem_to_mem: false, |
142 | channel: omap_crtc_channel(crtc: new_state->crtc)); |
143 | if (ret) { |
144 | dev_err(plane->dev->dev, "Failed to setup plane %s\n" , |
145 | plane->name); |
146 | dispc_ovl_enable(dispc: priv->dispc, plane: ovl_id, enable: false); |
147 | return; |
148 | } |
149 | |
150 | dispc_ovl_enable(dispc: priv->dispc, plane: ovl_id, enable: true); |
151 | |
152 | if (dual_ovl) { |
153 | ret = dispc_ovl_setup(dispc: priv->dispc, plane: r_ovl_id, oi: &r_info, |
154 | vm: omap_crtc_timings(crtc: new_state->crtc), mem_to_mem: false, |
155 | channel: omap_crtc_channel(crtc: new_state->crtc)); |
156 | if (ret) { |
157 | dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n" , |
158 | plane->name); |
159 | dispc_ovl_enable(dispc: priv->dispc, plane: r_ovl_id, enable: false); |
160 | dispc_ovl_enable(dispc: priv->dispc, plane: ovl_id, enable: false); |
161 | return; |
162 | } |
163 | |
164 | dispc_ovl_enable(dispc: priv->dispc, plane: r_ovl_id, enable: true); |
165 | } |
166 | } |
167 | |
168 | static void omap_plane_atomic_disable(struct drm_plane *plane, |
169 | struct drm_atomic_state *state) |
170 | { |
171 | struct omap_drm_private *priv = plane->dev->dev_private; |
172 | struct omap_plane *omap_plane = to_omap_plane(plane); |
173 | struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, |
174 | plane); |
175 | struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, |
176 | plane); |
177 | struct omap_plane_state *new_omap_state; |
178 | struct omap_plane_state *old_omap_state; |
179 | |
180 | new_omap_state = to_omap_plane_state(new_state); |
181 | old_omap_state = to_omap_plane_state(old_state); |
182 | |
183 | if (!old_omap_state->overlay) |
184 | return; |
185 | |
186 | new_state->rotation = DRM_MODE_ROTATE_0; |
187 | new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; |
188 | |
189 | omap_overlay_update_state(priv, overlay: old_omap_state->overlay); |
190 | new_omap_state->overlay = NULL; |
191 | |
192 | if (is_omap_plane_dual_overlay(state: old_state)) { |
193 | omap_overlay_update_state(priv, overlay: old_omap_state->r_overlay); |
194 | new_omap_state->r_overlay = NULL; |
195 | } |
196 | } |
197 | |
198 | #define FRAC_16_16(mult, div) (((mult) << 16) / (div)) |
199 | |
200 | static int omap_plane_atomic_check(struct drm_plane *plane, |
201 | struct drm_atomic_state *state) |
202 | { |
203 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, |
204 | plane); |
205 | struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, |
206 | plane); |
207 | struct omap_drm_private *priv = plane->dev->dev_private; |
208 | struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state); |
209 | struct omap_global_state *omap_overlay_global_state; |
210 | struct drm_crtc_state *crtc_state; |
211 | bool new_r_hw_overlay = false; |
212 | bool new_hw_overlay = false; |
213 | u32 max_width, max_height; |
214 | struct drm_crtc *crtc; |
215 | u16 width, height; |
216 | u32 caps = 0; |
217 | u32 fourcc; |
218 | int ret; |
219 | |
220 | omap_overlay_global_state = omap_get_global_state(s: state); |
221 | if (IS_ERR(ptr: omap_overlay_global_state)) |
222 | return PTR_ERR(ptr: omap_overlay_global_state); |
223 | |
224 | dispc_ovl_get_max_size(dispc: priv->dispc, width: &width, height: &height); |
225 | max_width = width << 16; |
226 | max_height = height << 16; |
227 | |
228 | crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc; |
229 | if (!crtc) |
230 | return 0; |
231 | |
232 | crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); |
233 | /* we should have a crtc state if the plane is attached to a crtc */ |
234 | if (WARN_ON(!crtc_state)) |
235 | return 0; |
236 | |
237 | /* |
238 | * Note: these are just sanity checks to filter out totally bad scaling |
239 | * factors. The real limits must be calculated case by case, and |
240 | * unfortunately we currently do those checks only at the commit |
241 | * phase in dispc. |
242 | */ |
243 | ret = drm_atomic_helper_check_plane_state(plane_state: new_plane_state, crtc_state, |
244 | FRAC_16_16(1, 8), FRAC_16_16(8, 1), |
245 | can_position: true, can_update_disabled: true); |
246 | if (ret) |
247 | return ret; |
248 | |
249 | DBG("%s: visible %d -> %d" , plane->name, |
250 | old_plane_state->visible, new_plane_state->visible); |
251 | |
252 | if (!new_plane_state->visible) { |
253 | omap_overlay_release(s: state, overlay: omap_state->overlay); |
254 | omap_overlay_release(s: state, overlay: omap_state->r_overlay); |
255 | omap_state->overlay = NULL; |
256 | omap_state->r_overlay = NULL; |
257 | return 0; |
258 | } |
259 | |
260 | if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) |
261 | return -EINVAL; |
262 | |
263 | if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay) |
264 | return -EINVAL; |
265 | |
266 | if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay) |
267 | return -EINVAL; |
268 | |
269 | /* Make sure dimensions are within bounds. */ |
270 | if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height) |
271 | return -EINVAL; |
272 | |
273 | |
274 | if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) { |
275 | bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv; |
276 | |
277 | if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) || |
278 | new_plane_state->crtc_w / 2 & 1)) { |
279 | /* |
280 | * When calculating the split overlay width |
281 | * and it yield an odd value we will need to adjust |
282 | * the indivual width +/- 1. So make sure it fits |
283 | */ |
284 | if (new_plane_state->src_w <= ((2 * width - 1) << 16) && |
285 | new_plane_state->crtc_w <= (2 * width - 1)) |
286 | new_r_hw_overlay = true; |
287 | else |
288 | return -EINVAL; |
289 | } else { |
290 | if (new_plane_state->src_w <= (2 * max_width) && |
291 | new_plane_state->crtc_w <= (2 * width)) |
292 | new_r_hw_overlay = true; |
293 | else |
294 | return -EINVAL; |
295 | } |
296 | } |
297 | |
298 | if (new_plane_state->rotation != DRM_MODE_ROTATE_0 && |
299 | !omap_framebuffer_supports_rotation(fb: new_plane_state->fb)) |
300 | return -EINVAL; |
301 | |
302 | if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w || |
303 | (new_plane_state->src_h >> 16) != new_plane_state->crtc_h) |
304 | caps |= OMAP_DSS_OVL_CAP_SCALE; |
305 | |
306 | fourcc = new_plane_state->fb->format->format; |
307 | |
308 | /* |
309 | * (re)allocate hw overlay if we don't have one or |
310 | * there is a caps mismatch |
311 | */ |
312 | if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) { |
313 | new_hw_overlay = true; |
314 | } else { |
315 | /* check supported format */ |
316 | if (!dispc_ovl_color_mode_supported(dispc: priv->dispc, plane: omap_state->overlay->id, |
317 | fourcc)) |
318 | new_hw_overlay = true; |
319 | } |
320 | |
321 | /* |
322 | * check if we need two overlays and only have 1 or |
323 | * if we had 2 overlays but will only need 1 |
324 | */ |
325 | if ((new_r_hw_overlay && !omap_state->r_overlay) || |
326 | (!new_r_hw_overlay && omap_state->r_overlay)) |
327 | new_hw_overlay = true; |
328 | |
329 | if (new_hw_overlay) { |
330 | struct omap_hw_overlay *old_ovl = omap_state->overlay; |
331 | struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay; |
332 | struct omap_hw_overlay *new_ovl = NULL; |
333 | struct omap_hw_overlay *new_r_ovl = NULL; |
334 | |
335 | omap_overlay_release(s: state, overlay: old_ovl); |
336 | omap_overlay_release(s: state, overlay: old_r_ovl); |
337 | |
338 | ret = omap_overlay_assign(s: state, plane, caps, fourcc, overlay: &new_ovl, |
339 | r_overlay: new_r_hw_overlay ? &new_r_ovl : NULL); |
340 | if (ret) { |
341 | DBG("%s: failed to assign hw_overlay" , plane->name); |
342 | omap_state->overlay = NULL; |
343 | omap_state->r_overlay = NULL; |
344 | return ret; |
345 | } |
346 | |
347 | omap_state->overlay = new_ovl; |
348 | if (new_r_hw_overlay) |
349 | omap_state->r_overlay = new_r_ovl; |
350 | else |
351 | omap_state->r_overlay = NULL; |
352 | } |
353 | |
354 | DBG("plane: %s overlay_id: %d" , plane->name, omap_state->overlay->id); |
355 | |
356 | if (omap_state->r_overlay) |
357 | DBG("plane: %s r_overlay_id: %d" , plane->name, omap_state->r_overlay->id); |
358 | |
359 | return 0; |
360 | } |
361 | |
362 | static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { |
363 | .prepare_fb = omap_plane_prepare_fb, |
364 | .cleanup_fb = omap_plane_cleanup_fb, |
365 | .atomic_check = omap_plane_atomic_check, |
366 | .atomic_update = omap_plane_atomic_update, |
367 | .atomic_disable = omap_plane_atomic_disable, |
368 | }; |
369 | |
370 | static void omap_plane_destroy(struct drm_plane *plane) |
371 | { |
372 | struct omap_plane *omap_plane = to_omap_plane(plane); |
373 | |
374 | DBG("%s" , plane->name); |
375 | |
376 | drm_plane_cleanup(plane); |
377 | |
378 | kfree(objp: omap_plane); |
379 | } |
380 | |
381 | /* helper to install properties which are common to planes and crtcs */ |
382 | void omap_plane_install_properties(struct drm_plane *plane, |
383 | struct drm_mode_object *obj) |
384 | { |
385 | struct drm_device *dev = plane->dev; |
386 | struct omap_drm_private *priv = dev->dev_private; |
387 | |
388 | if (priv->has_dmm) { |
389 | if (!plane->rotation_property) |
390 | drm_plane_create_rotation_property(plane, |
391 | DRM_MODE_ROTATE_0, |
392 | DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 | |
393 | DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 | |
394 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y); |
395 | |
396 | /* Attach the rotation property also to the crtc object */ |
397 | if (plane->rotation_property && obj != &plane->base) |
398 | drm_object_attach_property(obj, property: plane->rotation_property, |
399 | DRM_MODE_ROTATE_0); |
400 | } |
401 | |
402 | drm_object_attach_property(obj, property: priv->zorder_prop, init_val: 0); |
403 | } |
404 | |
405 | static void omap_plane_reset(struct drm_plane *plane) |
406 | { |
407 | struct omap_plane_state *omap_state; |
408 | |
409 | if (plane->state) |
410 | drm_atomic_helper_plane_destroy_state(plane, state: plane->state); |
411 | |
412 | omap_state = kzalloc(size: sizeof(*omap_state), GFP_KERNEL); |
413 | if (!omap_state) |
414 | return; |
415 | |
416 | __drm_atomic_helper_plane_reset(plane, state: &omap_state->base); |
417 | } |
418 | |
419 | static struct drm_plane_state * |
420 | omap_plane_atomic_duplicate_state(struct drm_plane *plane) |
421 | { |
422 | struct omap_plane_state *state, *current_state; |
423 | |
424 | if (WARN_ON(!plane->state)) |
425 | return NULL; |
426 | |
427 | current_state = to_omap_plane_state(plane->state); |
428 | |
429 | state = kmalloc(size: sizeof(*state), GFP_KERNEL); |
430 | if (!state) |
431 | return NULL; |
432 | |
433 | __drm_atomic_helper_plane_duplicate_state(plane, state: &state->base); |
434 | |
435 | state->overlay = current_state->overlay; |
436 | state->r_overlay = current_state->r_overlay; |
437 | |
438 | return &state->base; |
439 | } |
440 | |
441 | static void omap_plane_atomic_print_state(struct drm_printer *p, |
442 | const struct drm_plane_state *state) |
443 | { |
444 | struct omap_plane_state *omap_state = to_omap_plane_state(state); |
445 | |
446 | if (omap_state->overlay) |
447 | drm_printf(p, f: "\toverlay=%s (caps=0x%x)\n" , |
448 | omap_state->overlay->name, |
449 | omap_state->overlay->caps); |
450 | else |
451 | drm_printf(p, f: "\toverlay=None\n" ); |
452 | if (omap_state->r_overlay) |
453 | drm_printf(p, f: "\tr_overlay=%s (caps=0x%x)\n" , |
454 | omap_state->r_overlay->name, |
455 | omap_state->r_overlay->caps); |
456 | else |
457 | drm_printf(p, f: "\tr_overlay=None\n" ); |
458 | } |
459 | |
460 | static int omap_plane_atomic_set_property(struct drm_plane *plane, |
461 | struct drm_plane_state *state, |
462 | struct drm_property *property, |
463 | u64 val) |
464 | { |
465 | struct omap_drm_private *priv = plane->dev->dev_private; |
466 | |
467 | if (property == priv->zorder_prop) |
468 | state->zpos = val; |
469 | else |
470 | return -EINVAL; |
471 | |
472 | return 0; |
473 | } |
474 | |
475 | static int omap_plane_atomic_get_property(struct drm_plane *plane, |
476 | const struct drm_plane_state *state, |
477 | struct drm_property *property, |
478 | u64 *val) |
479 | { |
480 | struct omap_drm_private *priv = plane->dev->dev_private; |
481 | |
482 | if (property == priv->zorder_prop) |
483 | *val = state->zpos; |
484 | else |
485 | return -EINVAL; |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | static const struct drm_plane_funcs omap_plane_funcs = { |
491 | .update_plane = drm_atomic_helper_update_plane, |
492 | .disable_plane = drm_atomic_helper_disable_plane, |
493 | .reset = omap_plane_reset, |
494 | .destroy = omap_plane_destroy, |
495 | .atomic_duplicate_state = omap_plane_atomic_duplicate_state, |
496 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
497 | .atomic_set_property = omap_plane_atomic_set_property, |
498 | .atomic_get_property = omap_plane_atomic_get_property, |
499 | .atomic_print_state = omap_plane_atomic_print_state, |
500 | }; |
501 | |
502 | static bool omap_plane_supports_yuv(struct drm_plane *plane) |
503 | { |
504 | struct omap_drm_private *priv = plane->dev->dev_private; |
505 | struct omap_plane *omap_plane = to_omap_plane(plane); |
506 | const u32 *formats = dispc_ovl_get_color_modes(dispc: priv->dispc, plane: omap_plane->id); |
507 | u32 i; |
508 | |
509 | for (i = 0; formats[i]; i++) |
510 | if (formats[i] == DRM_FORMAT_YUYV || |
511 | formats[i] == DRM_FORMAT_UYVY || |
512 | formats[i] == DRM_FORMAT_NV12) |
513 | return true; |
514 | |
515 | return false; |
516 | } |
517 | |
518 | /* initialize plane */ |
519 | struct drm_plane *omap_plane_init(struct drm_device *dev, |
520 | int idx, enum drm_plane_type type, |
521 | u32 possible_crtcs) |
522 | { |
523 | struct omap_drm_private *priv = dev->dev_private; |
524 | unsigned int num_planes = dispc_get_num_ovls(dispc: priv->dispc); |
525 | struct drm_plane *plane; |
526 | struct omap_plane *omap_plane; |
527 | unsigned int zpos; |
528 | int ret; |
529 | u32 nformats; |
530 | const u32 *formats; |
531 | |
532 | if (WARN_ON(idx >= num_planes)) |
533 | return ERR_PTR(error: -EINVAL); |
534 | |
535 | omap_plane = kzalloc(size: sizeof(*omap_plane), GFP_KERNEL); |
536 | if (!omap_plane) |
537 | return ERR_PTR(error: -ENOMEM); |
538 | |
539 | omap_plane->id = idx; |
540 | |
541 | DBG("%d: type=%d" , omap_plane->id, type); |
542 | DBG(" crtc_mask: 0x%04x" , possible_crtcs); |
543 | |
544 | formats = dispc_ovl_get_color_modes(dispc: priv->dispc, plane: omap_plane->id); |
545 | for (nformats = 0; formats[nformats]; ++nformats) |
546 | ; |
547 | |
548 | plane = &omap_plane->base; |
549 | |
550 | ret = drm_universal_plane_init(dev, plane, possible_crtcs, |
551 | funcs: &omap_plane_funcs, formats, |
552 | format_count: nformats, NULL, type, NULL); |
553 | if (ret < 0) |
554 | goto error; |
555 | |
556 | drm_plane_helper_add(plane, funcs: &omap_plane_helper_funcs); |
557 | |
558 | omap_plane_install_properties(plane, obj: &plane->base); |
559 | |
560 | /* |
561 | * Set the zpos default depending on whether we are a primary or overlay |
562 | * plane. |
563 | */ |
564 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) |
565 | zpos = 0; |
566 | else |
567 | zpos = omap_plane->id; |
568 | drm_plane_create_zpos_property(plane, zpos, min: 0, max: num_planes - 1); |
569 | drm_plane_create_alpha_property(plane); |
570 | drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) | |
571 | BIT(DRM_MODE_BLEND_COVERAGE)); |
572 | |
573 | if (omap_plane_supports_yuv(plane)) |
574 | drm_plane_create_color_properties(plane, |
575 | BIT(DRM_COLOR_YCBCR_BT601) | |
576 | BIT(DRM_COLOR_YCBCR_BT709), |
577 | BIT(DRM_COLOR_YCBCR_FULL_RANGE) | |
578 | BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), |
579 | default_encoding: DRM_COLOR_YCBCR_BT601, |
580 | default_range: DRM_COLOR_YCBCR_FULL_RANGE); |
581 | |
582 | return plane; |
583 | |
584 | error: |
585 | dev_err(dev->dev, "%s(): could not create plane: %d\n" , |
586 | __func__, omap_plane->id); |
587 | |
588 | kfree(objp: omap_plane); |
589 | return NULL; |
590 | } |
591 | |