blob: 4e55aa783626caaf25716d99b381fc5f8c95839d [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++) {
428 drm_t* drm = calloc(1, sizeof(drm_t));
429
430 if (!drm)
431 return NULL;
432
Dominik Behr815dc3d2016-04-19 16:59:39 -0700433try_open_again:
Dominik Behr83010f82016-03-18 18:43:08 -0700434 ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
435 if (ret < 0) {
436 drm_fini(drm);
437 continue;
438 }
Dominik Behr83010f82016-03-18 18:43:08 -0700439 drm->fd = open(dev_name, O_RDWR, 0);
440 free(dev_name);
441 if (drm->fd < 0) {
442 drm_fini(drm);
443 continue;
444 }
Dominik Behr815dc3d2016-04-19 16:59:39 -0700445 /* if we have master this should succeed */
446 ret = drmSetMaster(drm->fd);
447 if (ret != 0) {
448 drmClose(drm->fd);
449 drm->fd = -1;
450 usleep(100*1000);
451 goto try_open_again;
452 }
Dominik Behr83010f82016-03-18 18:43:08 -0700453
Dominik Behr57b5c722018-08-08 18:52:31 -0700454 /* Set universal planes cap if possible. Ignore any errors. */
455 drmSetClientCap(drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
456
Rob Clarkb95af602020-09-29 13:38:45 -0700457 /* Try to set atomic, to detect if it is supported: */
458 ret = drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1);
459 if (!ret) {
Dominik Behrbf08a552018-08-07 19:46:03 -0700460 drm->atomic = true;
Dominik Behrbf08a552018-08-07 19:46:03 -0700461 }
462
Dominik Behr83010f82016-03-18 18:43:08 -0700463 drm->resources = drmModeGetResources(drm->fd);
464 if (!drm->resources) {
465 drm_fini(drm);
466 continue;
467 }
468
Dominik Behrb1abcba2016-04-14 14:57:21 -0700469 /* Expect at least one crtc so we do not try to run on VGEM. */
Dominik Behr83010f82016-03-18 18:43:08 -0700470 if (drm->resources->count_crtcs == 0 || drm->resources->count_connectors == 0) {
471 drm_fini(drm);
472 continue;
473 }
474
Dominik Behr83010f82016-03-18 18:43:08 -0700475 drm->plane_resources = drmModeGetPlaneResources(drm->fd);
Dominik Behr57b5c722018-08-08 18:52:31 -0700476
477 if (!find_main_monitor(drm)) {
478 drm_fini(drm);
479 continue;
480 }
481
Dominik Behr83010f82016-03-18 18:43:08 -0700482 drm->refcount = 1;
483
484 if (drm_score(drm) > drm_score(best_drm)) {
485 drm_fini(best_drm);
486 best_drm = drm;
Dominik Behr45896022016-11-10 14:12:59 -0800487 } else {
488 drm_fini(drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700489 }
490 }
491
492 if (best_drm) {
493 drmVersionPtr version;
494 version = drmGetVersion(best_drm->fd);
495 if (version) {
496 LOG(INFO,
Dominik Behrbf08a552018-08-07 19:46:03 -0700497 "Frecon using drm driver %s, version %d.%d, date(%s), desc(%s)%s",
Dominik Behr83010f82016-03-18 18:43:08 -0700498 version->name,
499 version->version_major,
500 version->version_minor,
501 version->date,
Dominik Behrbf08a552018-08-07 19:46:03 -0700502 version->desc,
503 best_drm->atomic ? " using atomic" : "");
Dominik Behr83010f82016-03-18 18:43:08 -0700504 drmFreeVersion(version);
505 }
Dominik Behr83010f82016-03-18 18:43:08 -0700506 }
507
508 return best_drm;
509}
510
511void drm_set(drm_t* drm_)
512{
Dominik Behrda3c0102016-06-08 15:05:38 -0700513 if (g_drm) {
514 drm_delref(g_drm);
515 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700516 }
Dominik Behrda3c0102016-06-08 15:05:38 -0700517 g_drm = drm_;
Dominik Behr83010f82016-03-18 18:43:08 -0700518}
519
520void drm_close(void)
521{
Dominik Behrda3c0102016-06-08 15:05:38 -0700522 if (g_drm) {
523 drm_delref(g_drm);
524 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700525 }
526}
527
Dominik Behrb1abcba2016-04-14 14:57:21 -0700528void drm_delref(drm_t* drm)
Dominik Behr83010f82016-03-18 18:43:08 -0700529{
Dominik Behrd4d56272016-03-31 18:55:17 -0700530 if (!drm)
531 return;
Dominik Behr83010f82016-03-18 18:43:08 -0700532 if (drm->refcount) {
533 drm->refcount--;
534 } else {
535 LOG(ERROR, "Imbalanced drm_close()");
536 }
537 if (drm->refcount) {
538 return;
539 }
540
541 drm_fini(drm);
542}
543
544drm_t* drm_addref(void)
545{
Dominik Behrda3c0102016-06-08 15:05:38 -0700546 if (g_drm) {
547 g_drm->refcount++;
548 return g_drm;
Dominik Behr83010f82016-03-18 18:43:08 -0700549 }
550
551 return NULL;
552}
553
Dominik Behrda3c0102016-06-08 15:05:38 -0700554int drm_dropmaster(drm_t* drm)
Dominik Behrb1abcba2016-04-14 14:57:21 -0700555{
Dominik Behrda3c0102016-06-08 15:05:38 -0700556 int ret = 0;
557
558 if (!drm)
559 drm = g_drm;
560 if (drm)
561 ret = drmDropMaster(drm->fd);
562 return ret;
563}
564
565int drm_setmaster(drm_t* drm)
566{
567 int ret = 0;
568
569 if (!drm)
570 drm = g_drm;
571 if (drm)
572 ret = drmSetMaster(drm->fd);
573 return ret;
Dominik Behrb1abcba2016-04-14 14:57:21 -0700574}
575
Dominik Behr83010f82016-03-18 18:43:08 -0700576/*
Dominik Behrb1abcba2016-04-14 14:57:21 -0700577 * Returns true if connector/crtc/driver have changed and framebuffer object have to be re-created.
Dominik Behr83010f82016-03-18 18:43:08 -0700578 */
579bool drm_rescan(void)
580{
581 drm_t* ndrm;
582
Dominik Behrb1abcba2016-04-14 14:57:21 -0700583 /* In case we had master, drop master so the newly created object could have it. */
Dominik Behrda3c0102016-06-08 15:05:38 -0700584 drm_dropmaster(g_drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700585 ndrm = drm_scan();
586 if (ndrm) {
Dominik Behrda3c0102016-06-08 15:05:38 -0700587 if (drm_equal(ndrm, g_drm)) {
Dominik Behr83010f82016-03-18 18:43:08 -0700588 drm_fini(ndrm);
Dominik Behrda3c0102016-06-08 15:05:38 -0700589 /* Regain master we dropped. */
590 drm_setmaster(g_drm);
Dominik Behr83010f82016-03-18 18:43:08 -0700591 } else {
Dominik Behrda3c0102016-06-08 15:05:38 -0700592 drm_delref(g_drm);
593 g_drm = ndrm;
Dominik Behr83010f82016-03-18 18:43:08 -0700594 return true;
595 }
596 } else {
Dominik Behrda3c0102016-06-08 15:05:38 -0700597 if (g_drm) {
598 drm_delref(g_drm); /* No usable monitor/drm object. */
599 g_drm = NULL;
Dominik Behr83010f82016-03-18 18:43:08 -0700600 return true;
601 }
602 }
603 return false;
604}
605
606bool drm_valid(drm_t* drm) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700607 return drm && drm->fd >= 0 && drm->resources && drm->console_connector_id;
Dominik Behr83010f82016-03-18 18:43:08 -0700608}
609
Dominik Behrbf08a552018-08-07 19:46:03 -0700610static bool is_crtc_possible(drm_t* drm, uint32_t crtc_id, uint32_t mask)
611{
612 int32_t crtc;
613 for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++)
614 if (drm->resources->crtcs[crtc] == crtc_id)
615 return !!(mask & (1u << crtc));
616
617 return false;
618
619}
620
621#define CHECK(fn) do { ret = fn; if (ret < 0) goto error_mode; } while (0)
622static int32_t drm_setmode_atomic(drm_t* drm, uint32_t fb_id)
623{
624 int32_t ret;
625 int32_t crtc, conn;
626 uint32_t plane;
627 uint32_t console_crtc_id = 0;
628 drmModeObjectPropertiesPtr crtc_props = NULL;
629 drmModeObjectPropertiesPtr plane_props = NULL;
630 drmModeObjectPropertiesPtr conn_props = NULL;
631 drmModePlaneResPtr plane_resources;
632 drmModeAtomicReqPtr pset = NULL;
633 uint32_t mode_id = 0;
634
635 plane_resources = drmModeGetPlaneResources(drm->fd);
636 if (!plane_resources)
637 return -ENOENT;
638
639 get_connector_path(drm, drm->console_connector_id, NULL, &console_crtc_id);
640 if (!console_crtc_id)
641 find_crtc_for_connector(drm, drm->console_connector_id, &console_crtc_id);
642 if (!console_crtc_id) {
643 LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
644 return -ENOENT;
645 }
646
647 pset = drmModeAtomicAlloc();
648 if (!pset) {
649 ret = -ENOMEM;
650 goto error_mode;
651 }
652
653 for (crtc = 0; crtc < drm->resources->count_crtcs; crtc++) {
654 uint32_t crtc_id = drm->resources->crtcs[crtc];
655
656 crtc_props = drmModeObjectGetProperties(drm->fd, crtc_id, DRM_MODE_OBJECT_CRTC);
657
658 if (!crtc_props) {
659 LOG(ERROR, "Could not query properties for crtc %d %m.", crtc_id);
660 if (crtc_id != console_crtc_id)
661 continue;
662 ret = -ENOENT;
663 goto error_mode;
664 }
665
666 if (crtc_id == console_crtc_id) {
667 CHECK(drmModeCreatePropertyBlob(drm->fd, &drm->console_mode_info,
668 sizeof(drm->console_mode_info),
669 &mode_id));
670 /* drm->crtc->mode has been set during init */
671 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", mode_id));
672 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 1));
673 /* Reset color matrix to identity and gamma/degamma LUTs to pass through,
674 * ignore errors in case they are not supported. */
675 atomic_set_prop(drm, pset, crtc_id, crtc_props, "CTM", 0);
676 atomic_set_prop(drm, pset, crtc_id, crtc_props, "DEGAMMA_LUT", 0);
677 atomic_set_prop(drm, pset, crtc_id, crtc_props, "GAMMA_LUT", 0);
678 } else {
679 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "MODE_ID", 0));
680 CHECK(atomic_set_prop(drm, pset, crtc_id, crtc_props, "ACTIVE", 0));
681 }
682
683 drmModeFreeObjectProperties(crtc_props);
684 crtc_props = NULL;
685 }
686
687 for (plane = 0; plane < plane_resources->count_planes; plane++) {
688 drmModePlanePtr planeobj;
689 uint32_t plane_id = plane_resources->planes[plane];
690 uint32_t possible_crtcs;
691 int primary;
692
693 planeobj = drmModeGetPlane(drm->fd, plane_id);
694 if (!planeobj) {
695 LOG(ERROR, "Could not query plane object for plane %d %m.", plane_id);
696 ret = -ENOENT;
697 goto error_mode;
698 }
699
700 possible_crtcs = planeobj->possible_crtcs;
701 drmModeFreePlane(planeobj);
702
703 primary = drm_is_primary_plane(drm, plane_id);
704
705 plane_props = drmModeObjectGetProperties(drm->fd, plane_id, DRM_MODE_OBJECT_PLANE);
706 if (!plane_props) {
707 LOG(ERROR, "Could not query properties for plane %d %m.", plane_id);
708 ret = -ENOENT;
709 goto error_mode;
710 }
711
712 if (is_crtc_possible(drm, console_crtc_id, possible_crtcs) && primary) {
713 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", fb_id));
714 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", console_crtc_id));
715 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_X", 0));
716 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_Y", 0));
717 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_W", drm->console_mode_info.hdisplay));
718 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_H", drm->console_mode_info.vdisplay));
719 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_X", 0));
720 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_Y", 0));
721 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_W", drm->console_mode_info.hdisplay << 16));
722 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "SRC_H", drm->console_mode_info.vdisplay << 16));
723 } else {
724 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "FB_ID", 0));
725 CHECK(atomic_set_prop(drm, pset, plane_id, plane_props, "CRTC_ID", 0));
726 }
727
728 drmModeFreeObjectProperties(plane_props);
729 plane_props = NULL;
730 }
731
732 for (conn = 0; conn < drm->resources->count_connectors; conn++) {
733 uint32_t conn_id = drm->resources->connectors[conn];
734
735 conn_props = drmModeObjectGetProperties(drm->fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
736 if (!conn_props) {
737 LOG(ERROR, "Could not query properties for connector %d %m.", conn_id);
738 if (conn_id != drm->console_connector_id)
739 continue;
740 ret = -ENOENT;
741 goto error_mode;
742 }
743 if (conn_id == drm->console_connector_id)
744 CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", console_crtc_id));
745 else
746 CHECK(atomic_set_prop(drm, pset, conn_id, conn_props, "CRTC_ID", 0));
747 drmModeFreeObjectProperties(conn_props);
748 conn_props = NULL;
749 }
750
751 ret = drmModeAtomicCommit(drm->fd, pset,
752 DRM_MODE_ATOMIC_ALLOW_MODESET , NULL);
753 if (ret < 0) {
754 drm_clear_rmfb(drm);
755 /* LOG(INFO, "TIMING: Console switch atomic modeset finished."); */
756 } else {
757 ret = 0;
758 }
759
760error_mode:
761 if (mode_id)
762 drmModeDestroyPropertyBlob(drm->fd, mode_id);
763
764 if (plane_resources)
765 drmModeFreePlaneResources(plane_resources);
766
767 if (crtc_props)
768 drmModeFreeObjectProperties(crtc_props);
769
770 if (conn_props)
771 drmModeFreeObjectProperties(conn_props);
772
773 if (plane_props)
774 drmModeFreeObjectProperties(plane_props);
775
776 drmModeAtomicFree(pset);
777 return ret;
778}
779#undef CHECK
780
Dominik Behr83010f82016-03-18 18:43:08 -0700781int32_t drm_setmode(drm_t* drm, uint32_t fb_id)
782{
Dominik Behr57b5c722018-08-08 18:52:31 -0700783 int conn;
Dominik Behr83010f82016-03-18 18:43:08 -0700784 int32_t ret;
Dominik Behr57b5c722018-08-08 18:52:31 -0700785 uint32_t existing_console_crtc_id = 0;
Dominik Behr51a4da52016-07-28 14:18:48 -0700786
Dominik Behrbf08a552018-08-07 19:46:03 -0700787 if (drm->atomic)
788 if (drm_setmode_atomic(drm, fb_id) == 0)
789 return 0;
790 /* Fallback to legacy mode set. */
791
Dominik Behr57b5c722018-08-08 18:52:31 -0700792 get_connector_path(drm, drm->console_connector_id, NULL, &existing_console_crtc_id);
Dominik Behr51a4da52016-07-28 14:18:48 -0700793
Dominik Behr57b5c722018-08-08 18:52:31 -0700794 /* Loop through all the connectors, disable ones that are configured and set video mode on console connector. */
795 for (conn = 0; conn < drm->resources->count_connectors; conn++) {
796 uint32_t connector_id = drm->resources->connectors[conn];
Dominik Behr83010f82016-03-18 18:43:08 -0700797
Dominik Behr57b5c722018-08-08 18:52:31 -0700798 if (connector_id == drm->console_connector_id) {
799 uint32_t console_crtc_id = 0;
800
801 if (existing_console_crtc_id)
802 console_crtc_id = existing_console_crtc_id;
803 else {
804 find_crtc_for_connector(drm, connector_id, &console_crtc_id);
805
806 if (!console_crtc_id) {
807 LOG(ERROR, "Could not get console crtc for connector:%d in modeset.\n", drm->console_connector_id);
808 return -ENOENT;
809 }
810 }
811
812 ret = drmModeSetCrtc(drm->fd, console_crtc_id,
813 fb_id,
814 0, 0, // x,y
815 &drm->console_connector_id,
816 1, // connector_count
817 &drm->console_mode_info); // mode
818
819 if (ret) {
820 LOG(ERROR, "Unable to set crtc:%d connector:%d %m", console_crtc_id, drm->console_connector_id);
821 return ret;
822 }
823
824 ret = drmModeSetCursor(drm->fd, console_crtc_id,
825 0, 0, 0);
826
827 if (ret)
828 LOG(ERROR, "Unable to hide cursor on crtc:%d %m.", console_crtc_id);
829
830 drm_disable_non_primary_planes(drm, console_crtc_id);
831
832 } else {
833 uint32_t crtc_id = 0;
834
835 get_connector_path(drm, connector_id, NULL, &crtc_id);
836 if (!crtc_id)
837 /* This connector is not configured, skip. */
838 continue;
839
840 if (existing_console_crtc_id && existing_console_crtc_id == crtc_id)
841 /* This connector is mirroring from the same CRTC as console. It will be turned off when console is set. */
842 continue;
843
844 ret = drmModeSetCrtc(drm->fd, crtc_id, 0, // buffer_id
845 0, 0, // x,y
846 NULL, // connectors
847 0, // connector_count
848 NULL); // mode
849 if (ret)
850 LOG(ERROR, "Unable to disable crtc %d: %m", crtc_id);
851 }
Dominik Behr83010f82016-03-18 18:43:08 -0700852 }
853
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800854 drm_clear_rmfb(drm);
Dominik Behr57b5c722018-08-08 18:52:31 -0700855 /* LOG(INFO, "TIMING: Console switch modeset finished."); */
Dominik Behr83010f82016-03-18 18:43:08 -0700856 return ret;
857}
858
Dominik Behr6e0f6fd2016-12-02 17:54:08 -0800859/*
860 * Delayed rmfb(). We want to keep fb at least till after next modeset
861 * so our transitions are cleaner (e.g. when recreating term after exitin
862 * shell). Also it keeps fb around till Chrome starts.
863 */
864void drm_rmfb(drm_t* drm, uint32_t fb_id)
865{
866 drm_clear_rmfb(drm);
867 drm->delayed_rmfb_fb_id = fb_id;
868}
869
Dominik Behr83010f82016-03-18 18:43:08 -0700870bool drm_read_edid(drm_t* drm)
871{
Dominik Behr57b5c722018-08-08 18:52:31 -0700872 drmModeConnector* console_connector;
Dominik Behr83010f82016-03-18 18:43:08 -0700873 if (drm->edid_found) {
874 return true;
875 }
876
Dominik Behr57b5c722018-08-08 18:52:31 -0700877 console_connector = drmModeGetConnector(drm->fd, drm->console_connector_id);
878
879 if (!console_connector)
880 return false;
881
882 for (int i = 0; i < console_connector->count_props; i++) {
Dominik Behr83010f82016-03-18 18:43:08 -0700883 drmModePropertyPtr prop;
884 drmModePropertyBlobPtr blob_ptr;
Dominik Behr57b5c722018-08-08 18:52:31 -0700885 prop = drmModeGetProperty(drm->fd, console_connector->props[i]);
Dominik Behr83010f82016-03-18 18:43:08 -0700886 if (prop) {
887 if (strcmp(prop->name, "EDID") == 0) {
888 blob_ptr = drmModeGetPropertyBlob(drm->fd,
Dominik Behr57b5c722018-08-08 18:52:31 -0700889 console_connector->prop_values[i]);
Dominik Behr83010f82016-03-18 18:43:08 -0700890 if (blob_ptr) {
891 memcpy(&drm->edid, blob_ptr->data, EDID_SIZE);
892 drmModeFreePropertyBlob(blob_ptr);
Dominik Behr57b5c722018-08-08 18:52:31 -0700893 drmModeFreeConnector(console_connector);
Dominik Behr83010f82016-03-18 18:43:08 -0700894 return (drm->edid_found = true);
895 }
896 }
897 }
898 }
899
Dominik Behr57b5c722018-08-08 18:52:31 -0700900 drmModeFreeConnector(console_connector);
Dominik Behr83010f82016-03-18 18:43:08 -0700901 return false;
902}
903
904uint32_t drm_gethres(drm_t* drm)
905{
Dominik Behr57b5c722018-08-08 18:52:31 -0700906 return drm->console_mode_info.hdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -0700907}
908
909uint32_t drm_getvres(drm_t* drm)
910{
Dominik Behr57b5c722018-08-08 18:52:31 -0700911 return drm->console_mode_info.vdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -0700912}