1/* swc: drm.c
2 *
3 * Copyright (c) 2013-2020 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "drm.h"
25#include "dmabuf.h"
26#include "event.h"
27#include "internal.h"
28#include "launch.h"
29#include "output.h"
30#include "plane.h"
31#include "screen.h"
32#include "util.h"
33#include "wayland_buffer.h"
34
35#include "wayland-drm-server-protocol.h"
36#include <dirent.h>
37#include <drm.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <limits.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <strings.h>
45#include <unistd.h>
46#include <wayland-server.h>
47#include <wld/drm.h>
48#include <wld/wld.h>
49#include <xf86drm.h>
50
51struct swc_drm swc_drm;
52
53static struct {
54 char *path;
55
56 struct wl_global *global;
57 struct wl_global *dmabuf;
58 struct wl_event_source *event_source;
59} drm;
60
61static void
62authenticate(struct wl_client *client, struct wl_resource *resource,
63 uint32_t magic)
64{
65 wl_drm_send_authenticated(resource);
66}
67
68static void
69create_buffer(struct wl_client *client, struct wl_resource *resource,
70 uint32_t id, uint32_t name, int32_t width, int32_t height,
71 uint32_t stride, uint32_t format)
72{
73 wl_resource_post_error(
74 resource, WL_DRM_ERROR_INVALID_NAME,
75 "GEM names are not supported, use a PRIME fd instead");
76}
77
78static void
79create_planar_buffer(struct wl_client *client, struct wl_resource *resource,
80 uint32_t id, uint32_t name, int32_t width, int32_t height,
81 uint32_t format, int32_t offset0, int32_t stride0,
82 int32_t offset1, int32_t stride1, int32_t offset2,
83 int32_t stride2)
84{
85 wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_FORMAT,
86 "planar buffers are not supported\n");
87}
88
89static void
90create_prime_buffer(struct wl_client *client, struct wl_resource *resource,
91 uint32_t id, int32_t fd, int32_t width, int32_t height,
92 uint32_t format, int32_t offset0, int32_t stride0,
93 int32_t offset1, int32_t stride1, int32_t offset2,
94 int32_t stride2)
95{
96 struct wld_buffer *buffer;
97 struct wl_resource *buffer_resource;
98 union wld_object object = {.i = fd};
99
100 buffer = wld_import_buffer(swc.drm->context, WLD_DRM_OBJECT_PRIME_FD,
101 object, width, height, format, stride0);
102 close(fd);
103
104 if (!buffer) {
105 goto error0;
106 }
107
108 buffer_resource = wayland_buffer_create_resource(
109 client, wl_resource_get_version(resource), id, buffer);
110
111 if (!buffer_resource) {
112 goto error1;
113 }
114
115 return;
116
117error1:
118 wld_buffer_unreference(buffer);
119error0:
120 wl_resource_post_no_memory(resource);
121}
122
123static const struct wl_drm_interface drm_impl = {
124 .authenticate = authenticate,
125 .create_buffer = create_buffer,
126 .create_planar_buffer = create_planar_buffer,
127 .create_prime_buffer = create_prime_buffer,
128};
129
130static int
131select_card(const struct dirent *entry)
132{
133 unsigned num;
134 return sscanf(entry->d_name, "card%u", &num) == 1;
135}
136
137static bool
138find_primary_drm_device(char *path, size_t size)
139{
140 struct dirent **cards, *card = NULL;
141 int num_cards, ret;
142 unsigned index;
143 FILE *file;
144 unsigned char boot_vga;
145
146 num_cards = scandir("/dev/dri", &cards, &select_card, &alphasort);
147
148 if (num_cards == -1) {
149 return false;
150 }
151
152 for (index = 0; index < num_cards; ++index) {
153 snprintf(path, size, "/sys/class/drm/%s/device/boot_vga",
154 cards[index]->d_name);
155
156 if ((file = fopen(path, "r"))) {
157 ret = fscanf(file, "%hhu", &boot_vga);
158 fclose(file);
159
160 if (ret == 1 && boot_vga) {
161 free(card);
162 card = cards[index];
163 DEBUG("/dev/dri/%s is the primary GPU\n", card->d_name);
164 break;
165 }
166 }
167
168 if (!card) {
169 card = cards[index];
170 } else {
171 free(cards[index]);
172 }
173 }
174
175 free(cards);
176
177 if (!card) {
178 return false;
179 }
180
181 if (snprintf(path, size, "/dev/dri/%s", card->d_name) >= size) {
182 return false;
183 }
184
185 free(card);
186 return true;
187}
188
189static bool
190find_available_crtc(drmModeRes *resources, drmModeConnector *connector,
191 uint32_t taken_crtcs, int *crtc_index)
192{
193 int i, j;
194 uint32_t possible_crtcs;
195 drmModeEncoder *encoder;
196
197 for (i = 0; i < connector->count_encoders; ++i) {
198 encoder = drmModeGetEncoder(swc.drm->fd, connector->encoders[i]);
199 if (!encoder) {
200 continue;
201 }
202 possible_crtcs = encoder->possible_crtcs;
203 drmModeFreeEncoder(encoder);
204
205 for (j = 0; j < resources->count_crtcs; ++j) {
206 if ((possible_crtcs & (1 << j)) && !(taken_crtcs & (1 << j))) {
207 *crtc_index = j;
208 return true;
209 }
210 }
211 }
212
213 return false;
214}
215
216static void
217handle_vblank(int fd, unsigned int sequence, unsigned int sec,
218 unsigned int usec, void *data)
219{
220}
221
222static void
223handle_page_flip(int fd, unsigned int sequence, unsigned int sec,
224 unsigned int usec, unsigned int crtc_id, void *data)
225{
226 struct drm_handler *handler = data;
227
228 handler->page_flip(handler, sec * 1000 + usec / 1000);
229}
230
231static drmEventContext event_context = {
232 .version = DRM_EVENT_CONTEXT_VERSION,
233 .vblank_handler = handle_vblank,
234 .page_flip_handler2 = handle_page_flip,
235};
236
237static int
238handle_data(int fd, uint32_t mask, void *data)
239{
240 drmHandleEvent(fd, &event_context);
241 return 1;
242}
243
244static void
245bind_drm(struct wl_client *client, void *data, uint32_t version, uint32_t id)
246{
247 struct wl_resource *resource;
248
249 resource = wl_resource_create(client, &wl_drm_interface, version, id);
250 if (!resource) {
251 wl_client_post_no_memory(client);
252 return;
253 }
254 wl_resource_set_implementation(resource, &drm_impl, NULL, NULL);
255
256 if (version >= 2) {
257 wl_drm_send_capabilities(resource, WL_DRM_CAPABILITY_PRIME);
258 }
259
260 wl_drm_send_device(resource, drm.path);
261 wl_drm_send_format(resource, WL_DRM_FORMAT_XRGB8888);
262 wl_drm_send_format(resource, WL_DRM_FORMAT_ARGB8888);
263}
264
265bool
266drm_initialize(void)
267{
268 uint64_t val;
269 char primary[PATH_MAX];
270
271 if (!find_primary_drm_device(primary, sizeof(primary))) {
272 ERROR("Could not find DRM device\n");
273 goto error0;
274 }
275
276 swc.drm->fd = launch_open_device(primary, O_RDWR | O_CLOEXEC);
277 if (swc.drm->fd == -1) {
278 ERROR("Could not open DRM device at %s\n", primary);
279 goto error0;
280 }
281 if (drmSetClientCap(swc.drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
282 ERROR("Could not enable DRM universal planes\n");
283 goto error1;
284 }
285 if (drmGetCap(swc.drm->fd, DRM_CAP_CURSOR_WIDTH, &val) < 0) {
286 val = 64;
287 }
288 swc.drm->cursor_w = val;
289 if (drmGetCap(swc.drm->fd, DRM_CAP_CURSOR_HEIGHT, &val) < 0) {
290 val = 64;
291 }
292 swc.drm->cursor_h = val;
293
294 drm.path = drmGetRenderDeviceNameFromFd(swc.drm->fd);
295 if (!drm.path) {
296 ERROR("Could not determine render node path\n");
297 goto error1;
298 }
299
300 if (!(swc.drm->context = wld_drm_create_context(swc.drm->fd))) {
301 ERROR("Could not create WLD DRM context\n");
302 goto error1;
303 }
304
305 if (!(swc.drm->renderer = wld_create_renderer(swc.drm->context))) {
306 ERROR("Could not create WLD DRM renderer\n");
307 goto error2;
308 }
309
310 drm.event_source = wl_event_loop_add_fd(
311 swc.event_loop, swc.drm->fd, WL_EVENT_READABLE, &handle_data, NULL);
312
313 if (!drm.event_source) {
314 ERROR("Could not create DRM event source\n");
315 goto error3;
316 }
317
318 if (!wld_drm_is_dumb(swc.drm->context)) {
319 drm.global = wl_global_create(swc.display, &wl_drm_interface, 2, NULL,
320 &bind_drm);
321 if (!drm.global) {
322 ERROR("Could not create wl_drm global\n");
323 goto error4;
324 }
325
326 drm.dmabuf = swc_dmabuf_create(swc.display);
327 if (!drm.dmabuf) {
328 WARNING("Could not create wp_linux_dmabuf global\n");
329 }
330 }
331
332 return true;
333
334error4:
335 wl_event_source_remove(drm.event_source);
336error3:
337 wld_destroy_renderer(swc.drm->renderer);
338error2:
339 wld_destroy_context(swc.drm->context);
340error1:
341 close(swc.drm->fd);
342error0:
343 return false;
344}
345
346void
347drm_finalize(void)
348{
349 if (drm.global) {
350 wl_global_destroy(drm.global);
351 }
352 wl_event_source_remove(drm.event_source);
353 wld_destroy_renderer(swc.drm->renderer);
354 wld_destroy_context(swc.drm->context);
355 free(drm.path);
356 close(swc.drm->fd);
357}
358
359bool
360drm_create_screens(struct wl_list *screens)
361{
362 drmModePlaneRes *plane_ids;
363 drmModeRes *resources;
364 drmModeConnector *connector;
365 struct plane *plane, *cursor_plane;
366 struct output *output;
367 uint32_t i, taken_crtcs = 0;
368 struct wl_list planes;
369
370 plane_ids = drmModeGetPlaneResources(swc.drm->fd);
371 if (!plane_ids) {
372 ERROR("Could not get DRM plane resources\n");
373 return false;
374 }
375 wl_list_init(&planes);
376 for (i = 0; i < plane_ids->count_planes; ++i) {
377 plane = plane_new(plane_ids->planes[i]);
378 if (plane) {
379 wl_list_insert(&planes, &plane->link);
380 }
381 }
382 drmModeFreePlaneResources(plane_ids);
383
384 resources = drmModeGetResources(swc.drm->fd);
385 if (!resources) {
386 struct plane *p, *ptmp;
387
388 wl_list_for_each_safe(p, ptmp, &planes, link)
389 plane_destroy(p);
390 ERROR("Could not get DRM resources\n");
391 return false;
392 }
393 for (i = 0; i < resources->count_connectors;
394 ++i, drmModeFreeConnector(connector)) {
395 connector = drmModeGetConnector(swc.drm->fd, resources->connectors[i]);
396
397 if (connector->connection == DRM_MODE_CONNECTED) {
398 int crtc_index;
399
400 if (!find_available_crtc(resources, connector, taken_crtcs,
401 &crtc_index)) {
402 WARNING("Could not find CRTC for connector %d\n", i);
403 continue;
404 }
405
406 cursor_plane = NULL;
407 wl_list_for_each(plane, &planes, link)
408 {
409 if (plane->type == DRM_PLANE_TYPE_CURSOR &&
410 plane->possible_crtcs & 1 << crtc_index) {
411 wl_list_remove(&plane->link);
412 cursor_plane = plane;
413 break;
414 }
415 }
416 if (!cursor_plane) {
417 WARNING("Could not find cursor plane for CRTC %d\n",
418 crtc_index);
419 }
420
421 if (!(output = output_new(connector))) {
422 continue;
423 }
424
425 output->screen =
426 screen_new(resources->crtcs[crtc_index], output, cursor_plane);
427 output->screen->id = crtc_index;
428 taken_crtcs |= 1 << crtc_index;
429
430 wl_list_insert(screens, &output->screen->link);
431 }
432 }
433 drmModeFreeResources(resources);
434
435 return true;
436}
437
438enum { WLD_USER_OBJECT_FRAMEBUFFER = WLD_USER_ID };
439
440struct framebuffer {
441 struct wld_exporter exporter;
442 struct wld_destructor destructor;
443 uint32_t id;
444};
445
446static bool
447framebuffer_export(struct wld_exporter *exporter, struct wld_buffer *buffer,
448 uint32_t type, union wld_object *object)
449{
450 struct framebuffer *framebuffer =
451 wl_container_of(exporter, framebuffer, exporter);
452
453 switch (type) {
454 case WLD_USER_OBJECT_FRAMEBUFFER:
455 object->u32 = framebuffer->id;
456 break;
457 default:
458 return false;
459 }
460
461 return true;
462}
463
464static void
465framebuffer_destroy(struct wld_destructor *destructor)
466{
467 struct framebuffer *framebuffer =
468 wl_container_of(destructor, framebuffer, destructor);
469
470 drmModeRmFB(swc.drm->fd, framebuffer->id);
471 free(framebuffer);
472}
473
474uint32_t
475drm_get_framebuffer(struct wld_buffer *buffer)
476{
477 struct framebuffer *framebuffer;
478 union wld_object object;
479 int ret;
480
481 if (!buffer) {
482 return 0;
483 }
484
485 if (wld_export(buffer, WLD_USER_OBJECT_FRAMEBUFFER, &object)) {
486 return object.u32;
487 }
488
489 if (!wld_export(buffer, WLD_DRM_OBJECT_HANDLE, &object)) {
490 ERROR("Could not get buffer handle\n");
491 return 0;
492 }
493
494 if (!(framebuffer = malloc(sizeof(*framebuffer)))) {
495 return 0;
496 }
497
498 ret = drmModeAddFB2(swc.drm->fd, buffer->width, buffer->height,
499 buffer->format, (uint32_t[4]){object.u32},
500 (uint32_t[4]){buffer->pitch}, (uint32_t[4]){0},
501 &framebuffer->id, 0);
502 if (ret < 0) {
503 free(framebuffer);
504 return 0;
505 }
506
507 framebuffer->exporter.export = &framebuffer_export;
508 wld_buffer_add_exporter(buffer, &framebuffer->exporter);
509 framebuffer->destructor.destroy = &framebuffer_destroy;
510 wld_buffer_add_destructor(buffer, &framebuffer->destructor);
511
512 return framebuffer->id;
513}