1 /*
2 * Copyright (C) 2019 Red Hat
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19
20 #include "config.h"
21
22 #include "backends/native/meta-kms-impl-device.h"
23
24 #include <errno.h>
25 #include <xf86drm.h>
26
27 #include "backends/native/meta-backend-native.h"
28 #include "backends/native/meta-device-pool.h"
29 #include "backends/native/meta-kms-connector-private.h"
30 #include "backends/native/meta-kms-connector.h"
31 #include "backends/native/meta-kms-crtc-private.h"
32 #include "backends/native/meta-kms-crtc.h"
33 #include "backends/native/meta-kms-impl.h"
34 #include "backends/native/meta-kms-mode-private.h"
35 #include "backends/native/meta-kms-page-flip-private.h"
36 #include "backends/native/meta-kms-plane-private.h"
37 #include "backends/native/meta-kms-plane.h"
38 #include "backends/native/meta-kms-private.h"
39
40 #include "meta-default-modes.h"
41 #include "meta-private-enum-types.h"
42
43 enum
44 {
45 PROP_0,
46
47 PROP_DEVICE,
48 PROP_IMPL,
49 PROP_PATH,
50 PROP_FLAGS,
51
52 N_PROPS
53 };
54
55 static GParamSpec *obj_props[N_PROPS];
56
57 typedef struct _MetaKmsImplDevicePrivate
58 {
59 MetaKmsDevice *device;
60 MetaKmsImpl *impl;
61
62 int fd_hold_count;
63 MetaDeviceFile *device_file;
64 GSource *fd_source;
65 char *path;
66 MetaKmsDeviceFlag flags;
67 gboolean has_latched_fd_hold;
68
69 char *driver_name;
70 char *driver_description;
71
72 GList *crtcs;
73 GList *connectors;
74 GList *planes;
75
76 MetaKmsDeviceCaps caps;
77
78 GList *fallback_modes;
79 } MetaKmsImplDevicePrivate;
80
81 static void
82 initable_iface_init (GInitableIface *iface);
83
G_DEFINE_TYPE_WITH_CODE(MetaKmsImplDevice,meta_kms_impl_device,G_TYPE_OBJECT,G_ADD_PRIVATE (MetaKmsImplDevice)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,initable_iface_init))84 G_DEFINE_TYPE_WITH_CODE (MetaKmsImplDevice, meta_kms_impl_device,
85 G_TYPE_OBJECT,
86 G_ADD_PRIVATE (MetaKmsImplDevice)
87 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
88 initable_iface_init))
89
90 G_DEFINE_QUARK (-meta-kms-error-quark, meta_kms_error)
91
92 MetaKmsDevice *
93 meta_kms_impl_device_get_device (MetaKmsImplDevice *impl_device)
94 {
95 MetaKmsImplDevicePrivate *priv =
96 meta_kms_impl_device_get_instance_private (impl_device);
97
98 return priv->device;
99 }
100
101 GList *
meta_kms_impl_device_copy_connectors(MetaKmsImplDevice * impl_device)102 meta_kms_impl_device_copy_connectors (MetaKmsImplDevice *impl_device)
103 {
104 MetaKmsImplDevicePrivate *priv =
105 meta_kms_impl_device_get_instance_private (impl_device);
106
107 return g_list_copy (priv->connectors);
108 }
109
110 GList *
meta_kms_impl_device_copy_crtcs(MetaKmsImplDevice * impl_device)111 meta_kms_impl_device_copy_crtcs (MetaKmsImplDevice *impl_device)
112 {
113 MetaKmsImplDevicePrivate *priv =
114 meta_kms_impl_device_get_instance_private (impl_device);
115
116 return g_list_copy (priv->crtcs);
117 }
118
119 GList *
meta_kms_impl_device_copy_planes(MetaKmsImplDevice * impl_device)120 meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device)
121 {
122 MetaKmsImplDevicePrivate *priv =
123 meta_kms_impl_device_get_instance_private (impl_device);
124
125 return g_list_copy (priv->planes);
126 }
127
128 GList *
meta_kms_impl_device_peek_connectors(MetaKmsImplDevice * impl_device)129 meta_kms_impl_device_peek_connectors (MetaKmsImplDevice *impl_device)
130 {
131 MetaKmsImplDevicePrivate *priv =
132 meta_kms_impl_device_get_instance_private (impl_device);
133
134 return priv->connectors;
135 }
136
137 GList *
meta_kms_impl_device_peek_crtcs(MetaKmsImplDevice * impl_device)138 meta_kms_impl_device_peek_crtcs (MetaKmsImplDevice *impl_device)
139 {
140 MetaKmsImplDevicePrivate *priv =
141 meta_kms_impl_device_get_instance_private (impl_device);
142
143 return priv->crtcs;
144 }
145
146 GList *
meta_kms_impl_device_peek_planes(MetaKmsImplDevice * impl_device)147 meta_kms_impl_device_peek_planes (MetaKmsImplDevice *impl_device)
148 {
149 MetaKmsImplDevicePrivate *priv =
150 meta_kms_impl_device_get_instance_private (impl_device);
151
152 return priv->planes;
153 }
154
155 const MetaKmsDeviceCaps *
meta_kms_impl_device_get_caps(MetaKmsImplDevice * impl_device)156 meta_kms_impl_device_get_caps (MetaKmsImplDevice *impl_device)
157 {
158 MetaKmsImplDevicePrivate *priv =
159 meta_kms_impl_device_get_instance_private (impl_device);
160
161 return &priv->caps;
162 }
163
164 GList *
meta_kms_impl_device_copy_fallback_modes(MetaKmsImplDevice * impl_device)165 meta_kms_impl_device_copy_fallback_modes (MetaKmsImplDevice *impl_device)
166 {
167 MetaKmsImplDevicePrivate *priv =
168 meta_kms_impl_device_get_instance_private (impl_device);
169
170 return g_list_copy (priv->fallback_modes);
171 }
172
173 const char *
meta_kms_impl_device_get_driver_name(MetaKmsImplDevice * impl_device)174 meta_kms_impl_device_get_driver_name (MetaKmsImplDevice *impl_device)
175 {
176 MetaKmsImplDevicePrivate *priv =
177 meta_kms_impl_device_get_instance_private (impl_device);
178
179 return priv->driver_name;
180 }
181
182 const char *
meta_kms_impl_device_get_driver_description(MetaKmsImplDevice * impl_device)183 meta_kms_impl_device_get_driver_description (MetaKmsImplDevice *impl_device)
184 {
185 MetaKmsImplDevicePrivate *priv =
186 meta_kms_impl_device_get_instance_private (impl_device);
187
188 return priv->driver_description;
189 }
190
191 const char *
meta_kms_impl_device_get_path(MetaKmsImplDevice * impl_device)192 meta_kms_impl_device_get_path (MetaKmsImplDevice *impl_device)
193 {
194 MetaKmsImplDevicePrivate *priv =
195 meta_kms_impl_device_get_instance_private (impl_device);
196
197 return priv->path;
198 }
199
200 gboolean
meta_kms_impl_device_dispatch(MetaKmsImplDevice * impl_device,GError ** error)201 meta_kms_impl_device_dispatch (MetaKmsImplDevice *impl_device,
202 GError **error)
203 {
204 MetaKmsImplDevicePrivate *priv =
205 meta_kms_impl_device_get_instance_private (impl_device);
206 MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
207 int fd;
208
209 drmEventContext drm_event_context;
210
211 meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
212
213 drm_event_context = (drmEventContext) { 0 };
214 klass->setup_drm_event_context (impl_device, &drm_event_context);
215
216 fd = meta_device_file_get_fd (priv->device_file);
217
218 while (TRUE)
219 {
220 if (drmHandleEvent (fd, &drm_event_context) != 0)
221 {
222 struct pollfd pfd;
223 int ret;
224
225 if (errno != EAGAIN)
226 {
227 g_set_error_literal (error, G_IO_ERROR,
228 g_io_error_from_errno (errno),
229 strerror (errno));
230 return FALSE;
231 }
232
233 pfd.fd = fd;
234 pfd.events = POLL_IN | POLL_ERR;
235 do
236 {
237 ret = poll (&pfd, 1, -1);
238 }
239 while (ret == -1 && errno == EINTR);
240 }
241 else
242 {
243 break;
244 }
245 }
246
247 return TRUE;
248 }
249
250 static gpointer
kms_event_dispatch_in_impl(MetaKmsImpl * impl,gpointer user_data,GError ** error)251 kms_event_dispatch_in_impl (MetaKmsImpl *impl,
252 gpointer user_data,
253 GError **error)
254 {
255 MetaKmsImplDevice *impl_device = user_data;
256 gboolean ret;
257
258 ret = meta_kms_impl_device_dispatch (impl_device, error);
259 return GINT_TO_POINTER (ret);
260 }
261
262 drmModePropertyPtr
meta_kms_impl_device_find_property(MetaKmsImplDevice * impl_device,drmModeObjectProperties * props,const char * prop_name,int * out_idx)263 meta_kms_impl_device_find_property (MetaKmsImplDevice *impl_device,
264 drmModeObjectProperties *props,
265 const char *prop_name,
266 int *out_idx)
267 {
268 MetaKmsImplDevicePrivate *priv =
269 meta_kms_impl_device_get_instance_private (impl_device);
270 int fd;
271 unsigned int i;
272
273 meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
274
275 fd = meta_device_file_get_fd (priv->device_file);
276
277 for (i = 0; i < props->count_props; i++)
278 {
279 drmModePropertyPtr prop;
280
281 prop = drmModeGetProperty (fd, props->props[i]);
282 if (!prop)
283 continue;
284
285 if (strcmp (prop->name, prop_name) == 0)
286 {
287 *out_idx = i;
288 return prop;
289 }
290
291 drmModeFreeProperty (prop);
292 }
293
294 return NULL;
295 }
296
297 static void
init_caps(MetaKmsImplDevice * impl_device)298 init_caps (MetaKmsImplDevice *impl_device)
299 {
300 MetaKmsImplDevicePrivate *priv =
301 meta_kms_impl_device_get_instance_private (impl_device);
302 int fd;
303 uint64_t cursor_width, cursor_height;
304 uint64_t prefer_shadow;
305 uint64_t uses_monotonic_clock;
306
307 fd = meta_device_file_get_fd (priv->device_file);
308 if (drmGetCap (fd, DRM_CAP_CURSOR_WIDTH, &cursor_width) == 0 &&
309 drmGetCap (fd, DRM_CAP_CURSOR_HEIGHT, &cursor_height) == 0)
310 {
311 priv->caps.has_cursor_size = TRUE;
312 priv->caps.cursor_width = cursor_width;
313 priv->caps.cursor_height = cursor_height;
314 }
315
316 if (drmGetCap (fd, DRM_CAP_DUMB_PREFER_SHADOW, &prefer_shadow) == 0)
317 {
318 if (prefer_shadow)
319 g_message ("Device '%s' prefers shadow buffer", priv->path);
320
321 priv->caps.prefers_shadow_buffer = prefer_shadow;
322 }
323
324 if (drmGetCap (fd, DRM_CAP_TIMESTAMP_MONOTONIC, &uses_monotonic_clock) == 0)
325 {
326 priv->caps.uses_monotonic_clock = uses_monotonic_clock;
327 }
328 }
329
330 static void
init_crtcs(MetaKmsImplDevice * impl_device,drmModeRes * drm_resources)331 init_crtcs (MetaKmsImplDevice *impl_device,
332 drmModeRes *drm_resources)
333 {
334 MetaKmsImplDevicePrivate *priv =
335 meta_kms_impl_device_get_instance_private (impl_device);
336 int idx;
337 int fd;
338
339 fd = meta_device_file_get_fd (priv->device_file);
340
341 for (idx = 0; idx < drm_resources->count_crtcs; idx++)
342 {
343 uint32_t crtc_id;
344 drmModeCrtc *drm_crtc;
345 MetaKmsCrtc *crtc;
346 g_autoptr (GError) error = NULL;
347
348 crtc_id = drm_resources->crtcs[idx];
349 drm_crtc = drmModeGetCrtc (fd, crtc_id);
350 if (!drm_crtc)
351 {
352 g_warning ("Failed to get CRTC %u info on '%s': %s",
353 crtc_id, priv->path, error->message);
354 continue;
355 }
356
357 crtc = meta_kms_crtc_new (impl_device, drm_crtc, idx, &error);
358
359 drmModeFreeCrtc (drm_crtc);
360
361 if (!crtc)
362 {
363 g_warning ("Failed to create CRTC for %u on '%s': %s",
364 crtc_id, priv->path, error->message);
365 continue;
366 }
367
368 priv->crtcs = g_list_prepend (priv->crtcs, crtc);
369 }
370 priv->crtcs = g_list_reverse (priv->crtcs);
371 }
372
373 static MetaKmsConnector *
find_existing_connector(MetaKmsImplDevice * impl_device,drmModeConnector * drm_connector)374 find_existing_connector (MetaKmsImplDevice *impl_device,
375 drmModeConnector *drm_connector)
376 {
377 MetaKmsImplDevicePrivate *priv =
378 meta_kms_impl_device_get_instance_private (impl_device);
379 GList *l;
380
381 for (l = priv->connectors; l; l = l->next)
382 {
383 MetaKmsConnector *connector = l->data;
384
385 if (meta_kms_connector_is_same_as (connector, drm_connector))
386 return connector;
387 }
388
389 return NULL;
390 }
391
392 static MetaKmsUpdateChanges
update_connectors(MetaKmsImplDevice * impl_device,drmModeRes * drm_resources)393 update_connectors (MetaKmsImplDevice *impl_device,
394 drmModeRes *drm_resources)
395 {
396 MetaKmsImplDevicePrivate *priv =
397 meta_kms_impl_device_get_instance_private (impl_device);
398 g_autolist (MetaKmsConnector) connectors = NULL;
399 gboolean needs_full_change = FALSE;
400 unsigned int i;
401 int fd;
402
403 fd = meta_device_file_get_fd (priv->device_file);
404
405 for (i = 0; i < drm_resources->count_connectors; i++)
406 {
407 drmModeConnector *drm_connector;
408 MetaKmsConnector *connector;
409
410 drm_connector = drmModeGetConnector (fd, drm_resources->connectors[i]);
411 if (!drm_connector)
412 continue;
413
414 connector = find_existing_connector (impl_device, drm_connector);
415 if (connector)
416 {
417 connector = g_object_ref (connector);
418 }
419 else
420 {
421 connector = meta_kms_connector_new (impl_device, drm_connector,
422 drm_resources);
423 needs_full_change = TRUE;
424 }
425
426 drmModeFreeConnector (drm_connector);
427
428 connectors = g_list_prepend (connectors, connector);
429 }
430
431 if (!needs_full_change)
432 return META_KMS_UPDATE_CHANGE_NONE;
433
434 g_list_free_full (priv->connectors, g_object_unref);
435 priv->connectors = g_list_reverse (g_steal_pointer (&connectors));
436
437 return META_KMS_UPDATE_CHANGE_FULL;
438 }
439
440 static MetaKmsPlaneType
get_plane_type(MetaKmsImplDevice * impl_device,drmModeObjectProperties * props)441 get_plane_type (MetaKmsImplDevice *impl_device,
442 drmModeObjectProperties *props)
443 {
444 drmModePropertyPtr prop;
445 int idx;
446
447 prop = meta_kms_impl_device_find_property (impl_device, props, "type", &idx);
448 if (!prop)
449 return FALSE;
450 drmModeFreeProperty (prop);
451
452 switch (props->prop_values[idx])
453 {
454 case DRM_PLANE_TYPE_PRIMARY:
455 return META_KMS_PLANE_TYPE_PRIMARY;
456 case DRM_PLANE_TYPE_CURSOR:
457 return META_KMS_PLANE_TYPE_CURSOR;
458 case DRM_PLANE_TYPE_OVERLAY:
459 return META_KMS_PLANE_TYPE_OVERLAY;
460 default:
461 g_warning ("Unhandled plane type %" G_GUINT64_FORMAT,
462 props->prop_values[idx]);
463 return -1;
464 }
465 }
466
467 MetaKmsPlane *
meta_kms_impl_device_add_fake_plane(MetaKmsImplDevice * impl_device,MetaKmsPlaneType plane_type,MetaKmsCrtc * crtc)468 meta_kms_impl_device_add_fake_plane (MetaKmsImplDevice *impl_device,
469 MetaKmsPlaneType plane_type,
470 MetaKmsCrtc *crtc)
471 {
472 MetaKmsImplDevicePrivate *priv =
473 meta_kms_impl_device_get_instance_private (impl_device);
474 MetaKmsPlane *plane;
475
476 plane = meta_kms_plane_new_fake (plane_type, crtc);
477 priv->planes = g_list_append (priv->planes, plane);
478
479 return plane;
480 }
481
482 static MetaKmsProp *
find_prop(MetaKmsProp * props,int n_props,const char * name)483 find_prop (MetaKmsProp *props,
484 int n_props,
485 const char *name)
486 {
487 int i;
488
489 for (i = 0; i < n_props; i++)
490 {
491 MetaKmsProp *prop = &props[i];
492
493 g_warn_if_fail (prop->name);
494
495 if (g_strcmp0 (prop->name, name) == 0)
496 return prop;
497 }
498
499 return NULL;
500 }
501
502 void
meta_kms_impl_device_init_prop_table(MetaKmsImplDevice * impl_device,uint32_t * drm_props,uint64_t * drm_prop_values,int n_drm_props,MetaKmsProp * props,int n_props,gpointer user_data)503 meta_kms_impl_device_init_prop_table (MetaKmsImplDevice *impl_device,
504 uint32_t *drm_props,
505 uint64_t *drm_prop_values,
506 int n_drm_props,
507 MetaKmsProp *props,
508 int n_props,
509 gpointer user_data)
510 {
511 int fd;
512 uint32_t i;
513
514 fd = meta_kms_impl_device_get_fd (impl_device);
515
516 for (i = 0; i < n_drm_props; i++)
517 {
518 drmModePropertyRes *drm_prop;
519 MetaKmsProp *prop;
520
521 drm_prop = drmModeGetProperty (fd, drm_props[i]);
522 if (!drm_prop)
523 continue;
524
525 prop = find_prop (props, n_props, drm_prop->name);
526 if (!prop)
527 {
528 drmModeFreeProperty (drm_prop);
529 continue;
530 }
531
532 if (!(drm_prop->flags & prop->type))
533 {
534 g_warning ("DRM property '%s' (%u) had unexpected flags (0x%x), "
535 "ignoring",
536 drm_prop->name, drm_props[i], drm_prop->flags);
537 drmModeFreeProperty (drm_prop);
538 continue;
539 }
540
541 prop->prop_id = drm_props[i];
542
543 if (prop->parse)
544 {
545 prop->parse (impl_device, prop,
546 drm_prop, drm_prop_values[i],
547 user_data);
548 }
549
550 drmModeFreeProperty (drm_prop);
551 }
552 }
553
554 static void
init_planes(MetaKmsImplDevice * impl_device)555 init_planes (MetaKmsImplDevice *impl_device)
556 {
557 MetaKmsImplDevicePrivate *priv =
558 meta_kms_impl_device_get_instance_private (impl_device);
559 int fd;
560 drmModePlaneRes *drm_planes;
561 unsigned int i;
562
563 fd = meta_device_file_get_fd (priv->device_file);
564
565 drm_planes = drmModeGetPlaneResources (fd);
566 if (!drm_planes)
567 return;
568
569 for (i = 0; i < drm_planes->count_planes; i++)
570 {
571 drmModePlane *drm_plane;
572 drmModeObjectProperties *props;
573
574 drm_plane = drmModeGetPlane (fd, drm_planes->planes[i]);
575 if (!drm_plane)
576 continue;
577
578 props = drmModeObjectGetProperties (fd,
579 drm_plane->plane_id,
580 DRM_MODE_OBJECT_PLANE);
581 if (props)
582 {
583 MetaKmsPlaneType plane_type;
584
585 plane_type = get_plane_type (impl_device, props);
586 if (plane_type != -1)
587 {
588 MetaKmsPlane *plane;
589
590 plane = meta_kms_plane_new (plane_type,
591 impl_device,
592 drm_plane, props);
593
594 priv->planes = g_list_prepend (priv->planes, plane);
595 }
596 }
597
598 g_clear_pointer (&props, drmModeFreeObjectProperties);
599 drmModeFreePlane (drm_plane);
600 }
601 priv->planes = g_list_reverse (priv->planes);
602 }
603
604 static void
init_fallback_modes(MetaKmsImplDevice * impl_device)605 init_fallback_modes (MetaKmsImplDevice *impl_device)
606 {
607 MetaKmsImplDevicePrivate *priv =
608 meta_kms_impl_device_get_instance_private (impl_device);
609 GList *modes = NULL;
610 int i;
611
612 for (i = 0; i < G_N_ELEMENTS (meta_default_landscape_drm_mode_infos); i++)
613 {
614 MetaKmsMode *mode;
615
616 mode = meta_kms_mode_new (impl_device,
617 &meta_default_landscape_drm_mode_infos[i],
618 META_KMS_MODE_FLAG_FALLBACK_LANDSCAPE);
619 modes = g_list_prepend (modes, mode);
620 }
621
622 for (i = 0; i < G_N_ELEMENTS (meta_default_portrait_drm_mode_infos); i++)
623 {
624 MetaKmsMode *mode;
625
626 mode = meta_kms_mode_new (impl_device,
627 &meta_default_portrait_drm_mode_infos[i],
628 META_KMS_MODE_FLAG_FALLBACK_PORTRAIT);
629 modes = g_list_prepend (modes, mode);
630 }
631
632 priv->fallback_modes = g_list_reverse (modes);
633 }
634
635 static MetaDeviceFile *
meta_kms_impl_device_open_device_file(MetaKmsImplDevice * impl_device,const char * path,GError ** error)636 meta_kms_impl_device_open_device_file (MetaKmsImplDevice *impl_device,
637 const char *path,
638 GError **error)
639 {
640 MetaKmsImplDevicePrivate *priv =
641 meta_kms_impl_device_get_instance_private (impl_device);
642 MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
643
644 return klass->open_device_file (impl_device, priv->path, error);
645 }
646
647 static gboolean
ensure_device_file(MetaKmsImplDevice * impl_device,GError ** error)648 ensure_device_file (MetaKmsImplDevice *impl_device,
649 GError **error)
650 {
651 MetaKmsImplDevicePrivate *priv =
652 meta_kms_impl_device_get_instance_private (impl_device);
653 MetaDeviceFile *device_file;
654
655 if (priv->device_file)
656 return TRUE;
657
658 device_file = meta_kms_impl_device_open_device_file (impl_device,
659 priv->path,
660 error);
661 if (!device_file)
662 return FALSE;
663
664 priv->device_file = device_file;
665
666 if (!(priv->flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING))
667 {
668 priv->fd_source =
669 meta_kms_register_fd_in_impl (meta_kms_impl_get_kms (priv->impl),
670 meta_device_file_get_fd (device_file),
671 kms_event_dispatch_in_impl,
672 impl_device);
673 }
674
675 return TRUE;
676 }
677
678 static void
ensure_latched_fd_hold(MetaKmsImplDevice * impl_device)679 ensure_latched_fd_hold (MetaKmsImplDevice *impl_device)
680 {
681 MetaKmsImplDevicePrivate *priv =
682 meta_kms_impl_device_get_instance_private (impl_device);
683
684 if (!priv->has_latched_fd_hold)
685 {
686 meta_kms_impl_device_hold_fd (impl_device);
687 priv->has_latched_fd_hold = TRUE;
688 }
689 }
690
691 static void
clear_latched_fd_hold(MetaKmsImplDevice * impl_device)692 clear_latched_fd_hold (MetaKmsImplDevice *impl_device)
693 {
694 MetaKmsImplDevicePrivate *priv =
695 meta_kms_impl_device_get_instance_private (impl_device);
696
697 if (priv->has_latched_fd_hold)
698 {
699 meta_kms_impl_device_unhold_fd (impl_device);
700 priv->has_latched_fd_hold = FALSE;
701 }
702 }
703
704 MetaKmsUpdateChanges
meta_kms_impl_device_update_states(MetaKmsImplDevice * impl_device,uint32_t crtc_id,uint32_t connector_id)705 meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device,
706 uint32_t crtc_id,
707 uint32_t connector_id)
708 {
709 MetaKmsImplDevicePrivate *priv =
710 meta_kms_impl_device_get_instance_private (impl_device);
711 g_autoptr (GError) error = NULL;
712 int fd;
713 drmModeRes *drm_resources;
714 MetaKmsUpdateChanges changes;
715 GList *l;
716
717 meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
718
719 meta_topic (META_DEBUG_KMS, "Updating device state for %s", priv->path);
720
721 if (!ensure_device_file (impl_device, &error))
722 {
723 g_warning ("Failed to reopen '%s': %s", priv->path, error->message);
724 goto err;
725 }
726
727 ensure_latched_fd_hold (impl_device);
728
729 fd = meta_device_file_get_fd (priv->device_file);
730 drm_resources = drmModeGetResources (fd);
731 if (!drm_resources)
732 {
733 meta_topic (META_DEBUG_KMS, "Device '%s' didn't return any resources",
734 priv->path);
735 goto err;
736 }
737
738 changes = update_connectors (impl_device, drm_resources);
739
740 for (l = priv->crtcs; l; l = l->next)
741 {
742 MetaKmsCrtc *crtc = META_KMS_CRTC (l->data);
743
744 if (crtc_id > 0 &&
745 meta_kms_crtc_get_id (crtc) != crtc_id)
746 continue;
747
748 changes |= meta_kms_crtc_update_state (crtc);
749 }
750
751 for (l = priv->connectors; l; l = l->next)
752 {
753 MetaKmsConnector *connector = META_KMS_CONNECTOR (l->data);
754
755 if (connector_id > 0 &&
756 meta_kms_connector_get_id (connector) != connector_id)
757 continue;
758
759 changes |= meta_kms_connector_update_state (connector, drm_resources);
760 }
761
762 drmModeFreeResources (drm_resources);
763
764 return changes;
765
766 err:
767 g_clear_list (&priv->planes, g_object_unref);
768 g_clear_list (&priv->crtcs, g_object_unref);
769 g_clear_list (&priv->connectors, g_object_unref);
770
771 return META_KMS_UPDATE_CHANGE_FULL;
772 }
773
774 void
meta_kms_impl_device_predict_states(MetaKmsImplDevice * impl_device,MetaKmsUpdate * update)775 meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device,
776 MetaKmsUpdate *update)
777 {
778 MetaKmsImplDevicePrivate *priv =
779 meta_kms_impl_device_get_instance_private (impl_device);
780
781 g_list_foreach (priv->crtcs, (GFunc) meta_kms_crtc_predict_state,
782 update);
783 g_list_foreach (priv->connectors, (GFunc) meta_kms_connector_predict_state,
784 update);
785 }
786
787 void
meta_kms_impl_device_notify_modes_set(MetaKmsImplDevice * impl_device)788 meta_kms_impl_device_notify_modes_set (MetaKmsImplDevice *impl_device)
789 {
790 clear_latched_fd_hold (impl_device);
791 }
792
793 int
meta_kms_impl_device_get_fd(MetaKmsImplDevice * impl_device)794 meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device)
795 {
796 MetaKmsImplDevicePrivate *priv =
797 meta_kms_impl_device_get_instance_private (impl_device);
798
799 meta_assert_in_kms_impl (meta_kms_impl_get_kms (priv->impl));
800
801 return meta_device_file_get_fd (priv->device_file);
802 }
803
804 MetaKmsFeedback *
meta_kms_impl_device_process_update(MetaKmsImplDevice * impl_device,MetaKmsUpdate * update,MetaKmsUpdateFlag flags)805 meta_kms_impl_device_process_update (MetaKmsImplDevice *impl_device,
806 MetaKmsUpdate *update,
807 MetaKmsUpdateFlag flags)
808 {
809 MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
810 MetaKmsFeedback *feedback;
811 g_autoptr (GError) error = NULL;
812
813 if (!ensure_device_file (impl_device, &error))
814 return meta_kms_feedback_new_failed (NULL, g_steal_pointer (&error));
815
816 meta_kms_impl_device_hold_fd (impl_device);
817 feedback = klass->process_update (impl_device, update, flags);
818 meta_kms_impl_device_unhold_fd (impl_device);
819
820 return feedback;
821 }
822
823 void
meta_kms_impl_device_handle_page_flip_callback(MetaKmsImplDevice * impl_device,MetaKmsPageFlipData * page_flip_data)824 meta_kms_impl_device_handle_page_flip_callback (MetaKmsImplDevice *impl_device,
825 MetaKmsPageFlipData *page_flip_data)
826 {
827 MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
828
829 klass->handle_page_flip_callback (impl_device, page_flip_data);
830 }
831
832 void
meta_kms_impl_device_discard_pending_page_flips(MetaKmsImplDevice * impl_device)833 meta_kms_impl_device_discard_pending_page_flips (MetaKmsImplDevice *impl_device)
834 {
835 MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
836
837 klass->discard_pending_page_flips (impl_device);
838 }
839
840 void
meta_kms_impl_device_hold_fd(MetaKmsImplDevice * impl_device)841 meta_kms_impl_device_hold_fd (MetaKmsImplDevice *impl_device)
842 {
843 MetaKmsImplDevicePrivate *priv =
844 meta_kms_impl_device_get_instance_private (impl_device);
845 MetaKms *kms = meta_kms_device_get_kms (priv->device);
846
847 meta_assert_in_kms_impl (kms);
848
849 g_assert (priv->device_file);
850
851 priv->fd_hold_count++;
852 }
853
854 void
meta_kms_impl_device_unhold_fd(MetaKmsImplDevice * impl_device)855 meta_kms_impl_device_unhold_fd (MetaKmsImplDevice *impl_device)
856 {
857 MetaKmsImplDevicePrivate *priv =
858 meta_kms_impl_device_get_instance_private (impl_device);
859 MetaKms *kms = meta_kms_device_get_kms (priv->device);
860
861 meta_assert_in_kms_impl (kms);
862
863 g_return_if_fail (priv->fd_hold_count > 0);
864
865 priv->fd_hold_count--;
866 if (priv->fd_hold_count == 0)
867 {
868 g_clear_pointer (&priv->device_file, meta_device_file_release);
869
870 if (priv->fd_source)
871 {
872 g_source_destroy (priv->fd_source);
873 g_clear_pointer (&priv->fd_source, g_source_unref);
874 }
875 }
876 }
877
878 static void
meta_kms_impl_device_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)879 meta_kms_impl_device_get_property (GObject *object,
880 guint prop_id,
881 GValue *value,
882 GParamSpec *pspec)
883 {
884 MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (object);
885 MetaKmsImplDevicePrivate *priv =
886 meta_kms_impl_device_get_instance_private (impl_device);
887
888 switch (prop_id)
889 {
890 case PROP_DEVICE:
891 g_value_set_object (value, priv->device);
892 break;
893 case PROP_IMPL:
894 g_value_set_object (value, priv->impl);
895 break;
896 case PROP_FLAGS:
897 g_value_set_flags (value, priv->flags);
898 break;
899 default:
900 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
901 break;
902 }
903 }
904
905 static void
meta_kms_impl_device_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)906 meta_kms_impl_device_set_property (GObject *object,
907 guint prop_id,
908 const GValue *value,
909 GParamSpec *pspec)
910 {
911 MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (object);
912 MetaKmsImplDevicePrivate *priv =
913 meta_kms_impl_device_get_instance_private (impl_device);
914
915 switch (prop_id)
916 {
917 case PROP_DEVICE:
918 priv->device = g_value_get_object (value);
919 break;
920 case PROP_IMPL:
921 priv->impl = g_value_get_object (value);
922 break;
923 case PROP_PATH:
924 priv->path = g_value_dup_string (value);
925 break;
926 case PROP_FLAGS:
927 priv->flags = g_value_get_flags (value);
928 break;
929 default:
930 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
931 break;
932 }
933 }
934
935 static void
meta_kms_impl_device_finalize(GObject * object)936 meta_kms_impl_device_finalize (GObject *object)
937 {
938 MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (object);
939 MetaKmsImplDevicePrivate *priv =
940 meta_kms_impl_device_get_instance_private (impl_device);
941
942 meta_kms_impl_remove_impl_device (priv->impl, impl_device);
943
944 g_list_free_full (priv->planes, g_object_unref);
945 g_list_free_full (priv->crtcs, g_object_unref);
946 g_list_free_full (priv->connectors, g_object_unref);
947 g_list_free_full (priv->fallback_modes,
948 (GDestroyNotify) meta_kms_mode_free);
949
950 clear_latched_fd_hold (impl_device);
951 g_warn_if_fail (!priv->device_file);
952
953 g_free (priv->driver_name);
954 g_free (priv->driver_description);
955 g_free (priv->path);
956
957 G_OBJECT_CLASS (meta_kms_impl_device_parent_class)->finalize (object);
958 }
959
960 gboolean
meta_kms_impl_device_init_mode_setting(MetaKmsImplDevice * impl_device,GError ** error)961 meta_kms_impl_device_init_mode_setting (MetaKmsImplDevice *impl_device,
962 GError **error)
963 {
964 MetaKmsImplDevicePrivate *priv =
965 meta_kms_impl_device_get_instance_private (impl_device);
966 int fd;
967 drmModeRes *drm_resources;
968
969 fd = meta_device_file_get_fd (priv->device_file);
970
971 drm_resources = drmModeGetResources (fd);
972 if (!drm_resources)
973 {
974 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
975 "Failed to activate universal planes: %s",
976 g_strerror (errno));
977 return FALSE;
978 }
979
980 init_caps (impl_device);
981
982 init_crtcs (impl_device, drm_resources);
983 init_planes (impl_device);
984
985 init_fallback_modes (impl_device);
986
987 update_connectors (impl_device, drm_resources);
988
989 drmModeFreeResources (drm_resources);
990
991 return TRUE;
992 }
993
994 void
meta_kms_impl_device_prepare_shutdown(MetaKmsImplDevice * impl_device)995 meta_kms_impl_device_prepare_shutdown (MetaKmsImplDevice *impl_device)
996 {
997 MetaKmsImplDeviceClass *klass = META_KMS_IMPL_DEVICE_GET_CLASS (impl_device);
998
999 if (klass->prepare_shutdown)
1000 klass->prepare_shutdown (impl_device);
1001 }
1002
1003 static gboolean
get_driver_info(int fd,char ** name,char ** description)1004 get_driver_info (int fd,
1005 char **name,
1006 char **description)
1007 {
1008 drmVersion *drm_version;
1009
1010 drm_version = drmGetVersion (fd);
1011 if (!drm_version)
1012 return FALSE;
1013
1014 *name = g_strndup (drm_version->name,
1015 drm_version->name_len);
1016 *description = g_strndup (drm_version->desc,
1017 drm_version->desc_len);
1018 drmFreeVersion (drm_version);
1019
1020 return TRUE;
1021 }
1022
1023 static gboolean
meta_kms_impl_device_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)1024 meta_kms_impl_device_initable_init (GInitable *initable,
1025 GCancellable *cancellable,
1026 GError **error)
1027 {
1028 MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (initable);
1029 MetaKmsImplDevicePrivate *priv =
1030 meta_kms_impl_device_get_instance_private (impl_device);
1031 int fd;
1032
1033 if (!ensure_device_file (impl_device, error))
1034 return FALSE;
1035
1036 ensure_latched_fd_hold (impl_device);
1037
1038 g_clear_pointer (&priv->path, g_free);
1039 priv->path = g_strdup (meta_device_file_get_path (priv->device_file));
1040
1041 fd = meta_device_file_get_fd (priv->device_file);
1042 if (!get_driver_info (fd, &priv->driver_name, &priv->driver_description))
1043 {
1044 priv->driver_name = g_strdup ("unknown");
1045 priv->driver_description = g_strdup ("Unknown");
1046 }
1047
1048 return TRUE;
1049 }
1050
1051 static void
meta_kms_impl_device_init(MetaKmsImplDevice * impl_device)1052 meta_kms_impl_device_init (MetaKmsImplDevice *impl_device)
1053 {
1054 }
1055
1056 static void
initable_iface_init(GInitableIface * iface)1057 initable_iface_init (GInitableIface *iface)
1058 {
1059 iface->init = meta_kms_impl_device_initable_init;
1060 }
1061
1062 static void
meta_kms_impl_device_class_init(MetaKmsImplDeviceClass * klass)1063 meta_kms_impl_device_class_init (MetaKmsImplDeviceClass *klass)
1064 {
1065 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1066
1067 object_class->get_property = meta_kms_impl_device_get_property;
1068 object_class->set_property = meta_kms_impl_device_set_property;
1069 object_class->finalize = meta_kms_impl_device_finalize;
1070
1071 obj_props[PROP_DEVICE] =
1072 g_param_spec_object ("device",
1073 "device",
1074 "MetaKmsDevice",
1075 META_TYPE_KMS_DEVICE,
1076 G_PARAM_READWRITE |
1077 G_PARAM_CONSTRUCT_ONLY |
1078 G_PARAM_STATIC_STRINGS);
1079 obj_props[PROP_IMPL] =
1080 g_param_spec_object ("impl",
1081 "impl",
1082 "MetaKmsImpl",
1083 META_TYPE_KMS_IMPL,
1084 G_PARAM_READWRITE |
1085 G_PARAM_CONSTRUCT_ONLY |
1086 G_PARAM_STATIC_STRINGS);
1087 obj_props[PROP_PATH] =
1088 g_param_spec_string ("path",
1089 "path",
1090 "Device path",
1091 NULL,
1092 G_PARAM_WRITABLE |
1093 G_PARAM_CONSTRUCT_ONLY |
1094 G_PARAM_STATIC_STRINGS);
1095 obj_props[PROP_FLAGS] =
1096 g_param_spec_flags ("flags",
1097 "flags",
1098 "KMS impl device flags",
1099 META_TYPE_KMS_DEVICE_FLAG,
1100 META_KMS_DEVICE_FLAG_NONE,
1101 G_PARAM_READWRITE |
1102 G_PARAM_CONSTRUCT_ONLY |
1103 G_PARAM_STATIC_STRINGS);
1104 g_object_class_install_properties (object_class, N_PROPS, obj_props);
1105 }
1106