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-connector.h"
23 #include "backends/native/meta-kms-connector-private.h"
24 
25 #include <errno.h>
26 
27 #include "backends/native/meta-kms-crtc.h"
28 #include "backends/native/meta-kms-device-private.h"
29 #include "backends/native/meta-kms-impl-device.h"
30 #include "backends/native/meta-kms-mode-private.h"
31 #include "backends/native/meta-kms-update-private.h"
32 
33 typedef struct _MetaKmsConnectorPropTable
34 {
35   MetaKmsProp props[META_KMS_CONNECTOR_N_PROPS];
36 } MetaKmsConnectorPropTable;
37 
38 struct _MetaKmsConnector
39 {
40   GObject parent;
41 
42   MetaKmsDevice *device;
43 
44   uint32_t id;
45   uint32_t type;
46   uint32_t type_id;
47   char *name;
48 
49   drmModeConnection connection;
50   MetaKmsConnectorState *current_state;
51 
52   MetaKmsConnectorPropTable prop_table;
53 
54   uint32_t edid_blob_id;
55   uint32_t tile_blob_id;
56 
57   gboolean fd_held;
58 };
59 
G_DEFINE_TYPE(MetaKmsConnector,meta_kms_connector,G_TYPE_OBJECT)60 G_DEFINE_TYPE (MetaKmsConnector, meta_kms_connector, G_TYPE_OBJECT)
61 
62 MetaKmsDevice *
63 meta_kms_connector_get_device (MetaKmsConnector *connector)
64 {
65   return connector->device;
66 }
67 
68 uint32_t
meta_kms_connector_get_prop_id(MetaKmsConnector * connector,MetaKmsConnectorProp prop)69 meta_kms_connector_get_prop_id (MetaKmsConnector     *connector,
70                                 MetaKmsConnectorProp  prop)
71 {
72   return connector->prop_table.props[prop].prop_id;
73 }
74 
75 const char *
meta_kms_connector_get_prop_name(MetaKmsConnector * connector,MetaKmsConnectorProp prop)76 meta_kms_connector_get_prop_name (MetaKmsConnector     *connector,
77                                   MetaKmsConnectorProp  prop)
78 {
79   return connector->prop_table.props[prop].name;
80 }
81 
82 uint32_t
meta_kms_connector_get_connector_type(MetaKmsConnector * connector)83 meta_kms_connector_get_connector_type (MetaKmsConnector *connector)
84 {
85   return connector->type;
86 }
87 
88 uint32_t
meta_kms_connector_get_id(MetaKmsConnector * connector)89 meta_kms_connector_get_id (MetaKmsConnector *connector)
90 {
91   return connector->id;
92 }
93 
94 const char *
meta_kms_connector_get_name(MetaKmsConnector * connector)95 meta_kms_connector_get_name (MetaKmsConnector *connector)
96 {
97   return connector->name;
98 }
99 
100 gboolean
meta_kms_connector_can_clone(MetaKmsConnector * connector,MetaKmsConnector * other_connector)101 meta_kms_connector_can_clone (MetaKmsConnector *connector,
102                               MetaKmsConnector *other_connector)
103 {
104   MetaKmsConnectorState *state = connector->current_state;
105   MetaKmsConnectorState *other_state = other_connector->current_state;
106 
107   if (state->common_possible_clones == 0 ||
108       other_state->common_possible_clones == 0)
109     return FALSE;
110 
111   if (state->encoder_device_idxs != other_state->encoder_device_idxs)
112     return FALSE;
113 
114   return TRUE;
115 }
116 
117 const MetaKmsConnectorState *
meta_kms_connector_get_current_state(MetaKmsConnector * connector)118 meta_kms_connector_get_current_state (MetaKmsConnector *connector)
119 {
120   return connector->current_state;
121 }
122 
123 gboolean
meta_kms_connector_is_underscanning_supported(MetaKmsConnector * connector)124 meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector)
125 {
126   uint32_t underscan_prop_id;
127 
128   underscan_prop_id =
129     meta_kms_connector_get_prop_id (connector,
130                                     META_KMS_CONNECTOR_PROP_UNDERSCAN);
131 
132   return underscan_prop_id != 0;
133 }
134 
135 static void
sync_fd_held(MetaKmsConnector * connector,MetaKmsImplDevice * impl_device)136 sync_fd_held (MetaKmsConnector  *connector,
137               MetaKmsImplDevice *impl_device)
138 {
139   gboolean should_hold_fd;
140 
141   should_hold_fd = connector->current_state->current_crtc_id != 0;
142 
143   if (connector->fd_held == should_hold_fd)
144     return;
145 
146   if (should_hold_fd)
147     meta_kms_impl_device_hold_fd (impl_device);
148   else
149     meta_kms_impl_device_unhold_fd (impl_device);
150 
151   connector->fd_held = should_hold_fd;
152 }
153 
154 static void
set_panel_orientation(MetaKmsConnectorState * state,drmModePropertyPtr prop,uint64_t orientation)155 set_panel_orientation (MetaKmsConnectorState *state,
156                        drmModePropertyPtr     prop,
157                        uint64_t               orientation)
158 {
159   const char *name;
160 
161   name = prop->enums[orientation].name;
162   if (strcmp (name, "Upside Down") == 0)
163     {
164       state->panel_orientation_transform = META_MONITOR_TRANSFORM_180;
165     }
166   else if (strcmp (name, "Left Side Up") == 0)
167     {
168       /* Left side up, rotate 90 degrees counter clockwise to correct */
169       state->panel_orientation_transform = META_MONITOR_TRANSFORM_90;
170     }
171   else if (strcmp (name, "Right Side Up") == 0)
172     {
173       /* Right side up, rotate 270 degrees counter clockwise to correct */
174       state->panel_orientation_transform = META_MONITOR_TRANSFORM_270;
175     }
176   else
177     {
178       state->panel_orientation_transform = META_MONITOR_TRANSFORM_NORMAL;
179     }
180 }
181 
182 static void
state_set_properties(MetaKmsConnectorState * state,MetaKmsImplDevice * impl_device,drmModeConnector * drm_connector)183 state_set_properties (MetaKmsConnectorState *state,
184                       MetaKmsImplDevice     *impl_device,
185                       drmModeConnector      *drm_connector)
186 {
187   int fd;
188   int i;
189 
190   fd = meta_kms_impl_device_get_fd (impl_device);
191 
192   for (i = 0; i < drm_connector->count_props; i++)
193     {
194       drmModePropertyPtr prop;
195 
196       prop = drmModeGetProperty (fd, drm_connector->props[i]);
197       if (!prop)
198         continue;
199 
200       if ((prop->flags & DRM_MODE_PROP_RANGE) &&
201           strcmp (prop->name, "suggested X") == 0)
202         state->suggested_x = drm_connector->prop_values[i];
203       else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
204                strcmp (prop->name, "suggested Y") == 0)
205         state->suggested_y = drm_connector->prop_values[i];
206       else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
207                strcmp (prop->name, "hotplug_mode_update") == 0)
208         state->hotplug_mode_update = drm_connector->prop_values[i];
209       else if (strcmp (prop->name, "scaling mode") == 0)
210         state->has_scaling = TRUE;
211       else if ((prop->flags & DRM_MODE_PROP_ENUM) &&
212                strcmp (prop->name, "panel orientation") == 0)
213         set_panel_orientation (state, prop, drm_connector->prop_values[i]);
214       else if ((prop->flags & DRM_MODE_PROP_RANGE) &&
215                strcmp (prop->name, "non-desktop") == 0)
216         state->non_desktop = drm_connector->prop_values[i];
217 
218       drmModeFreeProperty (prop);
219     }
220 }
221 
222 static CoglSubpixelOrder
drm_subpixel_order_to_cogl_subpixel_order(drmModeSubPixel subpixel)223 drm_subpixel_order_to_cogl_subpixel_order (drmModeSubPixel subpixel)
224 {
225   switch (subpixel)
226     {
227     case DRM_MODE_SUBPIXEL_NONE:
228       return COGL_SUBPIXEL_ORDER_NONE;
229       break;
230     case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
231       return COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB;
232       break;
233     case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
234       return COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR;
235       break;
236     case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
237       return COGL_SUBPIXEL_ORDER_VERTICAL_RGB;
238       break;
239     case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
240       return COGL_SUBPIXEL_ORDER_VERTICAL_BGR;
241       break;
242     case DRM_MODE_SUBPIXEL_UNKNOWN:
243       return COGL_SUBPIXEL_ORDER_UNKNOWN;
244     }
245   return COGL_SUBPIXEL_ORDER_UNKNOWN;
246 }
247 
248 static void
state_set_edid(MetaKmsConnectorState * state,MetaKmsConnector * connector,MetaKmsImplDevice * impl_device,uint32_t blob_id)249 state_set_edid (MetaKmsConnectorState *state,
250                 MetaKmsConnector      *connector,
251                 MetaKmsImplDevice     *impl_device,
252                 uint32_t               blob_id)
253 {
254   int fd;
255   drmModePropertyBlobPtr edid_blob;
256   GBytes *edid_data;
257 
258   fd = meta_kms_impl_device_get_fd (impl_device);
259   edid_blob = drmModeGetPropertyBlob (fd, blob_id);
260   if (!edid_blob)
261     {
262       g_warning ("Failed to read EDID of connector %s: %s",
263                  connector->name, g_strerror (errno));
264       return;
265     }
266 
267    edid_data = g_bytes_new (edid_blob->data, edid_blob->length);
268    drmModeFreePropertyBlob (edid_blob);
269 
270    state->edid_data = edid_data;
271 }
272 
273 static void
state_set_tile_info(MetaKmsConnectorState * state,MetaKmsConnector * connector,MetaKmsImplDevice * impl_device,uint32_t blob_id)274 state_set_tile_info (MetaKmsConnectorState *state,
275                      MetaKmsConnector      *connector,
276                      MetaKmsImplDevice     *impl_device,
277                      uint32_t               blob_id)
278 {
279   int fd;
280   drmModePropertyBlobPtr tile_blob;
281 
282   state->tile_info = (MetaTileInfo) { 0 };
283 
284   fd = meta_kms_impl_device_get_fd (impl_device);
285   tile_blob = drmModeGetPropertyBlob (fd, blob_id);
286   if (!tile_blob)
287     {
288       g_warning ("Failed to read TILE of connector %s: %s",
289                  connector->name, strerror (errno));
290       return;
291     }
292 
293   if (tile_blob->length > 0)
294     {
295       if (sscanf ((char *) tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d",
296                   &state->tile_info.group_id,
297                   &state->tile_info.flags,
298                   &state->tile_info.max_h_tiles,
299                   &state->tile_info.max_v_tiles,
300                   &state->tile_info.loc_h_tile,
301                   &state->tile_info.loc_v_tile,
302                   &state->tile_info.tile_w,
303                   &state->tile_info.tile_h) != 8)
304         {
305           g_warning ("Couldn't understand TILE property blob of connector %s",
306                      connector->name);
307           state->tile_info = (MetaTileInfo) { 0 };
308         }
309     }
310 
311   drmModeFreePropertyBlob (tile_blob);
312 }
313 
314 static void
state_set_blobs(MetaKmsConnectorState * state,MetaKmsConnector * connector,MetaKmsImplDevice * impl_device,drmModeConnector * drm_connector)315 state_set_blobs (MetaKmsConnectorState *state,
316                  MetaKmsConnector      *connector,
317                  MetaKmsImplDevice     *impl_device,
318                  drmModeConnector      *drm_connector)
319 {
320   int fd;
321   int i;
322 
323   fd = meta_kms_impl_device_get_fd (impl_device);
324 
325   for (i = 0; i < drm_connector->count_props; i++)
326     {
327       drmModePropertyPtr prop;
328 
329       prop = drmModeGetProperty (fd, drm_connector->props[i]);
330       if (!prop)
331         continue;
332 
333       if (prop->flags & DRM_MODE_PROP_BLOB)
334         {
335           uint32_t blob_id;
336 
337           blob_id = drm_connector->prop_values[i];
338 
339           if (blob_id)
340             {
341               if (strcmp (prop->name, "EDID") == 0)
342                 state_set_edid (state, connector, impl_device, blob_id);
343               else if (strcmp (prop->name, "TILE") == 0)
344                 state_set_tile_info (state, connector, impl_device, blob_id);
345             }
346         }
347 
348       drmModeFreeProperty (prop);
349     }
350 }
351 
352 static void
state_set_physical_dimensions(MetaKmsConnectorState * state,drmModeConnector * drm_connector)353 state_set_physical_dimensions (MetaKmsConnectorState *state,
354                                drmModeConnector      *drm_connector)
355 {
356   state->width_mm = drm_connector->mmWidth;
357   state->height_mm = drm_connector->mmHeight;
358 }
359 
360 static void
state_set_modes(MetaKmsConnectorState * state,MetaKmsImplDevice * impl_device,drmModeConnector * drm_connector)361 state_set_modes (MetaKmsConnectorState *state,
362                  MetaKmsImplDevice     *impl_device,
363                  drmModeConnector      *drm_connector)
364 {
365   int i;
366 
367   for (i = 0; i < drm_connector->count_modes; i++)
368     {
369       MetaKmsMode *mode;
370 
371       mode = meta_kms_mode_new (impl_device, &drm_connector->modes[i],
372                                 META_KMS_MODE_FLAG_NONE);
373       state->modes = g_list_prepend (state->modes, mode);
374     }
375   state->modes = g_list_reverse (state->modes);
376 }
377 
378 static void
set_encoder_device_idx_bit(uint32_t * encoder_device_idxs,uint32_t encoder_id,MetaKmsImplDevice * impl_device,drmModeRes * drm_resources)379 set_encoder_device_idx_bit (uint32_t          *encoder_device_idxs,
380                             uint32_t           encoder_id,
381                             MetaKmsImplDevice *impl_device,
382                             drmModeRes        *drm_resources)
383 {
384   int fd;
385   int i;
386 
387   fd = meta_kms_impl_device_get_fd (impl_device);
388 
389   for (i = 0; i < drm_resources->count_encoders; i++)
390     {
391       drmModeEncoder *drm_encoder;
392 
393       drm_encoder = drmModeGetEncoder (fd, drm_resources->encoders[i]);
394       if (!drm_encoder)
395         continue;
396 
397       if (drm_encoder->encoder_id == encoder_id)
398         {
399           *encoder_device_idxs |= (1 << i);
400           drmModeFreeEncoder (drm_encoder);
401           break;
402         }
403 
404       drmModeFreeEncoder (drm_encoder);
405     }
406 }
407 
408 static void
state_set_crtc_state(MetaKmsConnectorState * state,drmModeConnector * drm_connector,MetaKmsImplDevice * impl_device,drmModeRes * drm_resources)409 state_set_crtc_state (MetaKmsConnectorState *state,
410                       drmModeConnector      *drm_connector,
411                       MetaKmsImplDevice     *impl_device,
412                       drmModeRes            *drm_resources)
413 {
414   int fd;
415   int i;
416   uint32_t common_possible_crtcs;
417   uint32_t common_possible_clones;
418   uint32_t encoder_device_idxs;
419 
420   fd = meta_kms_impl_device_get_fd (impl_device);
421 
422   common_possible_crtcs = UINT32_MAX;
423   common_possible_clones = UINT32_MAX;
424   encoder_device_idxs = 0;
425   for (i = 0; i < drm_connector->count_encoders; i++)
426     {
427       drmModeEncoder *drm_encoder;
428 
429       drm_encoder = drmModeGetEncoder (fd, drm_connector->encoders[i]);
430       if (!drm_encoder)
431         continue;
432 
433       common_possible_crtcs &= drm_encoder->possible_crtcs;
434       common_possible_clones &= drm_encoder->possible_clones;
435 
436       set_encoder_device_idx_bit (&encoder_device_idxs,
437                                   drm_encoder->encoder_id,
438                                   impl_device,
439                                   drm_resources);
440 
441       if (drm_connector->encoder_id == drm_encoder->encoder_id)
442         state->current_crtc_id = drm_encoder->crtc_id;
443 
444       drmModeFreeEncoder (drm_encoder);
445     }
446 
447   state->common_possible_crtcs = common_possible_crtcs;
448   state->common_possible_clones = common_possible_clones;
449   state->encoder_device_idxs = encoder_device_idxs;
450 }
451 
452 static MetaKmsConnectorState *
meta_kms_connector_state_new(void)453 meta_kms_connector_state_new (void)
454 {
455   MetaKmsConnectorState *state;
456 
457   state = g_new0 (MetaKmsConnectorState, 1);
458   state->suggested_x = -1;
459   state->suggested_y = -1;
460 
461   return state;
462 }
463 
464 static void
meta_kms_connector_state_free(MetaKmsConnectorState * state)465 meta_kms_connector_state_free (MetaKmsConnectorState *state)
466 {
467   g_clear_pointer (&state->edid_data, g_bytes_unref);
468   g_list_free_full (state->modes, (GDestroyNotify) meta_kms_mode_free);
469   g_free (state);
470 }
471 
472 G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsConnectorState,
473                                meta_kms_connector_state_free);
474 
475 static gboolean
kms_modes_equal(GList * modes,GList * other_modes)476 kms_modes_equal (GList *modes,
477                  GList *other_modes)
478 {
479   GList *l;
480 
481   if (g_list_length (modes) != g_list_length (other_modes))
482     return FALSE;
483 
484   for (l = modes; l; l = l->next)
485     {
486       GList *k;
487       MetaKmsMode *mode = l->data;
488 
489       for (k = other_modes; k; k = k->next)
490         {
491           MetaKmsMode *other_mode = k->data;
492 
493           if (!meta_kms_mode_equal (mode, other_mode))
494             return FALSE;
495         }
496     }
497 
498   return TRUE;
499 }
500 
501 static MetaKmsUpdateChanges
meta_kms_connector_state_changes(MetaKmsConnectorState * state,MetaKmsConnectorState * new_state)502 meta_kms_connector_state_changes (MetaKmsConnectorState *state,
503                                   MetaKmsConnectorState *new_state)
504 {
505   if (state->current_crtc_id != new_state->current_crtc_id)
506     return META_KMS_UPDATE_CHANGE_FULL;
507 
508   if (state->common_possible_crtcs != new_state->common_possible_crtcs)
509     return META_KMS_UPDATE_CHANGE_FULL;
510 
511   if (state->common_possible_clones != new_state->common_possible_clones)
512     return META_KMS_UPDATE_CHANGE_FULL;
513 
514   if (state->encoder_device_idxs != new_state->encoder_device_idxs)
515     return META_KMS_UPDATE_CHANGE_FULL;
516 
517   if (state->width_mm != new_state->width_mm)
518     return META_KMS_UPDATE_CHANGE_FULL;
519 
520   if (state->height_mm != new_state->height_mm)
521     return META_KMS_UPDATE_CHANGE_FULL;
522 
523   if (state->has_scaling != new_state->has_scaling)
524     return META_KMS_UPDATE_CHANGE_FULL;
525 
526   if (state->non_desktop != new_state->non_desktop)
527     return META_KMS_UPDATE_CHANGE_FULL;
528 
529   if (state->subpixel_order != new_state->subpixel_order)
530     return META_KMS_UPDATE_CHANGE_FULL;
531 
532   if (state->suggested_x != new_state->suggested_x)
533     return META_KMS_UPDATE_CHANGE_FULL;
534 
535   if (state->suggested_y != new_state->suggested_y)
536     return META_KMS_UPDATE_CHANGE_FULL;
537 
538   if (state->hotplug_mode_update != new_state->hotplug_mode_update)
539     return META_KMS_UPDATE_CHANGE_FULL;
540 
541   if (state->panel_orientation_transform !=
542       new_state->panel_orientation_transform)
543     return META_KMS_UPDATE_CHANGE_FULL;
544 
545   if (!meta_tile_info_equal (&state->tile_info, &new_state->tile_info))
546     return META_KMS_UPDATE_CHANGE_FULL;
547 
548   if ((state->edid_data && !new_state->edid_data) || !state->edid_data ||
549       !g_bytes_equal (state->edid_data, new_state->edid_data))
550     return META_KMS_UPDATE_CHANGE_FULL;
551 
552   if (!kms_modes_equal (state->modes, new_state->modes))
553     return META_KMS_UPDATE_CHANGE_FULL;
554 
555   return META_KMS_UPDATE_CHANGE_NONE;
556 }
557 
558 static MetaKmsUpdateChanges
meta_kms_connector_read_state(MetaKmsConnector * connector,MetaKmsImplDevice * impl_device,drmModeConnector * drm_connector,drmModeRes * drm_resources)559 meta_kms_connector_read_state (MetaKmsConnector  *connector,
560                                MetaKmsImplDevice *impl_device,
561                                drmModeConnector  *drm_connector,
562                                drmModeRes        *drm_resources)
563 {
564   g_autoptr (MetaKmsConnectorState) state = NULL;
565   g_autoptr (MetaKmsConnectorState) current_state = NULL;
566   MetaKmsUpdateChanges connector_changes;
567   MetaKmsUpdateChanges changes;
568 
569   current_state = g_steal_pointer (&connector->current_state);
570   changes = META_KMS_UPDATE_CHANGE_NONE;
571 
572   if (drm_connector->connection != DRM_MODE_CONNECTED)
573     {
574       if (drm_connector->connection != connector->connection)
575         {
576           connector->connection = drm_connector->connection;
577           changes |= META_KMS_UPDATE_CHANGE_FULL;
578         }
579 
580       return changes;
581     }
582 
583   state = meta_kms_connector_state_new ();
584 
585   state_set_blobs (state, connector, impl_device, drm_connector);
586 
587   state_set_properties (state, impl_device, drm_connector);
588 
589   state->subpixel_order =
590     drm_subpixel_order_to_cogl_subpixel_order (drm_connector->subpixel);
591 
592   state_set_physical_dimensions (state, drm_connector);
593 
594   state_set_modes (state, impl_device, drm_connector);
595 
596   state_set_crtc_state (state, drm_connector, impl_device, drm_resources);
597 
598   if (drm_connector->connection != connector->connection)
599     {
600       connector->connection = drm_connector->connection;
601       changes |= META_KMS_UPDATE_CHANGE_FULL;
602     }
603 
604   if (!current_state)
605     connector_changes = META_KMS_UPDATE_CHANGE_FULL;
606   else
607     connector_changes = meta_kms_connector_state_changes (current_state, state);
608 
609   if (connector_changes == META_KMS_UPDATE_CHANGE_NONE)
610     {
611       connector->current_state = g_steal_pointer (&current_state);
612     }
613   else
614     {
615       connector->current_state = g_steal_pointer (&state);
616       changes |= connector_changes;
617     }
618 
619   if (changes != META_KMS_UPDATE_CHANGE_NONE)
620     sync_fd_held (connector, impl_device);
621 
622   return changes;
623 }
624 
625 MetaKmsUpdateChanges
meta_kms_connector_update_state(MetaKmsConnector * connector,drmModeRes * drm_resources)626 meta_kms_connector_update_state (MetaKmsConnector *connector,
627                                  drmModeRes       *drm_resources)
628 {
629   MetaKmsImplDevice *impl_device;
630   drmModeConnector *drm_connector;
631   MetaKmsUpdateChanges changes;
632 
633   impl_device = meta_kms_device_get_impl_device (connector->device);
634   drm_connector = drmModeGetConnector (meta_kms_impl_device_get_fd (impl_device),
635                                        connector->id);
636 
637   if (!drm_connector)
638     return META_KMS_UPDATE_CHANGE_FULL;
639 
640   changes = meta_kms_connector_read_state (connector, impl_device,
641                                            drm_connector,
642                                            drm_resources);
643   drmModeFreeConnector (drm_connector);
644 
645   return changes;
646 }
647 
648 void
meta_kms_connector_predict_state(MetaKmsConnector * connector,MetaKmsUpdate * update)649 meta_kms_connector_predict_state (MetaKmsConnector *connector,
650                                   MetaKmsUpdate    *update)
651 {
652   MetaKmsImplDevice *impl_device;
653   MetaKmsConnectorState *current_state;
654   GList *mode_sets;
655   GList *l;
656   current_state = connector->current_state;
657   if (!current_state)
658     return;
659 
660   mode_sets = meta_kms_update_get_mode_sets (update);
661   for (l = mode_sets; l; l = l->next)
662     {
663       MetaKmsModeSet *mode_set = l->data;
664       MetaKmsCrtc *crtc = mode_set->crtc;
665 
666       if (current_state->current_crtc_id == meta_kms_crtc_get_id (crtc))
667         {
668           if (g_list_find (mode_set->connectors, connector))
669             break;
670           else
671             current_state->current_crtc_id = 0;
672         }
673       else
674         {
675           if (g_list_find (mode_set->connectors, connector))
676             {
677               current_state->current_crtc_id = meta_kms_crtc_get_id (crtc);
678               break;
679             }
680         }
681     }
682 
683   impl_device = meta_kms_device_get_impl_device (connector->device);
684   sync_fd_held (connector, impl_device);
685 }
686 
687 static void
init_properties(MetaKmsConnector * connector,MetaKmsImplDevice * impl_device,drmModeConnector * drm_connector)688 init_properties (MetaKmsConnector  *connector,
689                  MetaKmsImplDevice *impl_device,
690                  drmModeConnector  *drm_connector)
691 {
692   MetaKmsConnectorPropTable *prop_table = &connector->prop_table;
693 
694   *prop_table = (MetaKmsConnectorPropTable) {
695     .props = {
696       [META_KMS_CONNECTOR_PROP_CRTC_ID] =
697         {
698           .name = "CRTC_ID",
699           .type = DRM_MODE_PROP_OBJECT,
700         },
701       [META_KMS_CONNECTOR_PROP_DPMS] =
702         {
703           .name = "DPMS",
704           .type = DRM_MODE_PROP_ENUM,
705         },
706       [META_KMS_CONNECTOR_PROP_UNDERSCAN] =
707         {
708           .name = "underscan",
709           .type = DRM_MODE_PROP_ENUM,
710         },
711       [META_KMS_CONNECTOR_PROP_UNDERSCAN_HBORDER] =
712         {
713           .name = "underscan hborder",
714           .type = DRM_MODE_PROP_RANGE,
715         },
716       [META_KMS_CONNECTOR_PROP_UNDERSCAN_VBORDER] =
717         {
718           .name = "underscan vborder",
719           .type = DRM_MODE_PROP_RANGE,
720         },
721     }
722   };
723 
724   meta_kms_impl_device_init_prop_table (impl_device,
725                                         drm_connector->props,
726                                         drm_connector->prop_values,
727                                         drm_connector->count_props,
728                                         connector->prop_table.props,
729                                         META_KMS_CONNECTOR_N_PROPS,
730                                         NULL);
731 }
732 
733 static char *
make_connector_name(drmModeConnector * drm_connector)734 make_connector_name (drmModeConnector *drm_connector)
735 {
736   static const char * const connector_type_names[] = {
737     "None",
738     "VGA",
739     "DVI-I",
740     "DVI-D",
741     "DVI-A",
742     "Composite",
743     "SVIDEO",
744     "LVDS",
745     "Component",
746     "DIN",
747     "DP",
748     "HDMI",
749     "HDMI-B",
750     "TV",
751     "eDP",
752     "Virtual",
753     "DSI",
754   };
755 
756   if (drm_connector->connector_type < G_N_ELEMENTS (connector_type_names))
757     return g_strdup_printf ("%s-%d",
758                             connector_type_names[drm_connector->connector_type],
759                             drm_connector->connector_type_id);
760   else
761     return g_strdup_printf ("Unknown%d-%d",
762                             drm_connector->connector_type,
763                             drm_connector->connector_type_id);
764 }
765 
766 gboolean
meta_kms_connector_is_same_as(MetaKmsConnector * connector,drmModeConnector * drm_connector)767 meta_kms_connector_is_same_as (MetaKmsConnector *connector,
768                                drmModeConnector *drm_connector)
769 {
770   return (connector->id == drm_connector->connector_id &&
771           connector->type == drm_connector->connector_type &&
772           connector->type_id == drm_connector->connector_type_id);
773 }
774 
775 MetaKmsConnector *
meta_kms_connector_new(MetaKmsImplDevice * impl_device,drmModeConnector * drm_connector,drmModeRes * drm_resources)776 meta_kms_connector_new (MetaKmsImplDevice *impl_device,
777                         drmModeConnector  *drm_connector,
778                         drmModeRes        *drm_resources)
779 {
780   MetaKmsConnector *connector;
781 
782   g_assert (drm_connector);
783   connector = g_object_new (META_TYPE_KMS_CONNECTOR, NULL);
784   connector->device = meta_kms_impl_device_get_device (impl_device);
785   connector->id = drm_connector->connector_id;
786   connector->type = drm_connector->connector_type;
787   connector->type_id = drm_connector->connector_type_id;
788   connector->name = make_connector_name (drm_connector);
789 
790   init_properties (connector, impl_device, drm_connector);
791 
792   meta_kms_connector_read_state (connector, impl_device,
793                                  drm_connector,
794                                  drm_resources);
795 
796   return connector;
797 }
798 
799 static void
meta_kms_connector_finalize(GObject * object)800 meta_kms_connector_finalize (GObject *object)
801 {
802   MetaKmsConnector *connector = META_KMS_CONNECTOR (object);
803 
804   if (connector->fd_held)
805     {
806       MetaKmsImplDevice *impl_device;
807 
808       impl_device = meta_kms_device_get_impl_device (connector->device);
809       meta_kms_impl_device_unhold_fd (impl_device);
810     }
811 
812   g_clear_pointer (&connector->current_state, meta_kms_connector_state_free);
813   g_free (connector->name);
814 
815   G_OBJECT_CLASS (meta_kms_connector_parent_class)->finalize (object);
816 }
817 
818 static void
meta_kms_connector_init(MetaKmsConnector * connector)819 meta_kms_connector_init (MetaKmsConnector *connector)
820 {
821 }
822 
823 static void
meta_kms_connector_class_init(MetaKmsConnectorClass * klass)824 meta_kms_connector_class_init (MetaKmsConnectorClass *klass)
825 {
826   GObjectClass *object_class = G_OBJECT_CLASS (klass);
827 
828   object_class->finalize = meta_kms_connector_finalize;
829 }
830