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