1 /*
2  * Copyright (C) 2017 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/edid.h"
23 #include "backends/meta-output.h"
24 
25 #include "backends/meta-crtc.h"
26 
27 enum
28 {
29   PROP_0,
30 
31   PROP_ID,
32   PROP_GPU,
33   PROP_INFO,
34 
35   N_PROPS
36 };
37 
38 static GParamSpec *obj_props[N_PROPS];
39 
40 typedef struct _MetaOutputPrivate
41 {
42   uint64_t id;
43 
44   MetaGpu *gpu;
45 
46   MetaOutputInfo *info;
47 
48   MetaMonitor *monitor;
49 
50   /* The CRTC driving this output, NULL if the output is not enabled */
51   MetaCrtc *crtc;
52 
53   gboolean is_primary;
54   gboolean is_presentation;
55 
56   gboolean is_underscanning;
57 
58   int backlight;
59 } MetaOutputPrivate;
60 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(MetaOutput,meta_output,G_TYPE_OBJECT)61 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaOutput, meta_output, G_TYPE_OBJECT)
62 
63 G_DEFINE_BOXED_TYPE (MetaOutputInfo, meta_output_info,
64                      meta_output_info_ref,
65                      meta_output_info_unref)
66 
67 MetaOutputInfo *
68 meta_output_info_new (void)
69 {
70   MetaOutputInfo *output_info;
71 
72   output_info = g_new0 (MetaOutputInfo, 1);
73   g_ref_count_init (&output_info->ref_count);
74 
75   return output_info;
76 }
77 
78 MetaOutputInfo *
meta_output_info_ref(MetaOutputInfo * output_info)79 meta_output_info_ref (MetaOutputInfo *output_info)
80 {
81   g_ref_count_inc (&output_info->ref_count);
82   return output_info;
83 }
84 
85 void
meta_output_info_unref(MetaOutputInfo * output_info)86 meta_output_info_unref (MetaOutputInfo *output_info)
87 {
88   if (g_ref_count_dec (&output_info->ref_count))
89     {
90       g_free (output_info->name);
91       g_free (output_info->vendor);
92       g_free (output_info->product);
93       g_free (output_info->serial);
94       g_free (output_info->modes);
95       g_free (output_info->possible_crtcs);
96       g_free (output_info->possible_clones);
97       g_free (output_info);
98     }
99 }
100 
101 uint64_t
meta_output_get_id(MetaOutput * output)102 meta_output_get_id (MetaOutput *output)
103 {
104   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
105 
106   return priv->id;
107 }
108 
109 MetaGpu *
meta_output_get_gpu(MetaOutput * output)110 meta_output_get_gpu (MetaOutput *output)
111 {
112   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
113 
114   return priv->gpu;
115 }
116 
117 MetaMonitor *
meta_output_get_monitor(MetaOutput * output)118 meta_output_get_monitor (MetaOutput *output)
119 {
120   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
121 
122   g_warn_if_fail (priv->monitor);
123 
124   return priv->monitor;
125 }
126 
127 void
meta_output_set_monitor(MetaOutput * output,MetaMonitor * monitor)128 meta_output_set_monitor (MetaOutput  *output,
129                          MetaMonitor *monitor)
130 {
131   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
132 
133   g_warn_if_fail (!priv->monitor);
134 
135   priv->monitor = monitor;
136 }
137 
138 void
meta_output_unset_monitor(MetaOutput * output)139 meta_output_unset_monitor (MetaOutput *output)
140 {
141   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
142 
143   g_warn_if_fail (priv->monitor);
144 
145   priv->monitor = NULL;
146 }
147 
148 const char *
meta_output_get_name(MetaOutput * output)149 meta_output_get_name (MetaOutput *output)
150 {
151   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
152 
153   return priv->info->name;
154 }
155 
156 gboolean
meta_output_is_primary(MetaOutput * output)157 meta_output_is_primary (MetaOutput *output)
158 {
159   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
160 
161   return priv->is_primary;
162 }
163 
164 gboolean
meta_output_is_presentation(MetaOutput * output)165 meta_output_is_presentation (MetaOutput *output)
166 {
167   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
168 
169   return priv->is_presentation;
170 }
171 
172 gboolean
meta_output_is_underscanning(MetaOutput * output)173 meta_output_is_underscanning (MetaOutput *output)
174 {
175   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
176 
177   return priv->is_underscanning;
178 }
179 
180 void
meta_output_set_backlight(MetaOutput * output,int backlight)181 meta_output_set_backlight (MetaOutput *output,
182                            int         backlight)
183 {
184   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
185 
186   priv->backlight = backlight;
187 }
188 
189 int
meta_output_get_backlight(MetaOutput * output)190 meta_output_get_backlight (MetaOutput *output)
191 {
192   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
193 
194   return priv->backlight;
195 }
196 
197 void
meta_output_add_possible_clone(MetaOutput * output,MetaOutput * possible_clone)198 meta_output_add_possible_clone (MetaOutput *output,
199                                 MetaOutput *possible_clone)
200 {
201   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
202   MetaOutputInfo *output_info = priv->info;
203 
204   output_info->n_possible_clones++;
205   output_info->possible_clones = g_renew (MetaOutput *,
206                                           output_info->possible_clones,
207                                           output_info->n_possible_clones);
208   output_info->possible_clones[output_info->n_possible_clones - 1] =
209     possible_clone;
210 }
211 
212 const MetaOutputInfo *
meta_output_get_info(MetaOutput * output)213 meta_output_get_info (MetaOutput *output)
214 {
215   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
216 
217   return priv->info;
218 }
219 
220 void
meta_output_assign_crtc(MetaOutput * output,MetaCrtc * crtc,const MetaOutputAssignment * output_assignment)221 meta_output_assign_crtc (MetaOutput                 *output,
222                          MetaCrtc                   *crtc,
223                          const MetaOutputAssignment *output_assignment)
224 {
225   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
226 
227   g_assert (crtc);
228 
229   meta_output_unassign_crtc (output);
230 
231   g_set_object (&priv->crtc, crtc);
232 
233   meta_crtc_assign_output (crtc, output);
234 
235   priv->is_primary = output_assignment->is_primary;
236   priv->is_presentation = output_assignment->is_presentation;
237   priv->is_underscanning = output_assignment->is_underscanning;
238 }
239 
240 void
meta_output_unassign_crtc(MetaOutput * output)241 meta_output_unassign_crtc (MetaOutput *output)
242 {
243   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
244 
245   if (priv->crtc)
246     {
247       meta_crtc_unassign_output (priv->crtc, output);
248       g_clear_object (&priv->crtc);
249     }
250 
251   priv->is_primary = FALSE;
252   priv->is_presentation = FALSE;
253 }
254 
255 MetaCrtc *
meta_output_get_assigned_crtc(MetaOutput * output)256 meta_output_get_assigned_crtc (MetaOutput *output)
257 {
258   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
259 
260   return priv->crtc;
261 }
262 
263 MetaMonitorTransform
meta_output_logical_to_crtc_transform(MetaOutput * output,MetaMonitorTransform transform)264 meta_output_logical_to_crtc_transform (MetaOutput           *output,
265                                        MetaMonitorTransform  transform)
266 {
267   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
268   MetaMonitorTransform panel_orientation_transform;
269 
270   panel_orientation_transform = priv->info->panel_orientation_transform;
271   return meta_monitor_transform_transform (transform,
272                                            panel_orientation_transform);
273 }
274 
275 MetaMonitorTransform
meta_output_crtc_to_logical_transform(MetaOutput * output,MetaMonitorTransform transform)276 meta_output_crtc_to_logical_transform (MetaOutput           *output,
277                                        MetaMonitorTransform  transform)
278 {
279   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
280   MetaMonitorTransform inverted_panel_orientation_transform;
281 
282   inverted_panel_orientation_transform =
283     meta_monitor_transform_invert (priv->info->panel_orientation_transform);
284   return meta_monitor_transform_transform (transform,
285                                            inverted_panel_orientation_transform);
286 }
287 
288 void
meta_output_info_parse_edid(MetaOutputInfo * output_info,GBytes * edid)289 meta_output_info_parse_edid (MetaOutputInfo *output_info,
290                              GBytes         *edid)
291 {
292   MonitorInfo *parsed_edid;
293   size_t len;
294 
295   if (!edid)
296     goto out;
297 
298   parsed_edid = decode_edid (g_bytes_get_data (edid, &len));
299 
300   if (parsed_edid)
301     {
302       output_info->vendor = g_strndup (parsed_edid->manufacturer_code, 4);
303       if (!g_utf8_validate (output_info->vendor, -1, NULL))
304         g_clear_pointer (&output_info->vendor, g_free);
305 
306       output_info->product = g_strndup (parsed_edid->dsc_product_name, 14);
307       if (!g_utf8_validate (output_info->product, -1, NULL) ||
308           output_info->product[0] == '\0')
309         {
310           g_clear_pointer (&output_info->product, g_free);
311           output_info->product = g_strdup_printf ("0x%04x", (unsigned) parsed_edid->product_code);
312         }
313 
314       output_info->serial = g_strndup (parsed_edid->dsc_serial_number, 14);
315       if (!g_utf8_validate (output_info->serial, -1, NULL) ||
316           output_info->serial[0] == '\0')
317         {
318           g_clear_pointer (&output_info->serial, g_free);
319           output_info->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number);
320         }
321 
322       g_free (parsed_edid);
323     }
324 
325  out:
326   if (!output_info->vendor)
327     output_info->vendor = g_strdup ("unknown");
328   if (!output_info->product)
329     output_info->product = g_strdup ("unknown");
330   if (!output_info->serial)
331     output_info->serial = g_strdup ("unknown");
332 }
333 
334 gboolean
meta_output_is_laptop(MetaOutput * output)335 meta_output_is_laptop (MetaOutput *output)
336 {
337   const MetaOutputInfo *output_info = meta_output_get_info (output);
338 
339   switch (output_info->connector_type)
340     {
341     case META_CONNECTOR_TYPE_eDP:
342     case META_CONNECTOR_TYPE_LVDS:
343     case META_CONNECTOR_TYPE_DSI:
344       return TRUE;
345     default:
346       return FALSE;
347     }
348 }
349 
350 static void
meta_output_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)351 meta_output_set_property (GObject      *object,
352                           guint         prop_id,
353                           const GValue *value,
354                           GParamSpec   *pspec)
355 {
356   MetaOutput *output = META_OUTPUT (object);
357   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
358 
359   switch (prop_id)
360     {
361     case PROP_ID:
362       priv->id = g_value_get_uint64 (value);
363       break;
364     case PROP_GPU:
365       priv->gpu = g_value_get_object (value);
366       break;
367     case PROP_INFO:
368       priv->info = meta_output_info_ref (g_value_get_boxed (value));
369       break;
370     default:
371       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372     }
373 }
374 
375 static void
meta_output_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)376 meta_output_get_property (GObject    *object,
377                           guint       prop_id,
378                           GValue     *value,
379                           GParamSpec *pspec)
380 {
381   MetaOutput *output = META_OUTPUT (object);
382   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
383 
384   switch (prop_id)
385     {
386     case PROP_ID:
387       g_value_set_uint64 (value, priv->id);
388       break;
389     case PROP_GPU:
390       g_value_set_object (value, priv->gpu);
391       break;
392     case PROP_INFO:
393       g_value_set_boxed (value, priv->info);
394       break;
395     default:
396       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
397     }
398 }
399 
400 static void
meta_output_dispose(GObject * object)401 meta_output_dispose (GObject *object)
402 {
403   MetaOutput *output = META_OUTPUT (object);
404   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
405 
406   g_clear_object (&priv->crtc);
407 
408   G_OBJECT_CLASS (meta_output_parent_class)->dispose (object);
409 }
410 
411 static void
meta_output_finalize(GObject * object)412 meta_output_finalize (GObject *object)
413 {
414   MetaOutput *output = META_OUTPUT (object);
415   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
416 
417   g_clear_pointer (&priv->info, meta_output_info_unref);
418 
419   G_OBJECT_CLASS (meta_output_parent_class)->finalize (object);
420 }
421 
422 static void
meta_output_init(MetaOutput * output)423 meta_output_init (MetaOutput *output)
424 {
425   MetaOutputPrivate *priv = meta_output_get_instance_private (output);
426 
427   priv->backlight = -1;
428 }
429 
430 static void
meta_output_class_init(MetaOutputClass * klass)431 meta_output_class_init (MetaOutputClass *klass)
432 {
433   GObjectClass *object_class = G_OBJECT_CLASS (klass);
434 
435   object_class->set_property = meta_output_set_property;
436   object_class->get_property = meta_output_get_property;
437   object_class->dispose = meta_output_dispose;
438   object_class->finalize = meta_output_finalize;
439 
440   obj_props[PROP_ID] =
441     g_param_spec_uint64 ("id",
442                          "id",
443                          "CRTC id",
444                          0, UINT64_MAX, 0,
445                          G_PARAM_READWRITE |
446                          G_PARAM_CONSTRUCT_ONLY |
447                          G_PARAM_STATIC_STRINGS);
448   obj_props[PROP_GPU] =
449     g_param_spec_object ("gpu",
450                          "gpu",
451                          "MetaGpu",
452                          META_TYPE_GPU,
453                          G_PARAM_READWRITE |
454                          G_PARAM_CONSTRUCT_ONLY |
455                          G_PARAM_STATIC_STRINGS);
456   obj_props[PROP_INFO] =
457     g_param_spec_boxed ("info",
458                         "info",
459                         "MetaOutputInfo",
460                         META_TYPE_OUTPUT_INFO,
461                         G_PARAM_READWRITE |
462                         G_PARAM_CONSTRUCT_ONLY |
463                         G_PARAM_STATIC_STRINGS);
464   g_object_class_install_properties (object_class, N_PROPS, obj_props);
465 }
466 
467 gboolean
meta_tile_info_equal(MetaTileInfo * a,MetaTileInfo * b)468 meta_tile_info_equal (MetaTileInfo *a,
469                       MetaTileInfo *b)
470 {
471   if (a == b)
472     return TRUE;
473 
474   if (a->group_id != b->group_id)
475     return FALSE;
476 
477   if (a->flags != b->flags)
478     return FALSE;
479 
480   if (a->max_h_tiles != b->max_h_tiles)
481     return FALSE;
482 
483   if (a->max_v_tiles != b->max_v_tiles)
484     return FALSE;
485 
486   if (a->loc_h_tile != b->loc_h_tile)
487     return FALSE;
488 
489   if (a->loc_v_tile != b->loc_v_tile)
490     return FALSE;
491 
492   if (a->tile_w != b->tile_w)
493     return FALSE;
494 
495   if (a->tile_h != b->tile_h)
496     return FALSE;
497 
498   return TRUE;
499 }
500