1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /*
4  * Copyright (C) 2013-2017 Red Hat
5  * Copyright (C) 2018 DisplayLink (UK) Ltd.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20  * 02111-1307, USA.
21  */
22 
23 #include "config.h"
24 
25 #include "backends/native/meta-output-kms.h"
26 
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "backends/meta-crtc.h"
32 #include "backends/native/meta-kms-connector.h"
33 #include "backends/native/meta-kms-device.h"
34 #include "backends/native/meta-kms-mode.h"
35 #include "backends/native/meta-kms-update.h"
36 #include "backends/native/meta-kms-utils.h"
37 #include "backends/native/meta-crtc-kms.h"
38 #include "backends/native/meta-crtc-mode-kms.h"
39 
40 #define SYNC_TOLERANCE 0.01    /* 1 percent */
41 
42 struct _MetaOutputKms
43 {
44   MetaOutputNative parent;
45 
46   MetaKmsConnector *kms_connector;
47 };
48 
G_DEFINE_TYPE(MetaOutputKms,meta_output_kms,META_TYPE_OUTPUT_NATIVE)49 G_DEFINE_TYPE (MetaOutputKms, meta_output_kms, META_TYPE_OUTPUT_NATIVE)
50 
51 MetaKmsConnector *
52 meta_output_kms_get_kms_connector (MetaOutputKms *output_kms)
53 {
54   return output_kms->kms_connector;
55 }
56 
57 void
meta_output_kms_set_underscan(MetaOutputKms * output_kms,MetaKmsUpdate * kms_update)58 meta_output_kms_set_underscan (MetaOutputKms *output_kms,
59                                MetaKmsUpdate *kms_update)
60 {
61   MetaOutput *output = META_OUTPUT (output_kms);
62   const MetaOutputInfo *output_info = meta_output_get_info (output);
63 
64   if (!output_info->supports_underscanning)
65     return;
66 
67   if (meta_output_is_underscanning (output))
68     {
69       MetaCrtc *crtc;
70       const MetaCrtcConfig *crtc_config;
71       const MetaCrtcModeInfo *crtc_mode_info;
72       uint64_t hborder, vborder;
73 
74       crtc = meta_output_get_assigned_crtc (output);
75       crtc_config = meta_crtc_get_config (crtc);
76       crtc_mode_info = meta_crtc_mode_get_info (crtc_config->mode);
77 
78       hborder = MIN (128, (uint64_t) round (crtc_mode_info->width * 0.05));
79       vborder = MIN (128, (uint64_t) round (crtc_mode_info->height * 0.05));
80 
81       g_debug ("Setting underscan of connector %s to %" G_GUINT64_FORMAT " x %" G_GUINT64_FORMAT,
82                meta_kms_connector_get_name (output_kms->kms_connector),
83                hborder, vborder);
84 
85       meta_kms_update_set_underscanning (kms_update,
86                                          output_kms->kms_connector,
87                                          hborder, vborder);
88     }
89   else
90     {
91       g_debug ("Unsetting underscan of connector %s",
92                meta_kms_connector_get_name (output_kms->kms_connector));
93 
94       meta_kms_update_unset_underscanning (kms_update,
95                                            output_kms->kms_connector);
96     }
97 }
98 
99 uint32_t
meta_output_kms_get_connector_id(MetaOutputKms * output_kms)100 meta_output_kms_get_connector_id (MetaOutputKms *output_kms)
101 {
102   return meta_kms_connector_get_id (output_kms->kms_connector);
103 }
104 
105 gboolean
meta_output_kms_can_clone(MetaOutputKms * output_kms,MetaOutputKms * other_output_kms)106 meta_output_kms_can_clone (MetaOutputKms *output_kms,
107                            MetaOutputKms *other_output_kms)
108 {
109   return meta_kms_connector_can_clone (output_kms->kms_connector,
110                                        other_output_kms->kms_connector);
111 }
112 
113 static GBytes *
meta_output_kms_read_edid(MetaOutputNative * output_native)114 meta_output_kms_read_edid (MetaOutputNative *output_native)
115 {
116   MetaOutputKms *output_kms = META_OUTPUT_KMS (output_native);
117   const MetaKmsConnectorState *connector_state;
118   GBytes *edid_data;
119 
120   connector_state =
121     meta_kms_connector_get_current_state (output_kms->kms_connector);
122   edid_data = connector_state->edid_data;
123   if (!edid_data)
124     return NULL;
125 
126   return g_bytes_new_from_bytes (edid_data, 0, g_bytes_get_size (edid_data));
127 }
128 
129 static void
add_common_modes(MetaOutputInfo * output_info,MetaGpuKms * gpu_kms)130 add_common_modes (MetaOutputInfo *output_info,
131                   MetaGpuKms     *gpu_kms)
132 {
133   MetaCrtcMode *crtc_mode;
134   GPtrArray *array;
135   float refresh_rate;
136   unsigned i;
137   unsigned max_hdisplay = 0;
138   unsigned max_vdisplay = 0;
139   float max_refresh_rate = 0.0;
140   float max_bandwidth = 0.0;
141   MetaKmsDevice *kms_device;
142   MetaKmsModeFlag flag_filter;
143   GList *l;
144 
145   for (i = 0; i < output_info->n_modes; i++)
146     {
147       MetaCrtcMode *crtc_mode = output_info->modes[i];
148       MetaCrtcModeKms *crtc_mode_kms = META_CRTC_MODE_KMS (crtc_mode);
149       MetaKmsMode *kms_mode = meta_crtc_mode_kms_get_kms_mode (crtc_mode_kms);
150       const drmModeModeInfo *drm_mode = meta_kms_mode_get_drm_mode (kms_mode);
151       float bandwidth;
152 
153       refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
154       bandwidth = refresh_rate * drm_mode->hdisplay * drm_mode->vdisplay;
155       max_hdisplay = MAX (max_hdisplay, drm_mode->hdisplay);
156       max_vdisplay = MAX (max_vdisplay, drm_mode->vdisplay);
157       max_refresh_rate = MAX (max_refresh_rate, refresh_rate);
158       max_bandwidth = MAX (max_bandwidth, bandwidth);
159     }
160 
161   max_refresh_rate = MAX (max_refresh_rate, 60.0);
162   max_refresh_rate *= (1 + SYNC_TOLERANCE);
163 
164   kms_device = meta_gpu_kms_get_kms_device (gpu_kms);
165 
166   array = g_ptr_array_new ();
167 
168   if (max_hdisplay > max_vdisplay)
169     flag_filter = META_KMS_MODE_FLAG_FALLBACK_LANDSCAPE;
170   else
171     flag_filter = META_KMS_MODE_FLAG_FALLBACK_PORTRAIT;
172 
173   for (l = meta_kms_device_get_fallback_modes (kms_device); l; l = l->next)
174     {
175       MetaKmsMode *fallback_mode = l->data;
176       const drmModeModeInfo *drm_mode;
177       float bandwidth;
178 
179       if (!(meta_kms_mode_get_flags (fallback_mode) & flag_filter))
180         continue;
181 
182       drm_mode = meta_kms_mode_get_drm_mode (fallback_mode);
183       refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode);
184       bandwidth = refresh_rate * drm_mode->hdisplay * drm_mode->vdisplay;
185       if (drm_mode->hdisplay > max_hdisplay ||
186           drm_mode->vdisplay > max_vdisplay ||
187           refresh_rate > max_refresh_rate ||
188           bandwidth > max_bandwidth)
189         continue;
190 
191       crtc_mode = meta_gpu_kms_get_mode_from_kms_mode (gpu_kms, fallback_mode);
192       g_ptr_array_add (array, crtc_mode);
193     }
194 
195   output_info->modes = g_renew (MetaCrtcMode *, output_info->modes,
196                                 output_info->n_modes + array->len);
197   memcpy (output_info->modes + output_info->n_modes, array->pdata,
198           array->len * sizeof (MetaCrtcMode *));
199   output_info->n_modes += array->len;
200 
201   g_ptr_array_free (array, TRUE);
202 }
203 
204 static int
compare_modes(const void * one,const void * two)205 compare_modes (const void *one,
206                const void *two)
207 {
208   MetaCrtcMode *crtc_mode_one = *(MetaCrtcMode **) one;
209   MetaCrtcMode *crtc_mode_two = *(MetaCrtcMode **) two;
210   const MetaCrtcModeInfo *crtc_mode_info_one =
211     meta_crtc_mode_get_info (crtc_mode_one);
212   const MetaCrtcModeInfo *crtc_mode_info_two =
213     meta_crtc_mode_get_info (crtc_mode_two);
214 
215   if (crtc_mode_info_one->width != crtc_mode_info_two->width)
216     return crtc_mode_info_one->width > crtc_mode_info_two->width ? -1 : 1;
217   if (crtc_mode_info_one->height != crtc_mode_info_two->height)
218     return crtc_mode_info_one->height > crtc_mode_info_two->height ? -1 : 1;
219   if (crtc_mode_info_one->refresh_rate != crtc_mode_info_two->refresh_rate)
220     return (crtc_mode_info_one->refresh_rate > crtc_mode_info_two->refresh_rate
221             ? -1 : 1);
222 
223   return g_strcmp0 (meta_crtc_mode_get_name (crtc_mode_one),
224                     meta_crtc_mode_get_name (crtc_mode_two));
225 }
226 
227 static gboolean
init_output_modes(MetaOutputInfo * output_info,MetaGpuKms * gpu_kms,MetaKmsConnector * kms_connector,GError ** error)228 init_output_modes (MetaOutputInfo    *output_info,
229                    MetaGpuKms        *gpu_kms,
230                    MetaKmsConnector  *kms_connector,
231                    GError           **error)
232 {
233   const MetaKmsConnectorState *connector_state;
234   GList *l;
235   int i;
236 
237   connector_state = meta_kms_connector_get_current_state (kms_connector);
238 
239   output_info->preferred_mode = NULL;
240 
241   output_info->n_modes = g_list_length (connector_state->modes);
242   output_info->modes = g_new0 (MetaCrtcMode *, output_info->n_modes);
243   for (l = connector_state->modes, i = 0; l; l = l->next, i++)
244     {
245       MetaKmsMode *kms_mode = l->data;
246       const drmModeModeInfo *drm_mode = meta_kms_mode_get_drm_mode (kms_mode);
247       MetaCrtcMode *crtc_mode;
248 
249       crtc_mode = meta_gpu_kms_get_mode_from_kms_mode (gpu_kms, kms_mode);
250       output_info->modes[i] = crtc_mode;
251       if (drm_mode->type & DRM_MODE_TYPE_PREFERRED)
252         output_info->preferred_mode = output_info->modes[i];
253     }
254 
255   if (connector_state->has_scaling)
256     {
257       meta_topic (META_DEBUG_KMS, "Adding common modes to connector %u on %s",
258                   meta_kms_connector_get_id (kms_connector),
259                   meta_gpu_kms_get_file_path (gpu_kms));
260       add_common_modes (output_info, gpu_kms);
261     }
262 
263   if (!output_info->modes)
264     {
265       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
266                    "No modes available");
267       return FALSE;
268     }
269 
270   qsort (output_info->modes, output_info->n_modes,
271          sizeof (MetaCrtcMode *), compare_modes);
272 
273   if (!output_info->preferred_mode)
274     output_info->preferred_mode = output_info->modes[0];
275 
276   return TRUE;
277 }
278 
279 static MetaConnectorType
meta_kms_connector_type_from_drm(uint32_t drm_connector_type)280 meta_kms_connector_type_from_drm (uint32_t drm_connector_type)
281 {
282   g_warn_if_fail (drm_connector_type < META_CONNECTOR_TYPE_META);
283 
284   return (MetaConnectorType) drm_connector_type;
285 }
286 
287 MetaOutputKms *
meta_output_kms_new(MetaGpuKms * gpu_kms,MetaKmsConnector * kms_connector,MetaOutput * old_output,GError ** error)288 meta_output_kms_new (MetaGpuKms        *gpu_kms,
289                      MetaKmsConnector  *kms_connector,
290                      MetaOutput        *old_output,
291                      GError           **error)
292 {
293   MetaGpu *gpu = META_GPU (gpu_kms);
294   uint32_t connector_id;
295   uint32_t gpu_id;
296   g_autoptr (MetaOutputInfo) output_info = NULL;
297   MetaOutput *output;
298   MetaOutputKms *output_kms;
299   uint32_t drm_connector_type;
300   const MetaKmsConnectorState *connector_state;
301   GArray *crtcs;
302   GList *l;
303 
304   gpu_id = meta_gpu_kms_get_id (gpu_kms);
305   connector_id = meta_kms_connector_get_id (kms_connector);
306 
307   output_info = meta_output_info_new ();
308   output_info->name = g_strdup (meta_kms_connector_get_name (kms_connector));
309 
310   connector_state = meta_kms_connector_get_current_state (kms_connector);
311 
312   output_info->panel_orientation_transform =
313     connector_state->panel_orientation_transform;
314   if (meta_monitor_transform_is_rotated (output_info->panel_orientation_transform))
315     {
316       output_info->width_mm = connector_state->height_mm;
317       output_info->height_mm = connector_state->width_mm;
318     }
319   else
320     {
321       output_info->width_mm = connector_state->width_mm;
322       output_info->height_mm = connector_state->height_mm;
323     }
324 
325   if (!init_output_modes (output_info, gpu_kms, kms_connector, error))
326     return NULL;
327 
328   crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc *));
329 
330   for (l = meta_gpu_get_crtcs (gpu); l; l = l->next)
331     {
332       MetaCrtcKms *crtc_kms = META_CRTC_KMS (l->data);
333       MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms);
334       uint32_t crtc_idx;
335 
336       crtc_idx = meta_kms_crtc_get_idx (kms_crtc);
337       if (connector_state->common_possible_crtcs & (1 << crtc_idx))
338         g_array_append_val (crtcs, crtc_kms);
339     }
340 
341   output_info->n_possible_crtcs = crtcs->len;
342   output_info->possible_crtcs = (MetaCrtc **) g_array_free (crtcs, FALSE);
343 
344   output_info->suggested_x = connector_state->suggested_x;
345   output_info->suggested_y = connector_state->suggested_y;
346   output_info->hotplug_mode_update = connector_state->hotplug_mode_update;
347   output_info->supports_underscanning =
348     meta_kms_connector_is_underscanning_supported (kms_connector);
349 
350   meta_output_info_parse_edid (output_info, connector_state->edid_data);
351 
352   drm_connector_type = meta_kms_connector_get_connector_type (kms_connector);
353   output_info->connector_type =
354     meta_kms_connector_type_from_drm (drm_connector_type);
355 
356   output_info->tile_info = connector_state->tile_info;
357 
358   output = g_object_new (META_TYPE_OUTPUT_KMS,
359                          "id", ((uint64_t) gpu_id << 32) | connector_id,
360                          "gpu", gpu,
361                          "info", output_info,
362                          NULL);
363   output_kms = META_OUTPUT_KMS (output);
364   output_kms->kms_connector = kms_connector;
365 
366   if (connector_state->current_crtc_id)
367     {
368       for (l = meta_gpu_get_crtcs (gpu); l; l = l->next)
369         {
370           MetaCrtc *crtc = l->data;
371 
372           if (meta_crtc_get_id (crtc) == connector_state->current_crtc_id)
373             {
374               MetaOutputAssignment output_assignment;
375 
376               if (old_output)
377                 {
378                   output_assignment = (MetaOutputAssignment) {
379                     .is_primary = meta_output_is_primary (old_output),
380                     .is_presentation = meta_output_is_presentation (old_output),
381                   };
382                 }
383               else
384                 {
385                   output_assignment = (MetaOutputAssignment) {
386                     .is_primary = FALSE,
387                     .is_presentation = FALSE,
388                   };
389                 }
390               meta_output_assign_crtc (output, crtc, &output_assignment);
391               break;
392             }
393         }
394     }
395   else
396     {
397       meta_output_unassign_crtc (output);
398     }
399 
400   return output_kms;
401 }
402 
403 static void
meta_output_kms_init(MetaOutputKms * output_kms)404 meta_output_kms_init (MetaOutputKms *output_kms)
405 {
406 }
407 
408 static void
meta_output_kms_class_init(MetaOutputKmsClass * klass)409 meta_output_kms_class_init (MetaOutputKmsClass *klass)
410 {
411   MetaOutputNativeClass *output_native_class = META_OUTPUT_NATIVE_CLASS (klass);
412 
413   output_native_class->read_edid = meta_output_kms_read_edid;
414 }
415