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