blob: 240f79ce3bfebb5b79454a8f0d4e40446ab8a5fa [file] [log] [blame]
Dominik Behr83010f82016-03-18 18:43:08 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <errno.h>
8#include <fcntl.h>
9#include <stdbool.h>
10#include <stddef.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/mman.h>
15#include <time.h>
16#include <unistd.h>
17
18#include "drm.h"
Dominik Behrd2cc4d22016-01-29 17:31:52 -080019#include "input.h"
Dominik Behr83010f82016-03-18 18:43:08 -070020#include "util.h"
21
Dominik Behrda3c0102016-06-08 15:05:38 -070022static drm_t* g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -070023
Dominik Behrbf08a552018-08-07 19:46:03 -070024static int32_t atomic_set_prop(drm_t* drm, drmModeAtomicReqPtr pset, uint32_t id,
25 drmModeObjectPropertiesPtr props, const char *name, uint64_t value)
26{
27 uint32_t u;
28 int32_t ret;
29 drmModePropertyPtr prop;
30
31 for (u = 0; u < props->count_props; u++) {
32 prop = drmModeGetProperty(drm->fd, props->props[u]);
33 if (!prop)
34 continue;
35 if (strcmp(prop->name, name)) {
36 drmModeFreeProperty(prop);
37 continue;
38 }
39 ret = drmModeAtomicAddProperty(pset, id, prop->prop_id, value);
40 if (ret < 0)
41 LOG(ERROR, "setting atomic property %s failed with %d\n", name, ret);
42 else
43 ret = 0;
44 drmModeFreeProperty(prop);
45 return ret;
46 }
47 LOG(ERROR, "could not find atomic property %s\n", name);
48 return -ENOENT;
49}
50
Daniele Castagna3ccfd382017-02-02 19:51:00 -050051static int32_t crtc_planes_num(drm_t* drm, int32_t crtc_index)
52{
53 drmModePlanePtr plane;
54 int32_t planes_num = 0;
55 drmModePlaneResPtr plane_resources = drmModeGetPlaneResources(drm->fd);
Dominik Behr57b5c722018-08-08 18:52:31 -070056
57 if (!plane_resources)
58 return 1; /* Just pretend there is one plane. */
59
Daniele Castagna3ccfd382017-02-02 19:51:00 -050060 for (uint32_t p = 0; p < plane_resources->count_planes; p++) {
61 plane = drmModeGetPlane(drm->fd, plane_resources->planes[p]);
62
63 if (plane->possible_crtcs & (1 << crtc_index))
64 planes_num++;
65
66 drmModeFreePlane(plane);
67 }
68 drmModeFreePlaneResources(plane_resources);
69 return planes_num;
70}
71
Dominik Behr57b5c722018-08-08 18:52:31 -070072static bool get_connector_path(drm_t* drm, uint32_t connector_id, uint32_t* ret_encoder_id, uint32_t* ret_crtc_id)
Dominik Behr83010f82016-03-18 18:43:08 -070073{
Dominik Behr57b5c722018-08-08 18:52:31 -070074 drmModeConnector* connector = drmModeGetConnector(drm->fd, connector_id);
Dominik Behr83010f82016-03-18 18:43:08 -070075 drmModeEncoder* encoder;
Dominik Behr83010f82016-03-18 18:43:08 -070076
Dominik Behr57b5c722018-08-08 18:52:31 -070077 if (!connector)
78 return false; /* Error. */
Dominik Behr83010f82016-03-18 18:43:08 -070079
Dominik Behr57b5c722018-08-08 18:52:31 -070080 if (ret_encoder_id)
81 *ret_encoder_id = connector->encoder_id;
82 if (!connector->encoder_id) {
83 drmModeFreeConnector(connector);
84 if (ret_crtc_id)
85 *ret_crtc_id = 0;
86 return true; /* Not connected. */
Dominik Behr83010f82016-03-18 18:43:08 -070087 }
88
Dominik Behr57b5c722018-08-08 18:52:31 -070089 encoder = drmModeGetEncoder(drm->fd, connector->encoder_id);
90 if (!encoder) {
91 if (ret_crtc_id)
92 *ret_crtc_id = 0;
93 drmModeFreeConnector(connector);
94 return false; /* Error. */
95 }
96
97 if (ret_crtc_id)
98 *ret_crtc_id = encoder->crtc_id;
99
100 drmModeFreeEncoder(encoder);
101 drmModeFreeConnector(connector);
102 return true; /* Connected. */
103}
104
105/* Find CRTC with most planes for given connector_id. */
106static bool find_crtc_for_connector(drm_t* drm, uint32_t connector_id, uint32_t* ret_crtc_id)
107{
108 int enc;
109 int32_t crtc_id = -1;
110 int32_t max_crtc_planes = -1;
111 drmModeConnector* connector = drmModeGetConnector(drm->fd, connector_id);
112
113 if (!connector)
114 return false;
115
116 for (enc = 0; enc < connector->count_encoders; enc++) {
117 int crtc;
118 drmModeEncoder* encoder = drmModeGetEncoder(drm->fd, connector->encoders[enc]);
Dominik Behr83010f82016-03-18 18:43:08 -0700119
120 if (encoder) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700121 for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++) {
122 int32_t crtc_planes;
123
124 if (!(encoder->possible_crtcs & (1 << crtc)))
Dominik Behr83010f82016-03-18 18:43:08 -0700125 continue;
Daniele Castagna3ccfd382017-02-02 19:51:00 -0500126
Dominik Behr57b5c722018-08-08 18:52:31 -0700127 crtc_planes = crtc_planes_num(drm, crtc);
Daniele Castagna3ccfd382017-02-02 19:51:00 -0500128 if (max_crtc_planes < crtc_planes) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700129 crtc_id = drm->resources->crtcs[crtc];
Daniele Castagna3ccfd382017-02-02 19:51:00 -0500130 max_crtc_planes = crtc_planes;
131 }
Dominik Behr83010f82016-03-18 18:43:08 -0700132 }
Dominik Behr57b5c722018-08-08 18:52:31 -0700133
Daniele Castagna3ccfd382017-02-02 19:51:00 -0500134 drmModeFreeEncoder(encoder);
Dominik Behr57b5c722018-08-08 18:52:31 -0700135 if (crtc_id != -1) {
136 if (ret_crtc_id)
137 *ret_crtc_id = crtc_id;
138 drmModeFreeConnector(connector);
139 return true;
140 }
Dominik Behr83010f82016-03-18 18:43:08 -0700141 }
142 }
143
Dominik Behr57b5c722018-08-08 18:52:31 -0700144 drmModeFreeConnector(connector);
145 return false;
Dominik Behr83010f82016-03-18 18:43:08 -0700146}
147
148static int drm_is_primary_plane(drm_t* drm, uint32_t plane_id)
149{
150 uint32_t p;
151 bool found = false;
152 int ret = -1;
153
154 drmModeObjectPropertiesPtr props;
155 props = drmModeObjectGetProperties(drm->fd,
156 plane_id,
157 DRM_MODE_OBJECT_PLANE);
158 if (!props) {
159 LOG(ERROR, "Unable to get plane properties: %m");
160 return -1;
161 }
162
163 for (p = 0; p < props->count_props && !found; p++) {
164 drmModePropertyPtr prop;
165 prop = drmModeGetProperty(drm->fd, props->props[p]);
166 if (prop) {
167 if (strcmp("type", prop->name) == 0) {
168 found = true;
169 ret = (props->prop_values[p] == DRM_PLANE_TYPE_PRIMARY);
170 }
171 drmModeFreeProperty(prop);
172 }
173 }
174
175 drmModeFreeObjectProperties(props);
176
177 return ret;
178}
179
Dominik Behrb1abcba2016-04-14 14:57:21 -0700180/* Disable all planes except for primary on crtc we use. */
Dominik Behr57b5c722018-08-08 18:52:31 -0700181static void drm_disable_non_primary_planes(drm_t* drm, uint32_t console_crtc_id)
Dominik Behr83010f82016-03-18 18:43:08 -0700182{
183 int ret;
184
185 if (!drm->plane_resources)
186 return;
187
188 for (uint32_t p = 0; p < drm->plane_resources->count_planes; p++) {
189 drmModePlanePtr plane;
190 plane = drmModeGetPlane(drm->fd,
191 drm->plane_resources->planes[p]);
192 if (plane) {
193 int primary = drm_is_primary_plane(drm, plane->plane_id);
Dominik Behr57b5c722018-08-08 18:52:31 -0700194 if (!(plane->crtc_id == console_crtc_id && primary != 0)) {
Dominik Behr83010f82016-03-18 18:43:08 -0700195 ret = drmModeSetPlane(drm->fd, plane->plane_id, plane->crtc_id,
196 0, 0,
197 0, 0,
198 0, 0,
199 0, 0,
200 0, 0);
201 if (ret) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700202 LOG(WARNING, "Unable to disable plane:%d %m", plane->plane_id);
Dominik Behr83010f82016-03-18 18:43:08 -0700203 }
204 }
205 drmModeFreePlane(plane);
206 }
207 }
208}
209
210static bool drm_is_internal(unsigned type)
211{
212 unsigned t;
213 unsigned kInternalConnectors[] = {
214 DRM_MODE_CONNECTOR_LVDS,
215 DRM_MODE_CONNECTOR_eDP,
216 DRM_MODE_CONNECTOR_DSI,
217 };
218 for (t = 0; t < ARRAY_SIZE(kInternalConnectors); t++)
219 if (type == kInternalConnectors[t])
220 return true;
221 return false;
222}
223
224static drmModeConnector* find_first_connected_connector(drm_t* drm, bool internal, bool external)
225{
226 for (int i = 0; i < drm->resources->count_connectors; i++) {
227 drmModeConnector* connector;
228
229 connector = drmModeGetConnector(drm->fd, drm->resources->connectors[i]);
230 if (connector) {
231 bool is_internal = drm_is_internal(connector->connector_type);
232 if (!internal && is_internal)
233 continue;
234 if (!external && !is_internal)
235 continue;
236 if ((connector->count_modes > 0) &&
237 (connector->connection == DRM_MODE_CONNECTED))
238 return connector;
239
240 drmModeFreeConnector(connector);
241 }
242 }
243 return NULL;
244}
245
Dominik Behrbb728f32019-09-03 17:52:13 -0700246static int find_panel_orientation(drm_t *drm)
247{
248 uint32_t p;
249 bool found = false;
250 drmModeObjectPropertiesPtr props;
251
252 props = drmModeObjectGetProperties(drm->fd,
253 drm->console_connector_id,
254 DRM_MODE_OBJECT_CONNECTOR);
255 if (!props) {
256 LOG(ERROR, "Unable to get connector properties: %m");
257 return -1;
258 }
259
260 for (p = 0; p < props->count_props && !found; p++) {
261 drmModePropertyPtr prop;
262 prop = drmModeGetProperty(drm->fd, props->props[p]);
263 if (prop) {
264 if (strcmp("panel orientation", prop->name) == 0) {
265 found = true;
266 drm->panel_orientation = (int32_t)(props->prop_values[p]);
267 }
268 drmModeFreeProperty(prop);
269 }
270 }
271
272 drmModeFreeObjectProperties(props);
273 return 0;
274}
275
276
Dominik Behr57b5c722018-08-08 18:52:31 -0700277static bool find_main_monitor(drm_t* drm)
Dominik Behr83010f82016-03-18 18:43:08 -0700278{
279 int modes;
Dominik Behr57b5c722018-08-08 18:52:31 -0700280 uint32_t console_crtc_id = 0;
Dominik Behrd2cc4d22016-01-29 17:31:52 -0800281 int lid_state = input_check_lid_state();
Dominik Behr83010f82016-03-18 18:43:08 -0700282 drmModeConnector* main_monitor_connector = NULL;
283
Dominik Behr57b5c722018-08-08 18:52:31 -0700284 drm->console_connector_id = 0;
285
Dominik Behr83010f82016-03-18 18:43:08 -0700286 /*
287 * Find the LVDS/eDP/DSI connectors. Those are the main screens.
288 */
Dominik Behrd2cc4d22016-01-29 17:31:52 -0800289 if (lid_state <= 0)
290 main_monitor_connector = find_first_connected_connector(drm, true, false);
Dominik Behr83010f82016-03-18 18:43:08 -0700291
292 /*
293 * Now try external connectors.
294 */
295 if (!main_monitor_connector)
296 main_monitor_connector =
297 find_first_connected_connector(drm, false, true);
298
299 /*
300 * If we still didn't find a connector, give up and return.
301 */
302 if (!main_monitor_connector)
Dominik Behr57b5c722018-08-08 18:52:31 -0700303 return false;
Dominik Behr83010f82016-03-18 18:43:08 -0700304
Dominik Behr57b5c722018-08-08 18:52:31 -0700305 if (!main_monitor_connector->count_modes)
306 return false;
307
308 drm->console_connector_id = main_monitor_connector->connector_id;
309 drm->console_connector_internal = drm_is_internal(main_monitor_connector->connector_type);
310 drm->console_mmWidth = main_monitor_connector->mmWidth;
311 drm->console_mmHeight = main_monitor_connector->mmHeight;
312
Dominik Behr83010f82016-03-18 18:43:08 -0700313 for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
314 if (main_monitor_connector->modes[modes].type &
315 DRM_MODE_TYPE_PREFERRED) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700316 drm->console_mode_info = main_monitor_connector->modes[modes];
Dominik Behr83010f82016-03-18 18:43:08 -0700317 break;
318 }
319 }
Dominik Behr57b5c722018-08-08 18:52:31 -0700320 /* If there was no preferred mode use first one. */
321 if (modes == main_monitor_connector->count_modes)
322 drm->console_mode_info = main_monitor_connector->modes[0];
Dominik Behr83010f82016-03-18 18:43:08 -0700323
Dominik Behrbb728f32019-09-03 17:52:13 -0700324 find_panel_orientation(drm);
325
Dominik Behr57b5c722018-08-08 18:52:31 -0700326 drmModeFreeConnector(main_monitor_connector);
327
328 get_connector_path(drm, drm->console_connector_id, NULL, &console_crtc_id);
329
330 if (!console_crtc_id)
331 /* No existing path, find one. */
332 find_crtc_for_connector(drm, drm->console_connector_id, &console_crtc_id);
333
334 if (!console_crtc_id)
335 /* Cannot find CRTC for connector. We will not be able to use it. */
336 return false;
337
338 return true;
Dominik Behr83010f82016-03-18 18:43:08 -0700339}
340
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800341static void drm_clear_rmfb(drm_t* drm)
342{
343 if (drm->delayed_rmfb_fb_id) {
344 drmModeRmFB(drm->fd, drm->delayed_rmfb_fb_id);
345 drm->delayed_rmfb_fb_id = 0;
346 }
347}
348
Dominik Behr83010f82016-03-18 18:43:08 -0700349static void drm_fini(drm_t* drm)
350{
351 if (!drm)
352 return;
353
354 if (drm->fd >= 0) {
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800355 drm_clear_rmfb(drm);
356
Dominik Behr83010f82016-03-18 18:43:08 -0700357 if (drm->plane_resources) {
358 drmModeFreePlaneResources(drm->plane_resources);
359 drm->plane_resources = NULL;
360 }
361
362 if (drm->resources) {
363 drmModeFreeResources(drm->resources);
364 drm->resources = NULL;
365 }
366
367 drmClose(drm->fd);
368 drm->fd = -1;
369 }
370
371 free(drm);
372}
373
374static bool drm_equal(drm_t* l, drm_t* r)
375{
Dominik Behrd4d56272016-03-31 18:55:17 -0700376 if (!l && !r)
377 return true;
378 if ((!l && r) || (l && !r))
379 return false;
Dominik Behr83010f82016-03-18 18:43:08 -0700380
Dominik Behr57b5c722018-08-08 18:52:31 -0700381 if (l->console_connector_id != r->console_connector_id)
Dominik Behr83010f82016-03-18 18:43:08 -0700382 return false;
Dominik Behr83010f82016-03-18 18:43:08 -0700383 return true;
384}
385
386static int drm_score(drm_t* drm)
387{
388 drmVersionPtr version;
389 int score = 0;
390
391 if (!drm)
392 return -1000000000;
393
Dominik Behr57b5c722018-08-08 18:52:31 -0700394 if (!drm->console_connector_id)
Dominik Behr83010f82016-03-18 18:43:08 -0700395 return -1000000000;
396
Dominik Behr57b5c722018-08-08 18:52:31 -0700397 if (drm->console_connector_internal)
Dominik Behr83010f82016-03-18 18:43:08 -0700398 score++;
399
400 version = drmGetVersion(drm->fd);
401 if (version) {
Dominik Behrb1abcba2016-04-14 14:57:21 -0700402 /* We would rather use any driver besides UDL. */
Dominik Behr83010f82016-03-18 18:43:08 -0700403 if (strcmp("udl", version->name) == 0)
404 score--;
405 if (strcmp("evdi", version->name) == 0)
406 score--;
Dominik Behrb1abcba2016-04-14 14:57:21 -0700407 /* VGEM should be ignored because it has no displays, but lets make sure. */
Dominik Behr83010f82016-03-18 18:43:08 -0700408 if (strcmp("vgem", version->name) == 0)
409 score -= 1000000;
410 drmFreeVersion(version);
411 }
412 return score;
413}
414
Dominik Behrb1abcba2016-04-14 14:57:21 -0700415/*
416 * Scan and find best DRM object to display frecon on.
417 * This object should be created with DRM master, and we will keep master till
418 * first mode set or explicit drop master.
419 */
Dominik Behr83010f82016-03-18 18:43:08 -0700420drm_t* drm_scan(void)
421{
422 unsigned i;
423 char* dev_name;
424 int ret;
425 drm_t *best_drm = NULL;
426
427 for (i = 0; i < DRM_MAX_MINOR; i++) {
Rob Clark93af3522020-10-01 15:34:30 -0700428 uint64_t atomic = 0;
Dominik Behr83010f82016-03-18 18:43:08 -0700429 drm_t* drm = calloc(1, sizeof(drm_t));
430
431 if (!drm)
432 return NULL;
433
Dominik Behr815dc3d2016-04-19 16:59:39 -0700434try_open_again:
Dominik Behr83010f82016-03-18 18:43:08 -0700435 ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
436 if (ret < 0) {
437 drm_fini(drm);
438 continue;
439 }
Dominik Behr83010f82016-03-18 18:43:08 -0700440 drm->fd = open(dev_name, O_RDWR, 0);
441 free(dev_name);
442 if (drm->fd < 0) {
443 drm_fini(drm);
444 continue;
445 }
Dominik Behr815dc3d2016-04-19 16:59:39 -0700446 /* if we have master this should succeed */
447 ret = drmSetMaster(drm->fd);
448 if (ret != 0) {
449 drmClose(drm->fd);
450 drm->fd = -1;
451 usleep(100*1000);
452 goto try_open_again;
453 }
Dominik Behr83010f82016-03-18 18:43:08 -0700454
Dominik Behr57b5c722018-08-08 18:52:31 -0700455 /* Set universal planes cap if possible. Ignore any errors. */
456 drmSetClientCap(drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
457
Rob Clark93af3522020-10-01 15:34:30 -0700458 ret = drmGetCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, &atomic);
459 if (!ret && atomic) {
Dominik Behrbf08a552018-08-07 19:46:03 -0700460 drm->atomic = true;
Rob Clark93af3522020-10-01 15:34:30 -0700461 ret = drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1);
462 if (ret < 0) {
463 LOG(ERROR, "Failed to set atomic cap.");
464 drm->atomic = false;
465 }
Dominik Behrbf08a552018-08-07 19:46:03 -0700466 }
467
Dominik Behr83010f82016-03-18 18:43:08 -0700468 drm->resources = drmModeGetResources(drm->fd);
469 if (!drm->resources) {
470 drm_fini(drm);
471 continue;
472 }
473
Dominik Behrb1abcba2016-04-14 14:57:21 -0700474 /* Expect at least one crtc so we do not try to run on VGEM. */
Dominik Behr83010f82016-03-18 18:43:08 -0700475 if (drm->resources->count_crtcs == 0 || drm->resources->count_connectors == 0) {
476 drm_fini(drm);
477 continue;
478 }
479
Dominik Behr83010f82016-03-18 18:43:08 -0700480 drm->plane_resources = drmModeGetPlaneResources(drm->fd);
Dominik Behr57b5c722018-08-08 18:52:31 -0700481
482 if (!find_main_monitor(drm)) {
483 drm_fini(drm);
484 continue;
485 }
486
Dominik Behr83010f82016-03-18 18:43:08 -0700487 drm->refcount = 1;
488
489 if (drm_score(drm) > drm_score(best_drm)) {
490 drm_fini(best_drm);
491 best_drm = drm;
Dominik Behr45896022016-11-10 14:12:59 -0800492 } else {
493 drm_fini(drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700494 }
495 }
496
497 if (best_drm) {
498 drmVersionPtr version;
499 version = drmGetVersion(best_drm->fd);
500 if (version) {
501 LOG(INFO,
Dominik Behrbf08a552018-08-07 19:46:03 -0700502 "Frecon using drm driver %s, version %d.%d, date(%s), desc(%s)%s",
Dominik Behr83010f82016-03-18 18:43:08 -0700503 version->name,
504 version->version_major,
505 version->version_minor,
506 version->date,
Dominik Behrbf08a552018-08-07 19:46:03 -0700507 version->desc,
508 best_drm->atomic ? " using atomic" : "");
Dominik Behr83010f82016-03-18 18:43:08 -0700509 drmFreeVersion(version);
510 }
Dominik Behr83010f82016-03-18 18:43:08 -0700511 }
512
513 return best_drm;
514}
515
516void drm_set(drm_t* drm_)
517{
Dominik Behrda3c0102016-06-08 15:05:38 -0700518 if (g_drm) {
519 drm_delref(g_drm);
520 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700521 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700522 g_drm = drm_;
Dominik Behr83010f82016-03-18 18:43:08 -0700523}
524
525void drm_close(void)
526{
Dominik Behrda3c0102016-06-08 15:05:38 -0700527 if (g_drm) {
528 drm_delref(g_drm);
529 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700530 }
531}
532
Dominik Behrb1abcba2016-04-14 14:57:21 -0700533void drm_delref(drm_t* drm)
Dominik Behr83010f82016-03-18 18:43:08 -0700534{
Dominik Behrd4d56272016-03-31 18:55:17 -0700535 if (!drm)
536 return;
Dominik Behr83010f82016-03-18 18:43:08 -0700537 if (drm->refcount) {
538 drm->refcount--;
539 } else {
540 LOG(ERROR, "Imbalanced drm_close()");
541 }
542 if (drm->refcount) {
543 return;
544 }
545
546 drm_fini(drm);
547}
548
549drm_t* drm_addref(void)
550{
Dominik Behrda3c0102016-06-08 15:05:38 -0700551 if (g_drm) {
552 g_drm->refcount++;
553 return g_drm;
Dominik Behr83010f82016-03-18 18:43:08 -0700554 }
555
556 return NULL;
557}
558
Dominik Behrda3c0102016-06-08 15:05:38 -0700559int drm_dropmaster(drm_t* drm)
Dominik Behrb1abcba2016-04-14 14:57:21 -0700560{
Dominik Behrda3c0102016-06-08 15:05:38 -0700561 int ret = 0;
562
563 if (!drm)
564 drm = g_drm;
565 if (drm)
566 ret = drmDropMaster(drm->fd);
567 return ret;
568}
569
570int drm_setmaster(drm_t* drm)
571{
572 int ret = 0;
573
574 if (!drm)
575 drm = g_drm;
576 if (drm)
577 ret = drmSetMaster(drm->fd);
578 return ret;
Dominik Behrb1abcba2016-04-14 14:57:21 -0700579}
580
Dominik Behr83010f82016-03-18 18:43:08 -0700581/*
Dominik Behrb1abcba2016-04-14 14:57:21 -0700582 * Returns true if connector/crtc/driver have changed and framebuffer object have to be re-created.
Dominik Behr83010f82016-03-18 18:43:08 -0700583 */
584bool drm_rescan(void)
585{
586 drm_t* ndrm;
587
Dominik Behrb1abcba2016-04-14 14:57:21 -0700588 /* In case we had master, drop master so the newly created object could have it. */
Dominik Behrda3c0102016-06-08 15:05:38 -0700589 drm_dropmaster(g_drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700590 ndrm = drm_scan();
591 if (ndrm) {
Dominik Behrda3c0102016-06-08 15:05:38 -0700592 if (drm_equal(ndrm, g_drm)) {
Dominik Behr83010f82016-03-18 18:43:08 -0700593 drm_fini(ndrm);
Dominik Behrda3c0102016-06-08 15:05:38 -0700594 /* Regain master we dropped. */
595 drm_setmaster(g_drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700596 } else {
Dominik Behrda3c0102016-06-08 15:05:38 -0700597 drm_delref(g_drm);
598 g_drm = ndrm;
Dominik Behr83010f82016-03-18 18:43:08 -0700599 return true;
600 }
601 } else {
Dominik Behrda3c0102016-06-08 15:05:38 -0700602 if (g_drm) {
603 drm_delref(g_drm); /* No usable monitor/drm object. */
604 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700605 return true;
606 }
607 }
608 return false;
609}
610
611bool drm_valid(drm_t* drm) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700612 return drm && drm->fd >= 0 && drm->resources && drm->console_connector_id;
Dominik Behr83010f82016-03-18 18:43:08 -0700613}
614
Dominik Behrbf08a552018-08-07 19:46:03 -0700615static bool is_crtc_possible(drm_t* drm, uint32_t crtc_id, uint32_t mask)
616{
617 int32_t crtc;
618 for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++)
619 if (drm->resources->crtcs[crtc] == crtc_id)
620 return !!(mask & (1u << crtc));
621
622 return false;
623
624}
625
626#define CHECK(fn) do { ret = fn; if (ret < 0) goto error_mode; } while (0)
627static int32_t drm_setmode_atomic(drm_t* drm, uint32_t fb_id)
628{
629 int32_t ret;
630 int32_t crtc, conn;
631 uint32_t plane;
632 uint32_t console_crtc_id = 0;
633 drmModeObjectPropertiesPtr crtc_props = NULL;
634 drmModeObjectPropertiesPtr plane_props = NULL;
635 drmModeObjectPropertiesPtr conn_props = NULL;
636 drmModePlaneResPtr plane_resources;
637 drmModeAtomicReqPtr pset = NULL;
638 uint32_t mode_id = 0;
639
640 plane_resources = drmModeGetPlaneResources(drm->fd);
641 if (!plane_resources)
642 return -ENOENT;
643
644 get_connector_path(drm, drm->console_connector_id, NULL, &console_crtc_id);
645 if (!console_crtc_id)
646 find_crtc_for_connector(drm, drm->console_connector_id, &console_crtc_id);
647 if (!console_crtc_id) {
648 LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
649 return -ENOENT;
650 }
651
652 pset = drmModeAtomicAlloc();
653 if (!pset) {
654 ret = -ENOMEM;
655 goto error_mode;
656 }
657
658 for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++) {
659 uint32_t crtc_id = drm->resources->crtcs[crtc];
660
661 crtc_props = drmModeObjectGetProperties(drm->fd, crtc_id, DRM_MODE_OBJECT_CRTC);
662
663 if (!crtc_props) {
664 LOG(ERROR, "Could not query properties for crtc %d %m.", crtc_id);
665 if (crtc_id != console_crtc_id)
666 continue;
667 ret = -ENOENT;
668 goto error_mode;
669 }
670
671 if (crtc_id == console_crtc_id) {
672 CHECK(drmModeCreatePropertyBlob(drm->fd, &drm->console_mode_info,
673 sizeof(drm->console_mode_info),
674 &mode_id));
675 /* drm->crtc->mode has been set during init */
676 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", mode_id));
677 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 1));
678 /* Reset color matrix to identity and gamma/degamma LUTs to pass through,
679 * ignore errors in case they are not supported. */
680 atomic_set_prop(drm, pset, crtc_id, crtc_props, "CTM", 0);
681 atomic_set_prop(drm, pset, crtc_id, crtc_props, "DEGAMMA_LUT", 0);
682 atomic_set_prop(drm, pset, crtc_id, crtc_props, "GAMMA_LUT", 0);
683 } else {
684 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", 0));
685 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 0));
686 }
687
688 drmModeFreeObjectProperties(crtc_props);
689 crtc_props = NULL;
690 }
691
692 for (plane = 0; plane < plane_resources->count_planes; plane++) {
693 drmModePlanePtr planeobj;
694 uint32_t plane_id = plane_resources->planes[plane];
695 uint32_t possible_crtcs;
696 int primary;
697
698 planeobj = drmModeGetPlane(drm->fd, plane_id);
699 if (!planeobj) {
700 LOG(ERROR, "Could not query plane object for plane %d %m.", plane_id);
701 ret = -ENOENT;
702 goto error_mode;
703 }
704
705 possible_crtcs = planeobj->possible_crtcs;
706 drmModeFreePlane(planeobj);
707
708 primary = drm_is_primary_plane(drm, plane_id);
709
710 plane_props = drmModeObjectGetProperties(drm->fd, plane_id, DRM_MODE_OBJECT_PLANE);
711 if (!plane_props) {
712 LOG(ERROR, "Could not query properties for plane %d %m.", plane_id);
713 ret = -ENOENT;
714 goto error_mode;
715 }
716
717 if (is_crtc_possible(drm, console_crtc_id, possible_crtcs) && primary) {
718 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", fb_id));
719 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", console_crtc_id));
720 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_X", 0));
721 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_Y", 0));
722 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_W", drm->console_mode_info.hdisplay));
723 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_H", drm->console_mode_info.vdisplay));
724 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_X", 0));
725 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_Y", 0));
726 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_W", drm->console_mode_info.hdisplay << 16));
727 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_H", drm->console_mode_info.vdisplay << 16));
728 } else {
729 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", 0));
730 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", 0));
731 }
732
733 drmModeFreeObjectProperties(plane_props);
734 plane_props = NULL;
735 }
736
737 for (conn = 0; conn < drm->resources->count_connectors; conn++) {
738 uint32_t conn_id = drm->resources->connectors[conn];
739
740 conn_props = drmModeObjectGetProperties(drm->fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
741 if (!conn_props) {
742 LOG(ERROR, "Could not query properties for connector %d %m.", conn_id);
743 if (conn_id != drm->console_connector_id)
744 continue;
745 ret = -ENOENT;
746 goto error_mode;
747 }
748 if (conn_id == drm->console_connector_id)
749 CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", console_crtc_id));
750 else
751 CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", 0));
752 drmModeFreeObjectProperties(conn_props);
753 conn_props = NULL;
754 }
755
756 ret = drmModeAtomicCommit(drm->fd, pset,
757 DRM_MODE_ATOMIC_ALLOW_MODESET , NULL);
758 if (ret < 0) {
759 drm_clear_rmfb(drm);
760 /* LOG(INFO, "TIMING: Console switch atomic modeset finished."); */
761 } else {
762 ret = 0;
763 }
764
765error_mode:
766 if (mode_id)
767 drmModeDestroyPropertyBlob(drm->fd, mode_id);
768
769 if (plane_resources)
770 drmModeFreePlaneResources(plane_resources);
771
772 if (crtc_props)
773 drmModeFreeObjectProperties(crtc_props);
774
775 if (conn_props)
776 drmModeFreeObjectProperties(conn_props);
777
778 if (plane_props)
779 drmModeFreeObjectProperties(plane_props);
780
781 drmModeAtomicFree(pset);
782 return ret;
783}
784#undef CHECK
785
Dominik Behr83010f82016-03-18 18:43:08 -0700786int32_t drm_setmode(drm_t* drm, uint32_t fb_id)
787{
Dominik Behr57b5c722018-08-08 18:52:31 -0700788 int conn;
Dominik Behr83010f82016-03-18 18:43:08 -0700789 int32_t ret;
Dominik Behr57b5c722018-08-08 18:52:31 -0700790 uint32_t existing_console_crtc_id = 0;
Dominik Behr51a4da52016-07-28 14:18:48 -0700791
Dominik Behrbf08a552018-08-07 19:46:03 -0700792 if (drm->atomic)
793 if (drm_setmode_atomic(drm, fb_id) == 0)
794 return 0;
795 /* Fallback to legacy mode set. */
796
Dominik Behr57b5c722018-08-08 18:52:31 -0700797 get_connector_path(drm, drm->console_connector_id, NULL, &existing_console_crtc_id);
Dominik Behr51a4da52016-07-28 14:18:48 -0700798
Dominik Behr57b5c722018-08-08 18:52:31 -0700799 /* Loop through all the connectors, disable ones that are configured and set video mode on console connector. */
800 for (conn = 0; conn < drm->resources->count_connectors; conn++) {
801 uint32_t connector_id = drm->resources->connectors[conn];
Dominik Behr83010f82016-03-18 18:43:08 -0700802
Dominik Behr57b5c722018-08-08 18:52:31 -0700803 if (connector_id == drm->console_connector_id) {
804 uint32_t console_crtc_id = 0;
805
806 if (existing_console_crtc_id)
807 console_crtc_id = existing_console_crtc_id;
808 else {
809 find_crtc_for_connector(drm, connector_id, &console_crtc_id);
810
811 if (!console_crtc_id) {
812 LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
813 return -ENOENT;
814 }
815 }
816
817 ret = drmModeSetCrtc(drm->fd, console_crtc_id,
818 fb_id,
819 0, 0, // x,y
820 &drm->console_connector_id,
821 1, // connector_count
822 &drm->console_mode_info); // mode
823
824 if (ret) {
825 LOG(ERROR, "Unable to set crtc:%d connector:%d %m", console_crtc_id, drm->console_connector_id);
826 return ret;
827 }
828
829 ret = drmModeSetCursor(drm->fd, console_crtc_id,
830 0, 0, 0);
831
832 if (ret)
833 LOG(ERROR, "Unable to hide cursor on crtc:%d %m.", console_crtc_id);
834
835 drm_disable_non_primary_planes(drm, console_crtc_id);
836
837 } else {
838 uint32_t crtc_id = 0;
839
840 get_connector_path(drm, connector_id, NULL, &crtc_id);
841 if (!crtc_id)
842 /* This connector is not configured, skip. */
843 continue;
844
845 if (existing_console_crtc_id && existing_console_crtc_id == crtc_id)
846 /* This connector is mirroring from the same CRTC as console. It will be turned off when console is set. */
847 continue;
848
849 ret = drmModeSetCrtc(drm->fd, crtc_id, 0, // buffer_id
850 0, 0, // x,y
851 NULL, // connectors
852 0, // connector_count
853 NULL); // mode
854 if (ret)
855 LOG(ERROR, "Unable to disable crtc %d: %m", crtc_id);
856 }
Dominik Behr83010f82016-03-18 18:43:08 -0700857 }
858
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800859 drm_clear_rmfb(drm);
Dominik Behr57b5c722018-08-08 18:52:31 -0700860 /* LOG(INFO, "TIMING: Console switch modeset finished."); */
Dominik Behr83010f82016-03-18 18:43:08 -0700861 return ret;
862}
863
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800864/*
865 * Delayed rmfb(). We want to keep fb at least till after next modeset
866 * so our transitions are cleaner (e.g. when recreating term after exitin
867 * shell). Also it keeps fb around till Chrome starts.
868 */
869void drm_rmfb(drm_t* drm, uint32_t fb_id)
870{
871 drm_clear_rmfb(drm);
872 drm->delayed_rmfb_fb_id = fb_id;
873}
874
Dominik Behr83010f82016-03-18 18:43:08 -0700875bool drm_read_edid(drm_t* drm)
876{
Dominik Behr57b5c722018-08-08 18:52:31 -0700877 drmModeConnector* console_connector;
Dominik Behr83010f82016-03-18 18:43:08 -0700878 if (drm->edid_found) {
879 return true;
880 }
881
Dominik Behr57b5c722018-08-08 18:52:31 -0700882 console_connector = drmModeGetConnector(drm->fd, drm->console_connector_id);
883
884 if (!console_connector)
885 return false;
886
887 for (int i = 0; i < console_connector->count_props; i++) {
Dominik Behr83010f82016-03-18 18:43:08 -0700888 drmModePropertyPtr prop;
889 drmModePropertyBlobPtr blob_ptr;
Dominik Behr57b5c722018-08-08 18:52:31 -0700890 prop = drmModeGetProperty(drm->fd, console_connector->props[i]);
Dominik Behr83010f82016-03-18 18:43:08 -0700891 if (prop) {
892 if (strcmp(prop->name, "EDID") == 0) {
893 blob_ptr = drmModeGetPropertyBlob(drm->fd,
Dominik Behr57b5c722018-08-08 18:52:31 -0700894 console_connector->prop_values[i]);
Dominik Behr83010f82016-03-18 18:43:08 -0700895 if (blob_ptr) {
896 memcpy(&drm->edid, blob_ptr->data, EDID_SIZE);
897 drmModeFreePropertyBlob(blob_ptr);
Dominik Behr57b5c722018-08-08 18:52:31 -0700898 drmModeFreeConnector(console_connector);
Dominik Behr83010f82016-03-18 18:43:08 -0700899 return (drm->edid_found = true);
900 }
901 }
902 }
903 }
904
Dominik Behr57b5c722018-08-08 18:52:31 -0700905 drmModeFreeConnector(console_connector);
Dominik Behr83010f82016-03-18 18:43:08 -0700906 return false;
907}
908
909uint32_t drm_gethres(drm_t* drm)
910{
Dominik Behr57b5c722018-08-08 18:52:31 -0700911 return drm->console_mode_info.hdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -0700912}
913
914uint32_t drm_getvres(drm_t* drm)
915{
Dominik Behr57b5c722018-08-08 18:52:31 -0700916 return drm->console_mode_info.vdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -0700917}