blob: 6259c79770ad6630ad188413544883bc7e797b4f [file] [log] [blame]
Daniel Stone4c2fc702019-06-18 11:12:07 +01001/*
2 * Copyright © 2008-2011 Kristian Høgsberg
3 * Copyright © 2011 Intel Corporation
4 * Copyright © 2017, 2018 Collabora, Ltd.
5 * Copyright © 2017, 2018 General Electric Company
6 * Copyright (c) 2018 DisplayLink (UK) Ltd.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial
18 * portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 */
29
30#include "config.h"
31
32#include <stdint.h>
33
34#include <xf86drm.h>
35#include <xf86drmMode.h>
Daniel Stone4c2fc702019-06-18 11:12:07 +010036
Daniel Stone4c2fc702019-06-18 11:12:07 +010037#include <libweston/libweston.h>
38#include <libweston/backend-drm.h>
39#include "shared/helpers.h"
Pekka Paalanen4b301fe2021-02-04 17:39:45 +020040#include "shared/weston-drm-fourcc.h"
Daniel Stone4c2fc702019-06-18 11:12:07 +010041#include "drm-internal.h"
42#include "pixel-formats.h"
43#include "presentation-time-server-protocol.h"
44
45struct drm_property_enum_info plane_type_enums[] = {
46 [WDRM_PLANE_TYPE_PRIMARY] = {
47 .name = "Primary",
48 },
49 [WDRM_PLANE_TYPE_OVERLAY] = {
50 .name = "Overlay",
51 },
52 [WDRM_PLANE_TYPE_CURSOR] = {
53 .name = "Cursor",
54 },
55};
56
57const struct drm_property_info plane_props[] = {
58 [WDRM_PLANE_TYPE] = {
59 .name = "type",
60 .enum_values = plane_type_enums,
61 .num_enum_values = WDRM_PLANE_TYPE__COUNT,
62 },
63 [WDRM_PLANE_SRC_X] = { .name = "SRC_X", },
64 [WDRM_PLANE_SRC_Y] = { .name = "SRC_Y", },
65 [WDRM_PLANE_SRC_W] = { .name = "SRC_W", },
66 [WDRM_PLANE_SRC_H] = { .name = "SRC_H", },
67 [WDRM_PLANE_CRTC_X] = { .name = "CRTC_X", },
68 [WDRM_PLANE_CRTC_Y] = { .name = "CRTC_Y", },
69 [WDRM_PLANE_CRTC_W] = { .name = "CRTC_W", },
70 [WDRM_PLANE_CRTC_H] = { .name = "CRTC_H", },
71 [WDRM_PLANE_FB_ID] = { .name = "FB_ID", },
72 [WDRM_PLANE_CRTC_ID] = { .name = "CRTC_ID", },
73 [WDRM_PLANE_IN_FORMATS] = { .name = "IN_FORMATS" },
74 [WDRM_PLANE_IN_FENCE_FD] = { .name = "IN_FENCE_FD" },
75 [WDRM_PLANE_FB_DAMAGE_CLIPS] = { .name = "FB_DAMAGE_CLIPS" },
Marius Vladcdd6fa22019-08-29 20:42:00 +030076 [WDRM_PLANE_ZPOS] = { .name = "zpos" },
Daniel Stone4c2fc702019-06-18 11:12:07 +010077};
78
79struct drm_property_enum_info dpms_state_enums[] = {
80 [WDRM_DPMS_STATE_OFF] = {
81 .name = "Off",
82 },
83 [WDRM_DPMS_STATE_ON] = {
84 .name = "On",
85 },
86 [WDRM_DPMS_STATE_STANDBY] = {
87 .name = "Standby",
88 },
89 [WDRM_DPMS_STATE_SUSPEND] = {
90 .name = "Suspend",
91 },
92};
93
Ankit Nautiyala344fe32019-05-14 18:36:08 +053094struct drm_property_enum_info content_protection_enums[] = {
95 [WDRM_CONTENT_PROTECTION_UNDESIRED] = {
96 .name = "Undesired",
97 },
98 [WDRM_CONTENT_PROTECTION_DESIRED] = {
99 .name = "Desired",
100 },
101 [WDRM_CONTENT_PROTECTION_ENABLED] = {
102 .name = "Enabled",
103 },
104};
105
106struct drm_property_enum_info hdcp_content_type_enums[] = {
107 [WDRM_HDCP_CONTENT_TYPE0] = {
108 .name = "HDCP Type0",
109 },
110 [WDRM_HDCP_CONTENT_TYPE1] = {
111 .name = "HDCP Type1",
112 },
113};
114
Lucas Stach72e7a1e2019-11-25 23:31:57 +0000115struct drm_property_enum_info panel_orientation_enums[] = {
116 [WDRM_PANEL_ORIENTATION_NORMAL] = { .name = "Normal", },
117 [WDRM_PANEL_ORIENTATION_UPSIDE_DOWN] = { .name = "Upside Down", },
118 [WDRM_PANEL_ORIENTATION_LEFT_SIDE_UP] = { .name = "Left Side Up", },
119 [WDRM_PANEL_ORIENTATION_RIGHT_SIDE_UP] = { .name = "Right Side Up", },
120};
121
Daniel Stone4c2fc702019-06-18 11:12:07 +0100122const struct drm_property_info connector_props[] = {
123 [WDRM_CONNECTOR_EDID] = { .name = "EDID" },
124 [WDRM_CONNECTOR_DPMS] = {
125 .name = "DPMS",
126 .enum_values = dpms_state_enums,
127 .num_enum_values = WDRM_DPMS_STATE__COUNT,
128 },
129 [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", },
130 [WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", },
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530131 [WDRM_CONNECTOR_CONTENT_PROTECTION] = {
132 .name = "Content Protection",
133 .enum_values = content_protection_enums,
134 .num_enum_values = WDRM_CONTENT_PROTECTION__COUNT,
135 },
136 [WDRM_CONNECTOR_HDCP_CONTENT_TYPE] = {
137 .name = "HDCP Content Type",
138 .enum_values = hdcp_content_type_enums,
139 .num_enum_values = WDRM_HDCP_CONTENT_TYPE__COUNT,
140 },
Lucas Stach72e7a1e2019-11-25 23:31:57 +0000141 [WDRM_CONNECTOR_PANEL_ORIENTATION] = {
142 .name = "panel orientation",
143 .enum_values = panel_orientation_enums,
144 .num_enum_values = WDRM_PANEL_ORIENTATION__COUNT,
145 },
Daniel Stone4c2fc702019-06-18 11:12:07 +0100146};
147
148const struct drm_property_info crtc_props[] = {
149 [WDRM_CRTC_MODE_ID] = { .name = "MODE_ID", },
150 [WDRM_CRTC_ACTIVE] = { .name = "ACTIVE", },
151};
152
153
154/**
155 * Mode for drm_pending_state_apply and co.
156 */
157enum drm_state_apply_mode {
158 DRM_STATE_APPLY_SYNC, /**< state fully processed */
159 DRM_STATE_APPLY_ASYNC, /**< state pending event delivery */
160 DRM_STATE_TEST_ONLY, /**< test if the state can be applied */
161};
162
163/**
164 * Get the current value of a KMS property
165 *
166 * Given a drmModeObjectGetProperties return, as well as the drm_property_info
167 * for the target property, return the current value of that property,
168 * with an optional default. If the property is a KMS enum type, the return
169 * value will be translated into the appropriate internal enum.
170 *
171 * If the property is not present, the default value will be returned.
172 *
173 * @param info Internal structure for property to look up
174 * @param props Raw KMS properties for the target object
175 * @param def Value to return if property is not found
176 */
177uint64_t
178drm_property_get_value(struct drm_property_info *info,
179 const drmModeObjectProperties *props,
180 uint64_t def)
181{
182 unsigned int i;
183
184 if (info->prop_id == 0)
185 return def;
186
187 for (i = 0; i < props->count_props; i++) {
188 unsigned int j;
189
190 if (props->props[i] != info->prop_id)
191 continue;
192
193 /* Simple (non-enum) types can return the value directly */
194 if (info->num_enum_values == 0)
195 return props->prop_values[i];
196
197 /* Map from raw value to enum value */
198 for (j = 0; j < info->num_enum_values; j++) {
199 if (!info->enum_values[j].valid)
200 continue;
201 if (info->enum_values[j].value != props->prop_values[i])
202 continue;
203
204 return j;
205 }
206
207 /* We don't have a mapping for this enum; return default. */
208 break;
209 }
210
211 return def;
212}
213
214/**
Marius Vlad1accffe2019-11-01 12:00:09 +0200215 * Get the current range values of a KMS property
216 *
217 * Given a drmModeObjectGetProperties return, as well as the drm_property_info
218 * for the target property, return the current range values of that property,
219 *
220 * If the property is not present, or there's no it is not a prop range then
221 * NULL will be returned.
222 *
223 * @param info Internal structure for property to look up
224 * @param props Raw KMS properties for the target object
225 */
226uint64_t *
227drm_property_get_range_values(struct drm_property_info *info,
228 const drmModeObjectProperties *props)
229{
230 unsigned int i;
231
232 if (info->prop_id == 0)
233 return NULL;
234
235 for (i = 0; i < props->count_props; i++) {
236
237 if (props->props[i] != info->prop_id)
238 continue;
239
240 if (!(info->flags & DRM_MODE_PROP_RANGE) &&
241 !(info->flags & DRM_MODE_PROP_SIGNED_RANGE))
242 continue;
243
244 return info->range_values;
245 }
246
247 return NULL;
248}
249
250/**
Daniel Stone4c2fc702019-06-18 11:12:07 +0100251 * Cache DRM property values
252 *
253 * Update a per-object array of drm_property_info structures, given the
254 * DRM properties of the object.
255 *
256 * Call this every time an object newly appears (note that only connectors
257 * can be hotplugged), the first time it is seen, or when its status changes
258 * in a way which invalidates the potential property values (currently, the
259 * only case for this is connector hotplug).
260 *
261 * This updates the property IDs and enum values within the drm_property_info
262 * array.
263 *
264 * DRM property enum values are dynamic at runtime; the user must query the
265 * property to find out the desired runtime value for a requested string
266 * name. Using the 'type' field on planes as an example, there is no single
267 * hardcoded constant for primary plane types; instead, the property must be
268 * queried at runtime to find the value associated with the string "Primary".
269 *
270 * This helper queries and caches the enum values, to allow us to use a set
271 * of compile-time-constant enums portably across various implementations.
272 * The values given in enum_names are searched for, and stored in the
273 * same-indexed field of the map array.
274 *
275 * @param b DRM backend object
276 * @param src DRM property info array to source from
277 * @param info DRM property info array to copy into
278 * @param num_infos Number of entries in the source array
279 * @param props DRM object properties for the object
280 */
281void
282drm_property_info_populate(struct drm_backend *b,
283 const struct drm_property_info *src,
284 struct drm_property_info *info,
285 unsigned int num_infos,
286 drmModeObjectProperties *props)
287{
288 drmModePropertyRes *prop;
289 unsigned i, j;
290
291 for (i = 0; i < num_infos; i++) {
292 unsigned int j;
293
294 info[i].name = src[i].name;
295 info[i].prop_id = 0;
296 info[i].num_enum_values = src[i].num_enum_values;
297
298 if (src[i].num_enum_values == 0)
299 continue;
300
301 info[i].enum_values =
302 malloc(src[i].num_enum_values *
303 sizeof(*info[i].enum_values));
304 assert(info[i].enum_values);
305 for (j = 0; j < info[i].num_enum_values; j++) {
306 info[i].enum_values[j].name = src[i].enum_values[j].name;
307 info[i].enum_values[j].valid = false;
308 }
309 }
310
311 for (i = 0; i < props->count_props; i++) {
312 unsigned int k;
313
314 prop = drmModeGetProperty(b->drm.fd, props->props[i]);
315 if (!prop)
316 continue;
317
318 for (j = 0; j < num_infos; j++) {
319 if (!strcmp(prop->name, info[j].name))
320 break;
321 }
322
323 /* We don't know/care about this property. */
324 if (j == num_infos) {
325#ifdef DEBUG
326 weston_log("DRM debug: unrecognized property %u '%s'\n",
327 prop->prop_id, prop->name);
328#endif
329 drmModeFreeProperty(prop);
330 continue;
331 }
332
333 if (info[j].num_enum_values == 0 &&
334 (prop->flags & DRM_MODE_PROP_ENUM)) {
335 weston_log("DRM: expected property %s to not be an"
336 " enum, but it is; ignoring\n", prop->name);
337 drmModeFreeProperty(prop);
338 continue;
339 }
340
341 info[j].prop_id = props->props[i];
Marius Vlad1accffe2019-11-01 12:00:09 +0200342 info[j].flags = prop->flags;
343
344 if (prop->flags & DRM_MODE_PROP_RANGE ||
345 prop->flags & DRM_MODE_PROP_SIGNED_RANGE) {
346 info[j].num_range_values = prop->count_values;
347 for (int i = 0; i < prop->count_values; i++)
348 info[j].range_values[i] = prop->values[i];
349 }
350
Daniel Stone4c2fc702019-06-18 11:12:07 +0100351
352 if (info[j].num_enum_values == 0) {
353 drmModeFreeProperty(prop);
354 continue;
355 }
356
357 if (!(prop->flags & DRM_MODE_PROP_ENUM)) {
358 weston_log("DRM: expected property %s to be an enum,"
359 " but it is not; ignoring\n", prop->name);
360 drmModeFreeProperty(prop);
361 info[j].prop_id = 0;
362 continue;
363 }
364
365 for (k = 0; k < info[j].num_enum_values; k++) {
366 int l;
367
368 for (l = 0; l < prop->count_enums; l++) {
369 if (!strcmp(prop->enums[l].name,
370 info[j].enum_values[k].name))
371 break;
372 }
373
374 if (l == prop->count_enums)
375 continue;
376
377 info[j].enum_values[k].valid = true;
378 info[j].enum_values[k].value = prop->enums[l].value;
379 }
380
381 drmModeFreeProperty(prop);
382 }
383
384#ifdef DEBUG
385 for (i = 0; i < num_infos; i++) {
386 if (info[i].prop_id == 0)
387 weston_log("DRM warning: property '%s' missing\n",
388 info[i].name);
389 }
390#endif
391}
392
393/**
394 * Free DRM property information
395 *
396 * Frees all memory associated with a DRM property info array and zeroes
397 * it out, leaving it usable for a further drm_property_info_update() or
398 * drm_property_info_free().
399 *
400 * @param info DRM property info array
401 * @param num_props Number of entries in array to free
402 */
403void
404drm_property_info_free(struct drm_property_info *info, int num_props)
405{
406 int i;
407
408 for (i = 0; i < num_props; i++)
409 free(info[i].enum_values);
410
411 memset(info, 0, sizeof(*info) * num_props);
412}
413
Daniel Stone4c2fc702019-06-18 11:12:07 +0100414static inline uint32_t *
415formats_ptr(struct drm_format_modifier_blob *blob)
416{
417 return (uint32_t *)(((char *)blob) + blob->formats_offset);
418}
419
420static inline struct drm_format_modifier *
421modifiers_ptr(struct drm_format_modifier_blob *blob)
422{
423 return (struct drm_format_modifier *)
424 (((char *)blob) + blob->modifiers_offset);
425}
Daniel Stone4c2fc702019-06-18 11:12:07 +0100426
427/**
428 * Populates the plane's formats array, using either the IN_FORMATS blob
429 * property (if available), or the plane's format list if not.
430 */
431int
432drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
Stefan Agner465ab2c2020-06-17 23:36:44 +0200433 const drmModeObjectProperties *props,
434 const bool use_modifiers)
Daniel Stone4c2fc702019-06-18 11:12:07 +0100435{
Scott Anderson74663092021-02-01 15:46:33 -0300436 unsigned i, j;
437 drmModePropertyBlobRes *blob = NULL;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100438 struct drm_format_modifier_blob *fmt_mod_blob;
439 struct drm_format_modifier *blob_modifiers;
440 uint32_t *blob_formats;
441 uint32_t blob_id;
Scott Anderson74663092021-02-01 15:46:33 -0300442 struct weston_drm_format *fmt;
443 int ret = 0;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100444
Stefan Agner465ab2c2020-06-17 23:36:44 +0200445 if (!use_modifiers)
446 goto fallback;
447
Daniel Stone4c2fc702019-06-18 11:12:07 +0100448 blob_id = drm_property_get_value(&plane->props[WDRM_PLANE_IN_FORMATS],
449 props,
450 0);
451 if (blob_id == 0)
452 goto fallback;
453
454 blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
455 if (!blob)
456 goto fallback;
457
458 fmt_mod_blob = blob->data;
459 blob_formats = formats_ptr(fmt_mod_blob);
460 blob_modifiers = modifiers_ptr(fmt_mod_blob);
461
Scott Anderson74663092021-02-01 15:46:33 -0300462 assert(kplane->count_formats == fmt_mod_blob->count_formats);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100463
464 for (i = 0; i < fmt_mod_blob->count_formats; i++) {
Scott Anderson74663092021-02-01 15:46:33 -0300465 fmt = weston_drm_format_array_add_format(&plane->formats,
466 blob_formats[i]);
467 if (!fmt) {
468 ret = -1;
469 goto out;
470 }
Daniel Stone4c2fc702019-06-18 11:12:07 +0100471
472 for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
473 struct drm_format_modifier *mod = &blob_modifiers[j];
474
475 if ((i < mod->offset) || (i > mod->offset + 63))
476 continue;
477 if (!(mod->formats & (1 << (i - mod->offset))))
478 continue;
479
Scott Anderson74663092021-02-01 15:46:33 -0300480 ret = weston_drm_format_add_modifier(fmt, mod->modifier);
481 if (ret < 0)
482 goto out;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100483 }
484
Leandro Ribeiro98101e82021-04-21 11:46:35 -0300485 if (fmt->modifiers.size == 0)
486 weston_drm_format_array_remove_latest_format(&plane->formats);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100487 }
488
Scott Anderson74663092021-02-01 15:46:33 -0300489out:
Daniel Stone4c2fc702019-06-18 11:12:07 +0100490 drmModeFreePropertyBlob(blob);
Scott Anderson74663092021-02-01 15:46:33 -0300491 return ret;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100492
493fallback:
Daniel Stone4c2fc702019-06-18 11:12:07 +0100494 /* No IN_FORMATS blob available, so just use the old. */
Tomohito Esaki29beeaf2019-06-24 17:23:44 +0900495 for (i = 0; i < kplane->count_formats; i++) {
Scott Anderson74663092021-02-01 15:46:33 -0300496 fmt = weston_drm_format_array_add_format(&plane->formats,
497 kplane->formats[i]);
498 if (!fmt)
499 return -1;
Leandro Ribeiro567cc922021-04-21 11:44:53 -0300500 ret = weston_drm_format_add_modifier(fmt, DRM_FORMAT_MOD_INVALID);
Scott Anderson74663092021-02-01 15:46:33 -0300501 if (ret < 0)
502 return -1;
Tomohito Esaki29beeaf2019-06-24 17:23:44 +0900503 }
Daniel Stone4c2fc702019-06-18 11:12:07 +0100504 return 0;
505}
506
507void
508drm_output_set_gamma(struct weston_output *output_base,
509 uint16_t size, uint16_t *r, uint16_t *g, uint16_t *b)
510{
511 int rc;
512 struct drm_output *output = to_drm_output(output_base);
513 struct drm_backend *backend =
514 to_drm_backend(output->base.compositor);
515
516 /* check */
517 if (output_base->gamma_size != size)
518 return;
519
520 rc = drmModeCrtcSetGamma(backend->drm.fd,
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300521 output->crtc->crtc_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100522 size, r, g, b);
523 if (rc)
524 weston_log("set gamma failed: %s\n", strerror(errno));
525}
526
527/**
528 * Mark an output state as current on the output, i.e. it has been
529 * submitted to the kernel. The mode argument determines whether this
530 * update will be applied synchronously (e.g. when calling drmModeSetCrtc),
531 * or asynchronously (in which case we wait for events to complete).
532 */
533static void
534drm_output_assign_state(struct drm_output_state *state,
535 enum drm_state_apply_mode mode)
536{
537 struct drm_output *output = state->output;
538 struct drm_backend *b = to_drm_backend(output->base.compositor);
539 struct drm_plane_state *plane_state;
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530540 struct drm_head *head;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100541
542 assert(!output->state_last);
543
544 if (mode == DRM_STATE_APPLY_ASYNC)
545 output->state_last = output->state_cur;
546 else
547 drm_output_state_free(output->state_cur);
548
549 wl_list_remove(&state->link);
550 wl_list_init(&state->link);
551 state->pending_state = NULL;
552
553 output->state_cur = state;
554
555 if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300556 drm_debug(b, "\t[CRTC:%u] setting pending flip\n",
557 output->crtc->crtc_id);
Emmanuel Gil Peyrot1b3ad092019-12-09 02:50:55 +0100558 output->atomic_complete_pending = true;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100559 }
560
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530561 if (b->atomic_modeset &&
562 state->protection == WESTON_HDCP_DISABLE)
563 wl_list_for_each(head, &output->base.head_list, base.output_link)
564 weston_head_set_content_protection_status(&head->base,
565 WESTON_HDCP_DISABLE);
566
Daniel Stone4c2fc702019-06-18 11:12:07 +0100567 /* Replace state_cur on each affected plane with the new state, being
568 * careful to dispose of orphaned (but only orphaned) previous state.
569 * If the previous state is not orphaned (still has an output_state
570 * attached), it will be disposed of by freeing the output_state. */
571 wl_list_for_each(plane_state, &state->plane_list, link) {
572 struct drm_plane *plane = plane_state->plane;
573
574 if (plane->state_cur && !plane->state_cur->output_state)
575 drm_plane_state_free(plane->state_cur, true);
576 plane->state_cur = plane_state;
577
578 if (mode != DRM_STATE_APPLY_ASYNC) {
579 plane_state->complete = true;
580 continue;
581 }
582
583 if (b->atomic_modeset)
584 continue;
585
586 assert(plane->type != WDRM_PLANE_TYPE_OVERLAY);
587 if (plane->type == WDRM_PLANE_TYPE_PRIMARY)
Emmanuel Gil Peyrot1b3ad092019-12-09 02:50:55 +0100588 output->page_flip_pending = true;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100589 }
590}
591
592static void
593drm_output_set_cursor(struct drm_output_state *output_state)
594{
595 struct drm_output *output = output_state->output;
596 struct drm_backend *b = to_drm_backend(output->base.compositor);
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300597 struct drm_crtc *crtc = output->crtc;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100598 struct drm_plane *plane = output->cursor_plane;
599 struct drm_plane_state *state;
Stefan Agner974390a2019-07-08 00:42:05 +0200600 uint32_t handle;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100601
602 if (!plane)
603 return;
604
605 state = drm_output_state_get_existing_plane(output_state, plane);
606 if (!state)
607 return;
608
609 if (!state->fb) {
610 pixman_region32_fini(&plane->base.damage);
611 pixman_region32_init(&plane->base.damage);
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300612 drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100613 return;
614 }
615
616 assert(state->fb == output->gbm_cursor_fb[output->current_cursor]);
617 assert(!plane->state_cur->output || plane->state_cur->output == output);
618
Stefan Agner974390a2019-07-08 00:42:05 +0200619 handle = output->gbm_cursor_handle[output->current_cursor];
Daniel Stone4c2fc702019-06-18 11:12:07 +0100620 if (plane->state_cur->fb != state->fb) {
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300621 if (drmModeSetCursor(b->drm.fd, crtc->crtc_id, handle,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100622 b->cursor_width, b->cursor_height)) {
623 weston_log("failed to set cursor: %s\n",
624 strerror(errno));
625 goto err;
626 }
627 }
628
629 pixman_region32_fini(&plane->base.damage);
630 pixman_region32_init(&plane->base.damage);
631
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300632 if (drmModeMoveCursor(b->drm.fd, crtc->crtc_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100633 state->dest_x, state->dest_y)) {
634 weston_log("failed to move cursor: %s\n", strerror(errno));
635 goto err;
636 }
637
638 return;
639
640err:
Emmanuel Gil Peyrot1b3ad092019-12-09 02:50:55 +0100641 b->cursors_are_broken = true;
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300642 drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100643}
644
645static int
646drm_output_apply_state_legacy(struct drm_output_state *state)
647{
648 struct drm_output *output = state->output;
649 struct drm_backend *backend = to_drm_backend(output->base.compositor);
650 struct drm_plane *scanout_plane = output->scanout_plane;
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300651 struct drm_crtc *crtc = output->crtc;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100652 struct drm_property_info *dpms_prop;
653 struct drm_plane_state *scanout_state;
654 struct drm_mode *mode;
655 struct drm_head *head;
656 const struct pixel_format_info *pinfo = NULL;
657 uint32_t connectors[MAX_CLONED_CONNECTORS];
658 int n_conn = 0;
659 struct timespec now;
660 int ret = 0;
661
662 wl_list_for_each(head, &output->base.head_list, base.output_link) {
663 assert(n_conn < MAX_CLONED_CONNECTORS);
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300664 connectors[n_conn++] = head->connector.connector_id;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100665 }
666
667 /* If disable_planes is set then assign_planes() wasn't
668 * called for this render, so we could still have a stale
669 * cursor plane set up.
670 */
671 if (output->base.disable_planes) {
672 output->cursor_view = NULL;
673 if (output->cursor_plane) {
674 output->cursor_plane->base.x = INT32_MIN;
675 output->cursor_plane->base.y = INT32_MIN;
676 }
677 }
678
679 if (state->dpms != WESTON_DPMS_ON) {
680 if (output->cursor_plane) {
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300681 ret = drmModeSetCursor(backend->drm.fd, crtc->crtc_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100682 0, 0, 0);
683 if (ret)
684 weston_log("drmModeSetCursor failed disable: %s\n",
685 strerror(errno));
686 }
687
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300688 ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id, 0, 0, 0,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100689 NULL, 0, NULL);
690 if (ret)
691 weston_log("drmModeSetCrtc failed disabling: %s\n",
692 strerror(errno));
693
694 drm_output_assign_state(state, DRM_STATE_APPLY_SYNC);
695 weston_compositor_read_presentation_clock(output->base.compositor, &now);
696 drm_output_update_complete(output,
697 WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION,
698 now.tv_sec, now.tv_nsec / 1000);
699
700 return 0;
701 }
702
703 scanout_state =
704 drm_output_state_get_existing_plane(state, scanout_plane);
705
706 /* The legacy SetCrtc API doesn't allow us to do scaling, and the
707 * legacy PageFlip API doesn't allow us to do clipping either. */
708 assert(scanout_state->src_x == 0);
709 assert(scanout_state->src_y == 0);
710 assert(scanout_state->src_w ==
711 (unsigned) (output->base.current_mode->width << 16));
712 assert(scanout_state->src_h ==
713 (unsigned) (output->base.current_mode->height << 16));
714 assert(scanout_state->dest_x == 0);
715 assert(scanout_state->dest_y == 0);
716 assert(scanout_state->dest_w == scanout_state->src_w >> 16);
717 assert(scanout_state->dest_h == scanout_state->src_h >> 16);
718 /* The legacy SetCrtc API doesn't support fences */
719 assert(scanout_state->in_fence_fd == -1);
720
721 mode = to_drm_mode(output->base.current_mode);
722 if (backend->state_invalid ||
723 !scanout_plane->state_cur->fb ||
724 scanout_plane->state_cur->fb->strides[0] !=
725 scanout_state->fb->strides[0]) {
726
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300727 ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100728 scanout_state->fb->fb_id,
729 0, 0,
730 connectors, n_conn,
731 &mode->mode_info);
732 if (ret) {
733 weston_log("set mode failed: %s\n", strerror(errno));
734 goto err;
735 }
736 }
737
738 pinfo = scanout_state->fb->format;
739 drm_debug(backend, "\t[CRTC:%u, PLANE:%u] FORMAT: %s\n",
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300740 crtc->crtc_id, scanout_state->plane->plane_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100741 pinfo ? pinfo->drm_format_name : "UNKNOWN");
742
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300743 if (drmModePageFlip(backend->drm.fd, crtc->crtc_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100744 scanout_state->fb->fb_id,
745 DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
746 weston_log("queueing pageflip failed: %s\n", strerror(errno));
747 goto err;
748 }
749
750 assert(!output->page_flip_pending);
751
752 if (output->pageflip_timer)
753 wl_event_source_timer_update(output->pageflip_timer,
754 backend->pageflip_timeout);
755
756 drm_output_set_cursor(state);
757
758 if (state->dpms != output->state_cur->dpms) {
759 wl_list_for_each(head, &output->base.head_list, base.output_link) {
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300760 dpms_prop = &head->connector.props[WDRM_CONNECTOR_DPMS];
Daniel Stone4c2fc702019-06-18 11:12:07 +0100761 if (dpms_prop->prop_id == 0)
762 continue;
763
764 ret = drmModeConnectorSetProperty(backend->drm.fd,
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300765 head->connector.connector_id,
766 dpms_prop->prop_id,
767 state->dpms);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100768 if (ret) {
769 weston_log("DRM: DPMS: failed property set for %s\n",
770 head->base.name);
771 }
772 }
773 }
774
775 drm_output_assign_state(state, DRM_STATE_APPLY_ASYNC);
776
777 return 0;
778
779err:
780 output->cursor_view = NULL;
781 drm_output_state_free(state);
782 return -1;
783}
784
Daniel Stone4c2fc702019-06-18 11:12:07 +0100785static int
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300786crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100787 enum wdrm_crtc_property prop, uint64_t val)
788{
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300789 struct drm_property_info *info = &crtc->props_crtc[prop];
Daniel Stone4c2fc702019-06-18 11:12:07 +0100790 int ret;
791
792 if (info->prop_id == 0)
793 return -1;
794
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300795 ret = drmModeAtomicAddProperty(req, crtc->crtc_id, info->prop_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100796 val);
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300797 drm_debug(crtc->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
798 (unsigned long) crtc->crtc_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100799 (unsigned long) info->prop_id, info->name,
800 (unsigned long long) val, (unsigned long long) val);
801 return (ret <= 0) ? -1 : 0;
802}
803
804static int
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300805connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100806 enum wdrm_connector_property prop, uint64_t val)
807{
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300808 struct drm_property_info *info = &connector->props[prop];
809 uint32_t connector_id = connector->connector_id;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100810 int ret;
811
812 if (info->prop_id == 0)
813 return -1;
814
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300815 ret = drmModeAtomicAddProperty(req, connector_id, info->prop_id, val);
816 drm_debug(connector->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
817 (unsigned long) connector_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100818 (unsigned long) info->prop_id, info->name,
819 (unsigned long long) val, (unsigned long long) val);
820 return (ret <= 0) ? -1 : 0;
821}
822
823static int
824plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
825 enum wdrm_plane_property prop, uint64_t val)
826{
827 struct drm_property_info *info = &plane->props[prop];
828 int ret;
829
830 if (info->prop_id == 0)
831 return -1;
832
833 ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
834 val);
835 drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
836 (unsigned long) plane->plane_id,
837 (unsigned long) info->prop_id, info->name,
838 (unsigned long long) val, (unsigned long long) val);
839 return (ret <= 0) ? -1 : 0;
840}
841
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530842static bool
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300843drm_connector_has_prop(struct drm_connector *connector,
844 enum wdrm_connector_property prop)
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530845{
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300846 if (connector->props[prop].prop_id != 0)
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530847 return true;
848
849 return false;
850}
851
852/*
853 * This function converts the protection requests from weston_hdcp_protection
854 * corresponding drm values. These values can be set in "Content Protection"
855 * & "HDCP Content Type" connector properties.
856 */
857static void
858get_drm_protection_from_weston(enum weston_hdcp_protection weston_protection,
859 enum wdrm_content_protection_state *drm_protection,
860 enum wdrm_hdcp_content_type *drm_cp_type)
861{
862
863 switch (weston_protection) {
864 case WESTON_HDCP_DISABLE:
865 *drm_protection = WDRM_CONTENT_PROTECTION_UNDESIRED;
866 *drm_cp_type = WDRM_HDCP_CONTENT_TYPE0;
867 break;
868 case WESTON_HDCP_ENABLE_TYPE_0:
869 *drm_protection = WDRM_CONTENT_PROTECTION_DESIRED;
870 *drm_cp_type = WDRM_HDCP_CONTENT_TYPE0;
871 break;
872 case WESTON_HDCP_ENABLE_TYPE_1:
873 *drm_protection = WDRM_CONTENT_PROTECTION_DESIRED;
874 *drm_cp_type = WDRM_HDCP_CONTENT_TYPE1;
875 break;
876 default:
877 assert(0 && "bad weston_hdcp_protection");
878 }
879}
880
881static void
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300882drm_connector_set_hdcp_property(struct drm_connector *connector,
883 enum weston_hdcp_protection protection,
884 drmModeAtomicReq *req)
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530885{
886 int ret;
887 enum wdrm_content_protection_state drm_protection;
888 enum wdrm_hdcp_content_type drm_cp_type;
889 struct drm_property_enum_info *enum_info;
890 uint64_t prop_val;
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300891 struct drm_property_info *props = connector->props;
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530892
893 get_drm_protection_from_weston(protection, &drm_protection,
894 &drm_cp_type);
895
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300896 if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_CONTENT_PROTECTION))
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530897 return;
898
899 /*
900 * Content-type property is not exposed for platforms not supporting
901 * HDCP2.2, therefore, type-1 cannot be supported. The type-0 content
902 * still can be supported if the content-protection property is exposed.
903 */
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300904 if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_HDCP_CONTENT_TYPE) &&
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530905 drm_cp_type != WDRM_HDCP_CONTENT_TYPE0)
906 return;
907
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300908 enum_info = props[WDRM_CONNECTOR_CONTENT_PROTECTION].enum_values;
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530909 prop_val = enum_info[drm_protection].value;
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300910 ret = connector_add_prop(req, connector,
911 WDRM_CONNECTOR_CONTENT_PROTECTION, prop_val);
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530912 assert(ret == 0);
913
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300914 if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_HDCP_CONTENT_TYPE))
Ankit Nautiyalfc2c1802019-08-30 19:40:46 +0530915 return;
916
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300917 enum_info = props[WDRM_CONNECTOR_HDCP_CONTENT_TYPE].enum_values;
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530918 prop_val = enum_info[drm_cp_type].value;
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300919 ret = connector_add_prop(req, connector,
920 WDRM_CONNECTOR_HDCP_CONTENT_TYPE, prop_val);
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530921 assert(ret == 0);
922}
923
Daniel Stone4c2fc702019-06-18 11:12:07 +0100924static int
925drm_output_apply_state_atomic(struct drm_output_state *state,
926 drmModeAtomicReq *req,
927 uint32_t *flags)
928{
929 struct drm_output *output = state->output;
930 struct drm_backend *b = to_drm_backend(output->base.compositor);
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300931 struct drm_crtc *crtc = output->crtc;
Daniel Stone4c2fc702019-06-18 11:12:07 +0100932 struct drm_plane_state *plane_state;
933 struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
934 struct drm_head *head;
935 int ret = 0;
936
937 drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n",
938 (*flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "testing" : "applying",
939 (unsigned long) output->base.id, output->base.name);
940
941 if (state->dpms != output->state_cur->dpms) {
942 drm_debug(b, "\t\t\t[atomic] DPMS state differs, modeset OK\n");
943 *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
944 }
945
946 if (state->dpms == WESTON_DPMS_ON) {
947 ret = drm_mode_ensure_blob(b, current_mode);
948 if (ret != 0)
949 return ret;
950
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300951 ret |= crtc_add_prop(req, crtc, WDRM_CRTC_MODE_ID,
Daniel Stone4c2fc702019-06-18 11:12:07 +0100952 current_mode->blob_id);
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300953 ret |= crtc_add_prop(req, crtc, WDRM_CRTC_ACTIVE, 1);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100954
955 /* No need for the DPMS property, since it is implicit in
956 * routing and CRTC activity. */
957 wl_list_for_each(head, &output->base.head_list, base.output_link) {
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300958 ret |= connector_add_prop(req, &head->connector,
959 WDRM_CONNECTOR_CRTC_ID,
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300960 crtc->crtc_id);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100961 }
962 } else {
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300963 ret |= crtc_add_prop(req, crtc, WDRM_CRTC_MODE_ID, 0);
964 ret |= crtc_add_prop(req, crtc, WDRM_CRTC_ACTIVE, 0);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100965
966 /* No need for the DPMS property, since it is implicit in
967 * routing and CRTC activity. */
968 wl_list_for_each(head, &output->base.head_list, base.output_link)
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300969 ret |= connector_add_prop(req, &head->connector,
970 WDRM_CONNECTOR_CRTC_ID, 0);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100971 }
972
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530973 wl_list_for_each(head, &output->base.head_list, base.output_link)
Leandro Ribeiroe6369902020-06-17 11:09:47 -0300974 drm_connector_set_hdcp_property(&head->connector,
975 state->protection, req);
Ankit Nautiyala344fe32019-05-14 18:36:08 +0530976
Daniel Stone4c2fc702019-06-18 11:12:07 +0100977 if (ret != 0) {
978 weston_log("couldn't set atomic CRTC/connector state\n");
979 return ret;
980 }
981
982 wl_list_for_each(plane_state, &state->plane_list, link) {
983 struct drm_plane *plane = plane_state->plane;
984 const struct pixel_format_info *pinfo = NULL;
985
986 ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_ID,
987 plane_state->fb ? plane_state->fb->fb_id : 0);
988 ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID,
Leandro Ribeirob00d1a22020-08-13 14:12:28 -0300989 plane_state->fb ? crtc->crtc_id : 0);
Daniel Stone4c2fc702019-06-18 11:12:07 +0100990 ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_X,
991 plane_state->src_x);
992 ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_Y,
993 plane_state->src_y);
994 ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_W,
995 plane_state->src_w);
996 ret |= plane_add_prop(req, plane, WDRM_PLANE_SRC_H,
997 plane_state->src_h);
998 ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_X,
999 plane_state->dest_x);
1000 ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_Y,
1001 plane_state->dest_y);
1002 ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_W,
1003 plane_state->dest_w);
1004 ret |= plane_add_prop(req, plane, WDRM_PLANE_CRTC_H,
1005 plane_state->dest_h);
Scott Anderson15c603c2020-06-02 17:39:43 +12001006 if (plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS].prop_id != 0)
1007 ret |= plane_add_prop(req, plane, WDRM_PLANE_FB_DAMAGE_CLIPS,
1008 plane_state->damage_blob_id);
Daniel Stone4c2fc702019-06-18 11:12:07 +01001009
1010 if (plane_state->fb && plane_state->fb->format)
1011 pinfo = plane_state->fb->format;
1012
1013 drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
1014 (unsigned long) plane->plane_id,
1015 pinfo ? pinfo->drm_format_name : "UNKNOWN");
1016
1017 if (plane_state->in_fence_fd >= 0) {
1018 ret |= plane_add_prop(req, plane,
1019 WDRM_PLANE_IN_FENCE_FD,
1020 plane_state->in_fence_fd);
1021 }
1022
Marius Vladcdd6fa22019-08-29 20:42:00 +03001023 /* do note, that 'invented' zpos values are set as immutable */
1024 if (plane_state->zpos != DRM_PLANE_ZPOS_INVALID_PLANE &&
1025 plane_state->plane->zpos_min != plane_state->plane->zpos_max)
1026 ret |= plane_add_prop(req, plane,
1027 WDRM_PLANE_ZPOS,
1028 plane_state->zpos);
1029
Daniel Stone4c2fc702019-06-18 11:12:07 +01001030 if (ret != 0) {
1031 weston_log("couldn't set plane state\n");
1032 return ret;
1033 }
1034 }
1035
1036 return 0;
1037}
1038
1039/**
1040 * Helper function used only by drm_pending_state_apply, with the same
1041 * guarantees and constraints as that function.
1042 */
1043static int
1044drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
1045 enum drm_state_apply_mode mode)
1046{
1047 struct drm_backend *b = pending_state->backend;
1048 struct drm_output_state *output_state, *tmp;
1049 struct drm_plane *plane;
1050 drmModeAtomicReq *req = drmModeAtomicAlloc();
1051 uint32_t flags;
1052 int ret = 0;
1053
1054 if (!req)
1055 return -1;
1056
1057 switch (mode) {
1058 case DRM_STATE_APPLY_SYNC:
1059 flags = 0;
1060 break;
1061 case DRM_STATE_APPLY_ASYNC:
1062 flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
1063 break;
1064 case DRM_STATE_TEST_ONLY:
1065 flags = DRM_MODE_ATOMIC_TEST_ONLY;
1066 break;
1067 }
1068
1069 if (b->state_invalid) {
1070 struct weston_head *head_base;
1071 struct drm_head *head;
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001072 struct drm_crtc *crtc;
Leandro Ribeiroe6369902020-06-17 11:09:47 -03001073 uint32_t connector_id;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001074 int err;
1075
1076 drm_debug(b, "\t\t[atomic] previous state invalid; "
1077 "starting with fresh state\n");
1078
1079 /* If we need to reset all our state (e.g. because we've
1080 * just started, or just been VT-switched in), explicitly
1081 * disable all the CRTCs and connectors we aren't using. */
1082 wl_list_for_each(head_base,
1083 &b->compositor->head_list, compositor_link) {
1084 struct drm_property_info *info;
1085
1086 if (weston_head_is_enabled(head_base))
1087 continue;
1088
1089 head = to_drm_head(head_base);
Leandro Ribeiroe6369902020-06-17 11:09:47 -03001090 connector_id = head->connector.connector_id;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001091
1092 drm_debug(b, "\t\t[atomic] disabling inactive head %s\n",
1093 head_base->name);
1094
Leandro Ribeiroe6369902020-06-17 11:09:47 -03001095 info = &head->connector.props[WDRM_CONNECTOR_CRTC_ID];
1096 err = drmModeAtomicAddProperty(req, connector_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +01001097 info->prop_id, 0);
1098 drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> 0\n",
Leandro Ribeiroe6369902020-06-17 11:09:47 -03001099 (unsigned long) connector_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +01001100 (unsigned long) info->prop_id,
1101 info->name);
1102 if (err <= 0)
1103 ret = -1;
1104 }
1105
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001106 wl_list_for_each(crtc, &b->crtc_list, link) {
Daniel Stone4c2fc702019-06-18 11:12:07 +01001107 struct drm_property_info *info;
1108 drmModeObjectProperties *props;
1109 uint64_t active;
1110
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001111 /* Ignore CRTCs that are in use */
1112 if (crtc->output)
1113 continue;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001114
1115 /* We can't emit a disable on a CRTC that's already
1116 * off, as the kernel will refuse to generate an event
1117 * for an off->off state and fail the commit.
1118 */
1119 props = drmModeObjectGetProperties(b->drm.fd,
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001120 crtc->crtc_id,
Daniel Stone4c2fc702019-06-18 11:12:07 +01001121 DRM_MODE_OBJECT_CRTC);
1122 if (!props) {
1123 ret = -1;
1124 continue;
1125 }
1126
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001127 info = &crtc->props_crtc[WDRM_CRTC_ACTIVE];
Daniel Stone4c2fc702019-06-18 11:12:07 +01001128 active = drm_property_get_value(info, props, 0);
1129 drmModeFreeObjectProperties(props);
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001130 if (active == 0)
Daniel Stone4c2fc702019-06-18 11:12:07 +01001131 continue;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001132
1133 drm_debug(b, "\t\t[atomic] disabling unused CRTC %lu\n",
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001134 (unsigned long) crtc->crtc_id);
Daniel Stone4c2fc702019-06-18 11:12:07 +01001135
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001136 ret |= crtc_add_prop(req, crtc, WDRM_CRTC_ACTIVE, 0);
1137 ret |= crtc_add_prop(req, crtc, WDRM_CRTC_MODE_ID, 0);
Daniel Stone4c2fc702019-06-18 11:12:07 +01001138 }
1139
1140 /* Disable all the planes; planes which are being used will
1141 * override this state in the output-state application. */
1142 wl_list_for_each(plane, &b->plane_list, link) {
1143 drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n",
1144 (unsigned long) plane->plane_id);
1145 plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0);
1146 plane_add_prop(req, plane, WDRM_PLANE_FB_ID, 0);
1147 }
1148
1149 flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
1150 }
1151
1152 wl_list_for_each(output_state, &pending_state->output_list, link) {
1153 if (output_state->output->virtual)
1154 continue;
1155 if (mode == DRM_STATE_APPLY_SYNC)
1156 assert(output_state->dpms == WESTON_DPMS_OFF);
1157 ret |= drm_output_apply_state_atomic(output_state, req, &flags);
1158 }
1159
1160 if (ret != 0) {
1161 weston_log("atomic: couldn't compile atomic state\n");
1162 goto out;
1163 }
1164
1165 ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
1166 drm_debug(b, "[atomic] drmModeAtomicCommit\n");
1167
1168 /* Test commits do not take ownership of the state; return
1169 * without freeing here. */
1170 if (mode == DRM_STATE_TEST_ONLY) {
1171 drmModeAtomicFree(req);
1172 return ret;
1173 }
1174
1175 if (ret != 0) {
1176 weston_log("atomic: couldn't commit new state: %s\n",
1177 strerror(errno));
1178 goto out;
1179 }
1180
1181 wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
1182 link)
1183 drm_output_assign_state(output_state, mode);
1184
1185 b->state_invalid = false;
1186
1187 assert(wl_list_empty(&pending_state->output_list));
1188
1189out:
1190 drmModeAtomicFree(req);
1191 drm_pending_state_free(pending_state);
1192 return ret;
1193}
Daniel Stone4c2fc702019-06-18 11:12:07 +01001194
1195/**
1196 * Tests a pending state, to see if the kernel will accept the update as
1197 * constructed.
1198 *
1199 * Using atomic modesetting, the kernel performs the same checks as it would
1200 * on a real commit, returning success or failure without actually modifying
1201 * the running state. It does not return -EBUSY if there are pending updates
1202 * in flight, so states may be tested at any point, however this means a
1203 * state which passed testing may fail on a real commit if the timing is not
1204 * respected (e.g. committing before the previous commit has completed).
1205 *
1206 * Without atomic modesetting, we have no way to check, so we optimistically
1207 * claim it will work.
1208 *
1209 * Unlike drm_pending_state_apply() and drm_pending_state_apply_sync(), this
1210 * function does _not_ take ownership of pending_state, nor does it clear
1211 * state_invalid.
1212 */
1213int
1214drm_pending_state_test(struct drm_pending_state *pending_state)
1215{
Daniel Stone4c2fc702019-06-18 11:12:07 +01001216 struct drm_backend *b = pending_state->backend;
1217
1218 if (b->atomic_modeset)
1219 return drm_pending_state_apply_atomic(pending_state,
1220 DRM_STATE_TEST_ONLY);
Daniel Stone4c2fc702019-06-18 11:12:07 +01001221
1222 /* We have no way to test state before application on the legacy
1223 * modesetting API, so just claim it succeeded. */
1224 return 0;
1225}
1226
1227/**
1228 * Applies all of a pending_state asynchronously: the primary entry point for
1229 * applying KMS state to a device. Updates the state for all outputs in the
1230 * pending_state, as well as disabling any unclaimed outputs.
1231 *
1232 * Unconditionally takes ownership of pending_state, and clears state_invalid.
1233 */
1234int
1235drm_pending_state_apply(struct drm_pending_state *pending_state)
1236{
1237 struct drm_backend *b = pending_state->backend;
1238 struct drm_output_state *output_state, *tmp;
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001239 struct drm_crtc *crtc;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001240
Daniel Stone4c2fc702019-06-18 11:12:07 +01001241 if (b->atomic_modeset)
1242 return drm_pending_state_apply_atomic(pending_state,
1243 DRM_STATE_APPLY_ASYNC);
Daniel Stone4c2fc702019-06-18 11:12:07 +01001244
1245 if (b->state_invalid) {
1246 /* If we need to reset all our state (e.g. because we've
1247 * just started, or just been VT-switched in), explicitly
1248 * disable all the CRTCs we aren't using. This also disables
1249 * all connectors on these CRTCs, so we don't need to do that
1250 * separately with the pre-atomic API. */
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001251 wl_list_for_each(crtc, &b->crtc_list, link) {
1252 if (crtc->output)
1253 continue;
1254 drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
1255 NULL, 0, NULL);
1256 }
Daniel Stone4c2fc702019-06-18 11:12:07 +01001257 }
1258
1259 wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
1260 link) {
1261 struct drm_output *output = output_state->output;
1262 int ret;
1263
1264 if (output->virtual) {
1265 drm_output_assign_state(output_state,
1266 DRM_STATE_APPLY_ASYNC);
1267 continue;
1268 }
1269
1270 ret = drm_output_apply_state_legacy(output_state);
1271 if (ret != 0) {
1272 weston_log("Couldn't apply state for output %s\n",
1273 output->base.name);
1274 }
1275 }
1276
1277 b->state_invalid = false;
1278
1279 assert(wl_list_empty(&pending_state->output_list));
1280
1281 drm_pending_state_free(pending_state);
1282
1283 return 0;
1284}
1285
1286/**
1287 * The synchronous version of drm_pending_state_apply. May only be used to
1288 * disable outputs. Does so synchronously: the request is guaranteed to have
1289 * completed on return, and the output will not be touched afterwards.
1290 *
1291 * Unconditionally takes ownership of pending_state, and clears state_invalid.
1292 */
1293int
1294drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
1295{
1296 struct drm_backend *b = pending_state->backend;
1297 struct drm_output_state *output_state, *tmp;
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001298 struct drm_crtc *crtc;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001299
Daniel Stone4c2fc702019-06-18 11:12:07 +01001300 if (b->atomic_modeset)
1301 return drm_pending_state_apply_atomic(pending_state,
1302 DRM_STATE_APPLY_SYNC);
Daniel Stone4c2fc702019-06-18 11:12:07 +01001303
1304 if (b->state_invalid) {
1305 /* If we need to reset all our state (e.g. because we've
1306 * just started, or just been VT-switched in), explicitly
1307 * disable all the CRTCs we aren't using. This also disables
1308 * all connectors on these CRTCs, so we don't need to do that
1309 * separately with the pre-atomic API. */
Leandro Ribeiro744c0cb2020-08-13 16:15:58 -03001310 wl_list_for_each(crtc, &b->crtc_list, link) {
1311 if (crtc->output)
1312 continue;
1313 drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
1314 NULL, 0, NULL);
1315 }
Daniel Stone4c2fc702019-06-18 11:12:07 +01001316 }
1317
1318 wl_list_for_each_safe(output_state, tmp, &pending_state->output_list,
1319 link) {
1320 int ret;
1321
1322 assert(output_state->dpms == WESTON_DPMS_OFF);
1323 ret = drm_output_apply_state_legacy(output_state);
1324 if (ret != 0) {
1325 weston_log("Couldn't apply state for output %s\n",
1326 output_state->output->base.name);
1327 }
1328 }
1329
1330 b->state_invalid = false;
1331
1332 assert(wl_list_empty(&pending_state->output_list));
1333
1334 drm_pending_state_free(pending_state);
1335
1336 return 0;
1337}
1338
1339void
1340drm_output_update_msc(struct drm_output *output, unsigned int seq)
1341{
1342 uint64_t msc_hi = output->base.msc >> 32;
1343
1344 if (seq < (output->base.msc & 0xffffffff))
1345 msc_hi++;
1346
1347 output->base.msc = (msc_hi << 32) + seq;
1348}
1349
1350static void
1351page_flip_handler(int fd, unsigned int frame,
1352 unsigned int sec, unsigned int usec, void *data)
1353{
1354 struct drm_output *output = data;
1355 struct drm_backend *b = to_drm_backend(output->base.compositor);
1356 uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
1357 WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
1358 WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
1359
1360 drm_output_update_msc(output, frame);
1361
1362 assert(!b->atomic_modeset);
1363 assert(output->page_flip_pending);
Emmanuel Gil Peyrot1b3ad092019-12-09 02:50:55 +01001364 output->page_flip_pending = false;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001365
1366 drm_output_update_complete(output, flags, sec, usec);
1367}
1368
Daniel Stone4c2fc702019-06-18 11:12:07 +01001369static void
1370atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
1371 unsigned int usec, unsigned int crtc_id, void *data)
1372{
1373 struct drm_backend *b = data;
Leandro Ribeirob00d1a22020-08-13 14:12:28 -03001374 struct drm_crtc *crtc;
1375 struct drm_output *output;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001376 uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
1377 WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
1378 WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
1379
Leandro Ribeirob00d1a22020-08-13 14:12:28 -03001380 crtc = drm_crtc_find(b, crtc_id);
1381 assert(crtc);
1382
1383 output = crtc->output;
1384
Daniel Stone4c2fc702019-06-18 11:12:07 +01001385 /* During the initial modeset, we can disable CRTCs which we don't
1386 * actually handle during normal operation; this will give us events
1387 * for unknown outputs. Ignore them. */
1388 if (!output || !output->base.enabled)
1389 return;
1390
1391 drm_output_update_msc(output, frame);
1392
1393 drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id);
1394 assert(b->atomic_modeset);
1395 assert(output->atomic_complete_pending);
Emmanuel Gil Peyrot1b3ad092019-12-09 02:50:55 +01001396 output->atomic_complete_pending = false;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001397
1398 drm_output_update_complete(output, flags, sec, usec);
1399 drm_debug(b, "[atomic][CRTC:%u] flip processing completed\n", crtc_id);
1400}
Daniel Stone4c2fc702019-06-18 11:12:07 +01001401
1402int
1403on_drm_input(int fd, uint32_t mask, void *data)
1404{
Daniel Stone4c2fc702019-06-18 11:12:07 +01001405 struct drm_backend *b = data;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001406 drmEventContext evctx;
1407
1408 memset(&evctx, 0, sizeof evctx);
Daniel Stone4c2fc702019-06-18 11:12:07 +01001409 evctx.version = 3;
1410 if (b->atomic_modeset)
1411 evctx.page_flip_handler2 = atomic_flip_handler;
1412 else
Daniel Stone4c2fc702019-06-18 11:12:07 +01001413 evctx.page_flip_handler = page_flip_handler;
1414 drmHandleEvent(fd, &evctx);
1415
1416 return 1;
1417}
1418
1419int
1420init_kms_caps(struct drm_backend *b)
1421{
1422 uint64_t cap;
1423 int ret;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001424
1425 weston_log("using %s\n", b->drm.filename);
1426
1427 ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
Pekka Paalanenf48277b2021-05-10 11:44:57 +03001428 if (ret != 0 || cap != 1) {
1429 weston_log("Error: kernel DRM KMS does not support DRM_CAP_TIMESTAMP_MONOTONIC.\n");
1430 return -1;
1431 }
Daniel Stone4c2fc702019-06-18 11:12:07 +01001432
Pekka Paalanenf48277b2021-05-10 11:44:57 +03001433 if (weston_compositor_set_presentation_clock(b->compositor, CLOCK_MONOTONIC) < 0) {
1434 weston_log("Error: failed to set presentation clock to CLOCK_MONOTONIC.\n");
Daniel Stone4c2fc702019-06-18 11:12:07 +01001435 return -1;
1436 }
1437
1438 ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
1439 if (ret == 0)
1440 b->cursor_width = cap;
1441 else
1442 b->cursor_width = 64;
1443
1444 ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
1445 if (ret == 0)
1446 b->cursor_height = cap;
1447 else
1448 b->cursor_height = 64;
1449
1450 if (!getenv("WESTON_DISABLE_UNIVERSAL_PLANES")) {
1451 ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
1452 b->universal_planes = (ret == 0);
1453 }
Daniel Stone4c2fc702019-06-18 11:12:07 +01001454
Daniel Stone4c2fc702019-06-18 11:12:07 +01001455 if (b->universal_planes && !getenv("WESTON_DISABLE_ATOMIC")) {
1456 ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
1457 if (ret != 0)
1458 cap = 0;
1459 ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
1460 b->atomic_modeset = ((ret == 0) && (cap == 1));
1461 }
Daniel Stone4c2fc702019-06-18 11:12:07 +01001462 weston_log("DRM: %s atomic modesetting\n",
1463 b->atomic_modeset ? "supports" : "does not support");
1464
Stefan Agner465ab2c2020-06-17 23:36:44 +02001465 if (!getenv("WESTON_DISABLE_GBM_MODIFIERS")) {
1466 ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
1467 if (ret == 0)
1468 b->fb_modifiers = cap;
1469 }
1470 weston_log("DRM: %s GBM modifiers\n",
1471 b->fb_modifiers ? "supports" : "does not support");
Daniel Stone4c2fc702019-06-18 11:12:07 +01001472
Leandro Ribeiro96bef052020-09-09 15:23:49 -03001473 drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
1474
Daniel Stone4c2fc702019-06-18 11:12:07 +01001475 /*
1476 * KMS support for hardware planes cannot properly synchronize
1477 * without nuclear page flip. Without nuclear/atomic, hw plane
1478 * and cursor plane updates would either tear or cause extra
1479 * waits for vblanks which means dropping the compositor framerate
1480 * to a fraction. For cursors, it's not so bad, so they are
1481 * enabled.
1482 */
1483 if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
Emmanuel Gil Peyrot1b3ad092019-12-09 02:50:55 +01001484 b->sprites_are_broken = true;
Daniel Stone4c2fc702019-06-18 11:12:07 +01001485
1486 ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
1487 b->aspect_ratio_supported = (ret == 0);
1488 weston_log("DRM: %s picture aspect ratio\n",
1489 b->aspect_ratio_supported ? "supports" : "does not support");
1490
1491 return 0;
1492}