blob: 8e7ad9559ee3b3ac5ceb639113d56793bd3bcd07 [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 Behr57b5c722018-08-08 18:52:31 -0700246static bool find_main_monitor(drm_t* drm)
Dominik Behr83010f82016-03-18 18:43:08 -0700247{
248 int modes;
Dominik Behr57b5c722018-08-08 18:52:31 -0700249 uint32_t console_crtc_id = 0;
Dominik Behrd2cc4d22016-01-29 17:31:52 -0800250 int lid_state = input_check_lid_state();
Dominik Behr83010f82016-03-18 18:43:08 -0700251 drmModeConnector* main_monitor_connector = NULL;
252
Dominik Behr57b5c722018-08-08 18:52:31 -0700253 drm->console_connector_id = 0;
254
Dominik Behr83010f82016-03-18 18:43:08 -0700255 /*
256 * Find the LVDS/eDP/DSI connectors. Those are the main screens.
257 */
Dominik Behrd2cc4d22016-01-29 17:31:52 -0800258 if (lid_state <= 0)
259 main_monitor_connector = find_first_connected_connector(drm, true, false);
Dominik Behr83010f82016-03-18 18:43:08 -0700260
261 /*
262 * Now try external connectors.
263 */
264 if (!main_monitor_connector)
265 main_monitor_connector =
266 find_first_connected_connector(drm, false, true);
267
268 /*
269 * If we still didn't find a connector, give up and return.
270 */
271 if (!main_monitor_connector)
Dominik Behr57b5c722018-08-08 18:52:31 -0700272 return false;
Dominik Behr83010f82016-03-18 18:43:08 -0700273
Dominik Behr57b5c722018-08-08 18:52:31 -0700274 if (!main_monitor_connector->count_modes)
275 return false;
276
277 drm->console_connector_id = main_monitor_connector->connector_id;
278 drm->console_connector_internal = drm_is_internal(main_monitor_connector->connector_type);
279 drm->console_mmWidth = main_monitor_connector->mmWidth;
280 drm->console_mmHeight = main_monitor_connector->mmHeight;
281
Dominik Behr83010f82016-03-18 18:43:08 -0700282 for (modes = 0; modes < main_monitor_connector->count_modes; modes++) {
283 if (main_monitor_connector->modes[modes].type &
284 DRM_MODE_TYPE_PREFERRED) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700285 drm->console_mode_info = main_monitor_connector->modes[modes];
Dominik Behr83010f82016-03-18 18:43:08 -0700286 break;
287 }
288 }
Dominik Behr57b5c722018-08-08 18:52:31 -0700289 /* If there was no preferred mode use first one. */
290 if (modes == main_monitor_connector->count_modes)
291 drm->console_mode_info = main_monitor_connector->modes[0];
Dominik Behr83010f82016-03-18 18:43:08 -0700292
Dominik Behr57b5c722018-08-08 18:52:31 -0700293 drmModeFreeConnector(main_monitor_connector);
294
295 get_connector_path(drm, drm->console_connector_id, NULL, &console_crtc_id);
296
297 if (!console_crtc_id)
298 /* No existing path, find one. */
299 find_crtc_for_connector(drm, drm->console_connector_id, &console_crtc_id);
300
301 if (!console_crtc_id)
302 /* Cannot find CRTC for connector. We will not be able to use it. */
303 return false;
304
305 return true;
Dominik Behr83010f82016-03-18 18:43:08 -0700306}
307
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800308static void drm_clear_rmfb(drm_t* drm)
309{
310 if (drm->delayed_rmfb_fb_id) {
311 drmModeRmFB(drm->fd, drm->delayed_rmfb_fb_id);
312 drm->delayed_rmfb_fb_id = 0;
313 }
314}
315
Dominik Behr83010f82016-03-18 18:43:08 -0700316static void drm_fini(drm_t* drm)
317{
318 if (!drm)
319 return;
320
321 if (drm->fd >= 0) {
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800322 drm_clear_rmfb(drm);
323
Dominik Behr83010f82016-03-18 18:43:08 -0700324 if (drm->plane_resources) {
325 drmModeFreePlaneResources(drm->plane_resources);
326 drm->plane_resources = NULL;
327 }
328
329 if (drm->resources) {
330 drmModeFreeResources(drm->resources);
331 drm->resources = NULL;
332 }
333
334 drmClose(drm->fd);
335 drm->fd = -1;
336 }
337
338 free(drm);
339}
340
341static bool drm_equal(drm_t* l, drm_t* r)
342{
Dominik Behrd4d56272016-03-31 18:55:17 -0700343 if (!l && !r)
344 return true;
345 if ((!l && r) || (l && !r))
346 return false;
Dominik Behr83010f82016-03-18 18:43:08 -0700347
Dominik Behr57b5c722018-08-08 18:52:31 -0700348 if (l->console_connector_id != r->console_connector_id)
Dominik Behr83010f82016-03-18 18:43:08 -0700349 return false;
Dominik Behr83010f82016-03-18 18:43:08 -0700350 return true;
351}
352
353static int drm_score(drm_t* drm)
354{
355 drmVersionPtr version;
356 int score = 0;
357
358 if (!drm)
359 return -1000000000;
360
Dominik Behr57b5c722018-08-08 18:52:31 -0700361 if (!drm->console_connector_id)
Dominik Behr83010f82016-03-18 18:43:08 -0700362 return -1000000000;
363
Dominik Behr57b5c722018-08-08 18:52:31 -0700364 if (drm->console_connector_internal)
Dominik Behr83010f82016-03-18 18:43:08 -0700365 score++;
366
367 version = drmGetVersion(drm->fd);
368 if (version) {
Dominik Behrb1abcba2016-04-14 14:57:21 -0700369 /* We would rather use any driver besides UDL. */
Dominik Behr83010f82016-03-18 18:43:08 -0700370 if (strcmp("udl", version->name) == 0)
371 score--;
372 if (strcmp("evdi", version->name) == 0)
373 score--;
Dominik Behrb1abcba2016-04-14 14:57:21 -0700374 /* VGEM should be ignored because it has no displays, but lets make sure. */
Dominik Behr83010f82016-03-18 18:43:08 -0700375 if (strcmp("vgem", version->name) == 0)
376 score -= 1000000;
377 drmFreeVersion(version);
378 }
379 return score;
380}
381
Dominik Behrb1abcba2016-04-14 14:57:21 -0700382/*
383 * Scan and find best DRM object to display frecon on.
384 * This object should be created with DRM master, and we will keep master till
385 * first mode set or explicit drop master.
386 */
Dominik Behr83010f82016-03-18 18:43:08 -0700387drm_t* drm_scan(void)
388{
389 unsigned i;
390 char* dev_name;
391 int ret;
392 drm_t *best_drm = NULL;
393
394 for (i = 0; i < DRM_MAX_MINOR; i++) {
Dominik Behrbf08a552018-08-07 19:46:03 -0700395 uint64_t atomic = 0;
Dominik Behr83010f82016-03-18 18:43:08 -0700396 drm_t* drm = calloc(1, sizeof(drm_t));
397
398 if (!drm)
399 return NULL;
400
Dominik Behr815dc3d2016-04-19 16:59:39 -0700401try_open_again:
Dominik Behr83010f82016-03-18 18:43:08 -0700402 ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
403 if (ret < 0) {
404 drm_fini(drm);
405 continue;
406 }
Dominik Behr83010f82016-03-18 18:43:08 -0700407 drm->fd = open(dev_name, O_RDWR, 0);
408 free(dev_name);
409 if (drm->fd < 0) {
410 drm_fini(drm);
411 continue;
412 }
Dominik Behr815dc3d2016-04-19 16:59:39 -0700413 /* if we have master this should succeed */
414 ret = drmSetMaster(drm->fd);
415 if (ret != 0) {
416 drmClose(drm->fd);
417 drm->fd = -1;
418 usleep(100*1000);
419 goto try_open_again;
420 }
Dominik Behr83010f82016-03-18 18:43:08 -0700421
Dominik Behr57b5c722018-08-08 18:52:31 -0700422 /* Set universal planes cap if possible. Ignore any errors. */
423 drmSetClientCap(drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
424
Dominik Behrbf08a552018-08-07 19:46:03 -0700425 ret = drmGetCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, &atomic);
426 if (!ret && atomic) {
427 drm->atomic = true;
428 ret = drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1);
429 if (ret < 0) {
430 LOG(ERROR, "Failed to set atomic cap.");
431 drm->atomic = false;
432 }
433 }
434
Dominik Behr83010f82016-03-18 18:43:08 -0700435 drm->resources = drmModeGetResources(drm->fd);
436 if (!drm->resources) {
437 drm_fini(drm);
438 continue;
439 }
440
Dominik Behrb1abcba2016-04-14 14:57:21 -0700441 /* Expect at least one crtc so we do not try to run on VGEM. */
Dominik Behr83010f82016-03-18 18:43:08 -0700442 if (drm->resources->count_crtcs == 0 || drm->resources->count_connectors == 0) {
443 drm_fini(drm);
444 continue;
445 }
446
Dominik Behr83010f82016-03-18 18:43:08 -0700447 drm->plane_resources = drmModeGetPlaneResources(drm->fd);
Dominik Behr57b5c722018-08-08 18:52:31 -0700448
449 if (!find_main_monitor(drm)) {
450 drm_fini(drm);
451 continue;
452 }
453
Dominik Behr83010f82016-03-18 18:43:08 -0700454 drm->refcount = 1;
455
456 if (drm_score(drm) > drm_score(best_drm)) {
457 drm_fini(best_drm);
458 best_drm = drm;
Dominik Behr45896022016-11-10 14:12:59 -0800459 } else {
460 drm_fini(drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700461 }
462 }
463
464 if (best_drm) {
465 drmVersionPtr version;
466 version = drmGetVersion(best_drm->fd);
467 if (version) {
468 LOG(INFO,
Dominik Behrbf08a552018-08-07 19:46:03 -0700469 "Frecon using drm driver %s, version %d.%d, date(%s), desc(%s)%s",
Dominik Behr83010f82016-03-18 18:43:08 -0700470 version->name,
471 version->version_major,
472 version->version_minor,
473 version->date,
Dominik Behrbf08a552018-08-07 19:46:03 -0700474 version->desc,
475 best_drm->atomic ? " using atomic" : "");
Dominik Behr83010f82016-03-18 18:43:08 -0700476 drmFreeVersion(version);
477 }
Dominik Behr83010f82016-03-18 18:43:08 -0700478 }
479
480 return best_drm;
481}
482
483void drm_set(drm_t* drm_)
484{
Dominik Behrda3c0102016-06-08 15:05:38 -0700485 if (g_drm) {
486 drm_delref(g_drm);
487 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700488 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700489 g_drm = drm_;
Dominik Behr83010f82016-03-18 18:43:08 -0700490}
491
492void drm_close(void)
493{
Dominik Behrda3c0102016-06-08 15:05:38 -0700494 if (g_drm) {
495 drm_delref(g_drm);
496 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700497 }
498}
499
Dominik Behrb1abcba2016-04-14 14:57:21 -0700500void drm_delref(drm_t* drm)
Dominik Behr83010f82016-03-18 18:43:08 -0700501{
Dominik Behrd4d56272016-03-31 18:55:17 -0700502 if (!drm)
503 return;
Dominik Behr83010f82016-03-18 18:43:08 -0700504 if (drm->refcount) {
505 drm->refcount--;
506 } else {
507 LOG(ERROR, "Imbalanced drm_close()");
508 }
509 if (drm->refcount) {
510 return;
511 }
512
513 drm_fini(drm);
514}
515
516drm_t* drm_addref(void)
517{
Dominik Behrda3c0102016-06-08 15:05:38 -0700518 if (g_drm) {
519 g_drm->refcount++;
520 return g_drm;
Dominik Behr83010f82016-03-18 18:43:08 -0700521 }
522
523 return NULL;
524}
525
Dominik Behrda3c0102016-06-08 15:05:38 -0700526int drm_dropmaster(drm_t* drm)
Dominik Behrb1abcba2016-04-14 14:57:21 -0700527{
Dominik Behrda3c0102016-06-08 15:05:38 -0700528 int ret = 0;
529
530 if (!drm)
531 drm = g_drm;
532 if (drm)
533 ret = drmDropMaster(drm->fd);
534 return ret;
535}
536
537int drm_setmaster(drm_t* drm)
538{
539 int ret = 0;
540
541 if (!drm)
542 drm = g_drm;
543 if (drm)
544 ret = drmSetMaster(drm->fd);
545 return ret;
Dominik Behrb1abcba2016-04-14 14:57:21 -0700546}
547
Dominik Behr83010f82016-03-18 18:43:08 -0700548/*
Dominik Behrb1abcba2016-04-14 14:57:21 -0700549 * Returns true if connector/crtc/driver have changed and framebuffer object have to be re-created.
Dominik Behr83010f82016-03-18 18:43:08 -0700550 */
551bool drm_rescan(void)
552{
553 drm_t* ndrm;
554
Dominik Behrb1abcba2016-04-14 14:57:21 -0700555 /* In case we had master, drop master so the newly created object could have it. */
Dominik Behrda3c0102016-06-08 15:05:38 -0700556 drm_dropmaster(g_drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700557 ndrm = drm_scan();
558 if (ndrm) {
Dominik Behrda3c0102016-06-08 15:05:38 -0700559 if (drm_equal(ndrm, g_drm)) {
Dominik Behr83010f82016-03-18 18:43:08 -0700560 drm_fini(ndrm);
Dominik Behrda3c0102016-06-08 15:05:38 -0700561 /* Regain master we dropped. */
562 drm_setmaster(g_drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700563 } else {
Dominik Behrda3c0102016-06-08 15:05:38 -0700564 drm_delref(g_drm);
565 g_drm = ndrm;
Dominik Behr83010f82016-03-18 18:43:08 -0700566 return true;
567 }
568 } else {
Dominik Behrda3c0102016-06-08 15:05:38 -0700569 if (g_drm) {
570 drm_delref(g_drm); /* No usable monitor/drm object. */
571 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700572 return true;
573 }
574 }
575 return false;
576}
577
578bool drm_valid(drm_t* drm) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700579 return drm && drm->fd >= 0 && drm->resources && drm->console_connector_id;
Dominik Behr83010f82016-03-18 18:43:08 -0700580}
581
Dominik Behrbf08a552018-08-07 19:46:03 -0700582static bool is_crtc_possible(drm_t* drm, uint32_t crtc_id, uint32_t mask)
583{
584 int32_t crtc;
585 for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++)
586 if (drm->resources->crtcs[crtc] == crtc_id)
587 return !!(mask & (1u << crtc));
588
589 return false;
590
591}
592
593#define CHECK(fn) do { ret = fn; if (ret < 0) goto error_mode; } while (0)
594static int32_t drm_setmode_atomic(drm_t* drm, uint32_t fb_id)
595{
596 int32_t ret;
597 int32_t crtc, conn;
598 uint32_t plane;
599 uint32_t console_crtc_id = 0;
600 drmModeObjectPropertiesPtr crtc_props = NULL;
601 drmModeObjectPropertiesPtr plane_props = NULL;
602 drmModeObjectPropertiesPtr conn_props = NULL;
603 drmModePlaneResPtr plane_resources;
604 drmModeAtomicReqPtr pset = NULL;
605 uint32_t mode_id = 0;
606
607 plane_resources = drmModeGetPlaneResources(drm->fd);
608 if (!plane_resources)
609 return -ENOENT;
610
611 get_connector_path(drm, drm->console_connector_id, NULL, &console_crtc_id);
612 if (!console_crtc_id)
613 find_crtc_for_connector(drm, drm->console_connector_id, &console_crtc_id);
614 if (!console_crtc_id) {
615 LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
616 return -ENOENT;
617 }
618
619 pset = drmModeAtomicAlloc();
620 if (!pset) {
621 ret = -ENOMEM;
622 goto error_mode;
623 }
624
625 for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++) {
626 uint32_t crtc_id = drm->resources->crtcs[crtc];
627
628 crtc_props = drmModeObjectGetProperties(drm->fd, crtc_id, DRM_MODE_OBJECT_CRTC);
629
630 if (!crtc_props) {
631 LOG(ERROR, "Could not query properties for crtc %d %m.", crtc_id);
632 if (crtc_id != console_crtc_id)
633 continue;
634 ret = -ENOENT;
635 goto error_mode;
636 }
637
638 if (crtc_id == console_crtc_id) {
639 CHECK(drmModeCreatePropertyBlob(drm->fd, &drm->console_mode_info,
640 sizeof(drm->console_mode_info),
641 &mode_id));
642 /* drm->crtc->mode has been set during init */
643 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", mode_id));
644 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 1));
645 /* Reset color matrix to identity and gamma/degamma LUTs to pass through,
646 * ignore errors in case they are not supported. */
647 atomic_set_prop(drm, pset, crtc_id, crtc_props, "CTM", 0);
648 atomic_set_prop(drm, pset, crtc_id, crtc_props, "DEGAMMA_LUT", 0);
649 atomic_set_prop(drm, pset, crtc_id, crtc_props, "GAMMA_LUT", 0);
650 } else {
651 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", 0));
652 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 0));
653 }
654
655 drmModeFreeObjectProperties(crtc_props);
656 crtc_props = NULL;
657 }
658
659 for (plane = 0; plane < plane_resources->count_planes; plane++) {
660 drmModePlanePtr planeobj;
661 uint32_t plane_id = plane_resources->planes[plane];
662 uint32_t possible_crtcs;
663 int primary;
664
665 planeobj = drmModeGetPlane(drm->fd, plane_id);
666 if (!planeobj) {
667 LOG(ERROR, "Could not query plane object for plane %d %m.", plane_id);
668 ret = -ENOENT;
669 goto error_mode;
670 }
671
672 possible_crtcs = planeobj->possible_crtcs;
673 drmModeFreePlane(planeobj);
674
675 primary = drm_is_primary_plane(drm, plane_id);
676
677 plane_props = drmModeObjectGetProperties(drm->fd, plane_id, DRM_MODE_OBJECT_PLANE);
678 if (!plane_props) {
679 LOG(ERROR, "Could not query properties for plane %d %m.", plane_id);
680 ret = -ENOENT;
681 goto error_mode;
682 }
683
684 if (is_crtc_possible(drm, console_crtc_id, possible_crtcs) && primary) {
685 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", fb_id));
686 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", console_crtc_id));
687 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_X", 0));
688 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_Y", 0));
689 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_W", drm->console_mode_info.hdisplay));
690 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_H", drm->console_mode_info.vdisplay));
691 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_X", 0));
692 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_Y", 0));
693 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_W", drm->console_mode_info.hdisplay << 16));
694 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_H", drm->console_mode_info.vdisplay << 16));
695 } else {
696 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", 0));
697 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", 0));
698 }
699
700 drmModeFreeObjectProperties(plane_props);
701 plane_props = NULL;
702 }
703
704 for (conn = 0; conn < drm->resources->count_connectors; conn++) {
705 uint32_t conn_id = drm->resources->connectors[conn];
706
707 conn_props = drmModeObjectGetProperties(drm->fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
708 if (!conn_props) {
709 LOG(ERROR, "Could not query properties for connector %d %m.", conn_id);
710 if (conn_id != drm->console_connector_id)
711 continue;
712 ret = -ENOENT;
713 goto error_mode;
714 }
715 if (conn_id == drm->console_connector_id)
716 CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", console_crtc_id));
717 else
718 CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", 0));
719 drmModeFreeObjectProperties(conn_props);
720 conn_props = NULL;
721 }
722
723 ret = drmModeAtomicCommit(drm->fd, pset,
724 DRM_MODE_ATOMIC_ALLOW_MODESET , NULL);
725 if (ret < 0) {
726 drm_clear_rmfb(drm);
727 /* LOG(INFO, "TIMING: Console switch atomic modeset finished."); */
728 } else {
729 ret = 0;
730 }
731
732error_mode:
733 if (mode_id)
734 drmModeDestroyPropertyBlob(drm->fd, mode_id);
735
736 if (plane_resources)
737 drmModeFreePlaneResources(plane_resources);
738
739 if (crtc_props)
740 drmModeFreeObjectProperties(crtc_props);
741
742 if (conn_props)
743 drmModeFreeObjectProperties(conn_props);
744
745 if (plane_props)
746 drmModeFreeObjectProperties(plane_props);
747
748 drmModeAtomicFree(pset);
749 return ret;
750}
751#undef CHECK
752
Dominik Behr83010f82016-03-18 18:43:08 -0700753int32_t drm_setmode(drm_t* drm, uint32_t fb_id)
754{
Dominik Behr57b5c722018-08-08 18:52:31 -0700755 int conn;
Dominik Behr83010f82016-03-18 18:43:08 -0700756 int32_t ret;
Dominik Behr57b5c722018-08-08 18:52:31 -0700757 uint32_t existing_console_crtc_id = 0;
Dominik Behr51a4da52016-07-28 14:18:48 -0700758
Dominik Behrbf08a552018-08-07 19:46:03 -0700759 if (drm->atomic)
760 if (drm_setmode_atomic(drm, fb_id) == 0)
761 return 0;
762 /* Fallback to legacy mode set. */
763
Dominik Behr57b5c722018-08-08 18:52:31 -0700764 get_connector_path(drm, drm->console_connector_id, NULL, &existing_console_crtc_id);
Dominik Behr51a4da52016-07-28 14:18:48 -0700765
Dominik Behr57b5c722018-08-08 18:52:31 -0700766 /* Loop through all the connectors, disable ones that are configured and set video mode on console connector. */
767 for (conn = 0; conn < drm->resources->count_connectors; conn++) {
768 uint32_t connector_id = drm->resources->connectors[conn];
Dominik Behr83010f82016-03-18 18:43:08 -0700769
Dominik Behr57b5c722018-08-08 18:52:31 -0700770 if (connector_id == drm->console_connector_id) {
771 uint32_t console_crtc_id = 0;
772
773 if (existing_console_crtc_id)
774 console_crtc_id = existing_console_crtc_id;
775 else {
776 find_crtc_for_connector(drm, connector_id, &console_crtc_id);
777
778 if (!console_crtc_id) {
779 LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
780 return -ENOENT;
781 }
782 }
783
784 ret = drmModeSetCrtc(drm->fd, console_crtc_id,
785 fb_id,
786 0, 0, // x,y
787 &drm->console_connector_id,
788 1, // connector_count
789 &drm->console_mode_info); // mode
790
791 if (ret) {
792 LOG(ERROR, "Unable to set crtc:%d connector:%d %m", console_crtc_id, drm->console_connector_id);
793 return ret;
794 }
795
796 ret = drmModeSetCursor(drm->fd, console_crtc_id,
797 0, 0, 0);
798
799 if (ret)
800 LOG(ERROR, "Unable to hide cursor on crtc:%d %m.", console_crtc_id);
801
802 drm_disable_non_primary_planes(drm, console_crtc_id);
803
804 } else {
805 uint32_t crtc_id = 0;
806
807 get_connector_path(drm, connector_id, NULL, &crtc_id);
808 if (!crtc_id)
809 /* This connector is not configured, skip. */
810 continue;
811
812 if (existing_console_crtc_id && existing_console_crtc_id == crtc_id)
813 /* This connector is mirroring from the same CRTC as console. It will be turned off when console is set. */
814 continue;
815
816 ret = drmModeSetCrtc(drm->fd, crtc_id, 0, // buffer_id
817 0, 0, // x,y
818 NULL, // connectors
819 0, // connector_count
820 NULL); // mode
821 if (ret)
822 LOG(ERROR, "Unable to disable crtc %d: %m", crtc_id);
823 }
Dominik Behr83010f82016-03-18 18:43:08 -0700824 }
825
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800826 drm_clear_rmfb(drm);
Dominik Behr57b5c722018-08-08 18:52:31 -0700827 /* LOG(INFO, "TIMING: Console switch modeset finished."); */
Dominik Behr83010f82016-03-18 18:43:08 -0700828 return ret;
829}
830
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800831/*
832 * Delayed rmfb(). We want to keep fb at least till after next modeset
833 * so our transitions are cleaner (e.g. when recreating term after exitin
834 * shell). Also it keeps fb around till Chrome starts.
835 */
836void drm_rmfb(drm_t* drm, uint32_t fb_id)
837{
838 drm_clear_rmfb(drm);
839 drm->delayed_rmfb_fb_id = fb_id;
840}
841
Dominik Behr83010f82016-03-18 18:43:08 -0700842bool drm_read_edid(drm_t* drm)
843{
Dominik Behr57b5c722018-08-08 18:52:31 -0700844 drmModeConnector* console_connector;
Dominik Behr83010f82016-03-18 18:43:08 -0700845 if (drm->edid_found) {
846 return true;
847 }
848
Dominik Behr57b5c722018-08-08 18:52:31 -0700849 console_connector = drmModeGetConnector(drm->fd, drm->console_connector_id);
850
851 if (!console_connector)
852 return false;
853
854 for (int i = 0; i < console_connector->count_props; i++) {
Dominik Behr83010f82016-03-18 18:43:08 -0700855 drmModePropertyPtr prop;
856 drmModePropertyBlobPtr blob_ptr;
Dominik Behr57b5c722018-08-08 18:52:31 -0700857 prop = drmModeGetProperty(drm->fd, console_connector->props[i]);
Dominik Behr83010f82016-03-18 18:43:08 -0700858 if (prop) {
859 if (strcmp(prop->name, "EDID") == 0) {
860 blob_ptr = drmModeGetPropertyBlob(drm->fd,
Dominik Behr57b5c722018-08-08 18:52:31 -0700861 console_connector->prop_values[i]);
Dominik Behr83010f82016-03-18 18:43:08 -0700862 if (blob_ptr) {
863 memcpy(&drm->edid, blob_ptr->data, EDID_SIZE);
864 drmModeFreePropertyBlob(blob_ptr);
Dominik Behr57b5c722018-08-08 18:52:31 -0700865 drmModeFreeConnector(console_connector);
Dominik Behr83010f82016-03-18 18:43:08 -0700866 return (drm->edid_found = true);
867 }
868 }
869 }
870 }
871
Dominik Behr57b5c722018-08-08 18:52:31 -0700872 drmModeFreeConnector(console_connector);
Dominik Behr83010f82016-03-18 18:43:08 -0700873 return false;
874}
875
876uint32_t drm_gethres(drm_t* drm)
877{
Dominik Behr57b5c722018-08-08 18:52:31 -0700878 return drm->console_mode_info.hdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -0700879}
880
881uint32_t drm_getvres(drm_t* drm)
882{
Dominik Behr57b5c722018-08-08 18:52:31 -0700883 return drm->console_mode_info.vdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -0700884}