swc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/swc
Log | Files | Refs | README | LICENSE

drm.c (13545B)


      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 <dirent.h>
     36 #include <errno.h>
     37 #include <limits.h>
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <strings.h>
     42 #include <fcntl.h>
     43 #include <unistd.h>
     44 #include <drm.h>
     45 #include <xf86drm.h>
     46 #include <wld/wld.h>
     47 #include <wld/drm.h>
     48 #include <wayland-server.h>
     49 #include "wayland-drm-server-protocol.h"
     50 
     51 struct swc_drm swc_drm;
     52 
     53 static 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 
     61 static void
     62 authenticate(struct wl_client *client, struct wl_resource *resource, uint32_t magic)
     63 {
     64 	wl_drm_send_authenticated(resource);
     65 }
     66 
     67 static void
     68 create_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id,
     69               uint32_t name, int32_t width, int32_t height, uint32_t stride, uint32_t format)
     70 {
     71 	wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_NAME, "GEM names are not supported, use a PRIME fd instead");
     72 }
     73 
     74 static void
     75 create_planar_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id,
     76                      uint32_t name, int32_t width, int32_t height, uint32_t format,
     77                      int32_t offset0, int32_t stride0,
     78                      int32_t offset1, int32_t stride1,
     79                      int32_t offset2, int32_t stride2)
     80 {
     81 	wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_FORMAT, "planar buffers are not supported\n");
     82 }
     83 
     84 static void
     85 create_prime_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id,
     86                     int32_t fd, int32_t width, int32_t height, uint32_t format,
     87                     int32_t offset0, int32_t stride0,
     88                     int32_t offset1, int32_t stride1,
     89                     int32_t offset2, int32_t stride2)
     90 {
     91 	struct wld_buffer *buffer;
     92 	struct wl_resource *buffer_resource;
     93 	union wld_object object = { .i = fd };
     94 
     95 	buffer = wld_import_buffer(swc.drm->context, WLD_DRM_OBJECT_PRIME_FD, object, width, height, format, stride0);
     96 	close(fd);
     97 
     98 	if (!buffer)
     99 		goto error0;
    100 
    101 	buffer_resource = wayland_buffer_create_resource(client, wl_resource_get_version(resource), id, buffer);
    102 
    103 	if (!buffer_resource)
    104 		goto error1;
    105 
    106 	return;
    107 
    108 error1:
    109 	wld_buffer_unreference(buffer);
    110 error0:
    111 	wl_resource_post_no_memory(resource);
    112 }
    113 
    114 static const struct wl_drm_interface drm_impl = {
    115 	.authenticate = authenticate,
    116 	.create_buffer = create_buffer,
    117 	.create_planar_buffer = create_planar_buffer,
    118 	.create_prime_buffer = create_prime_buffer,
    119 };
    120 
    121 static int
    122 select_card(const struct dirent *entry)
    123 {
    124 	unsigned num;
    125 	return sscanf(entry->d_name, "card%u", &num) == 1;
    126 }
    127 
    128 static bool
    129 find_primary_drm_device(char *path, size_t size)
    130 {
    131 	struct dirent **cards, *card = NULL;
    132 	int num_cards, ret, fd;
    133 	unsigned index;
    134 	FILE *file;
    135 	unsigned char boot_vga;
    136 
    137 	num_cards = scandir("/dev/dri", &cards, &select_card, &alphasort);
    138 
    139 	if (num_cards == -1)
    140 		return false;
    141 
    142 	for (index = 0; index < num_cards; ++index) {
    143 		snprintf(path, size, "/sys/class/drm/%s/device/boot_vga", cards[index]->d_name);
    144 
    145 		if ((file = fopen(path, "r"))) {
    146 			ret = fscanf(file, "%hhu", &boot_vga);
    147 			fclose(file);
    148 
    149 			if (ret == 1 && boot_vga) {
    150 				free(card);
    151 				card = cards[index];
    152 				DEBUG("/dev/dri/%s is the primary GPU\n", card->d_name);
    153 				break;
    154 			}
    155 		}
    156 
    157 		if (snprintf(path, size, "/dev/dri/%s", cards[index]->d_name) >= size)
    158 			return false;
    159 
    160 		fd = open(path, O_RDWR | O_CLOEXEC);
    161 		if (fd == -1)
    162 			continue;
    163 
    164 		/* If the card doesn't support universal planes, we can't use it.
    165 		 * On many ARM devices, the device that performs hardware accelerated
    166 		 * rendering is different from the device composites framebuffers.
    167 		 * The one that composites framebuffers should have universal planes
    168 		 * support (if I understand correctly) */
    169 		if (drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
    170 			close(fd);
    171 			continue;
    172 		}
    173 		close(fd);
    174 
    175 		if (!card)
    176 			card = cards[index];
    177 		else
    178 			free(cards[index]);
    179 	}
    180 
    181 	free(cards);
    182 
    183 	if (!card)
    184 		return false;
    185 
    186 	if (snprintf(path, size, "/dev/dri/%s", card->d_name) >= size)
    187 		return false;
    188 
    189 	free(card);
    190 	return true;
    191 }
    192 
    193 static int
    194 select_render_node(const struct dirent *entry)
    195 {
    196 	unsigned num;
    197 	return sscanf(entry->d_name, "renderD%u", &num) == 1;
    198 }
    199 
    200 static bool
    201 find_render_node(char *path, size_t size)
    202 {
    203 	struct dirent **nodes;
    204 	int num_nodes;
    205 	unsigned i;
    206 
    207 	num_nodes = scandir("/dev/dri", &nodes, &select_render_node, &alphasort);
    208 
    209 	if (num_nodes == -1)
    210 		return false;
    211 
    212 	if (snprintf(path, size, "/dev/dri/%s", nodes[0]->d_name) >= size)
    213 		return false;
    214 
    215 	for (i = 0; i < num_nodes; ++i) {
    216 		free(nodes[i]);
    217 	}
    218 
    219 	free(nodes);
    220 
    221 	return true;
    222 }
    223 
    224 static bool
    225 find_available_crtc(drmModeRes *resources, drmModeConnector *connector, uint32_t taken_crtcs, int *crtc_index)
    226 {
    227 	int i, j;
    228 	uint32_t possible_crtcs;
    229 	drmModeEncoder *encoder;
    230 
    231 	for (i = 0; i < connector->count_encoders; ++i) {
    232 		encoder = drmModeGetEncoder(swc.drm->fd, connector->encoders[i]);
    233 		possible_crtcs = encoder->possible_crtcs;
    234 		drmModeFreeEncoder(encoder);
    235 
    236 		for (j = 0; j < resources->count_crtcs; ++j) {
    237 			if ((possible_crtcs & (1 << j)) && !(taken_crtcs & (1 << j))) {
    238 				*crtc_index = j;
    239 				return true;
    240 			}
    241 		}
    242 	}
    243 
    244 	return false;
    245 }
    246 
    247 static void
    248 handle_vblank(int fd, unsigned int sequence, unsigned int sec, unsigned int usec, void *data)
    249 {
    250 }
    251 
    252 static void
    253 handle_page_flip(int fd, unsigned int sequence, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *data)
    254 {
    255 	struct drm_handler *handler = data;
    256 
    257 	handler->page_flip(handler, sec * 1000 + usec / 1000);
    258 }
    259 
    260 static drmEventContext event_context = {
    261 	.version = DRM_EVENT_CONTEXT_VERSION,
    262 	.vblank_handler = handle_vblank,
    263 	.page_flip_handler2 = handle_page_flip,
    264 };
    265 
    266 static int
    267 handle_data(int fd, uint32_t mask, void *data)
    268 {
    269 	drmHandleEvent(fd, &event_context);
    270 	return 1;
    271 }
    272 
    273 static void
    274 bind_drm(struct wl_client *client, void *data, uint32_t version, uint32_t id)
    275 {
    276 	struct wl_resource *resource;
    277 
    278 	resource = wl_resource_create(client, &wl_drm_interface, version, id);
    279 	if (!resource) {
    280 		wl_client_post_no_memory(client);
    281 		return;
    282 	}
    283 	wl_resource_set_implementation(resource, &drm_impl, NULL, NULL);
    284 
    285 	if (version >= 2)
    286 		wl_drm_send_capabilities(resource, WL_DRM_CAPABILITY_PRIME);
    287 
    288 	wl_drm_send_device(resource, drm.path);
    289 	wl_drm_send_format(resource, WL_DRM_FORMAT_XRGB8888);
    290 	wl_drm_send_format(resource, WL_DRM_FORMAT_ARGB8888);
    291 }
    292 
    293 bool
    294 drm_initialize(void)
    295 {
    296 	uint64_t val;
    297 	char primary[PATH_MAX];
    298 	char render[PATH_MAX];
    299 
    300 	if (!find_primary_drm_device(primary, sizeof(primary))) {
    301 		ERROR("Could not find valid DRM device\n");
    302 		goto error0;
    303 	}
    304 	fprintf(stderr, "primary device: %s\n", primary);
    305 
    306 	swc.drm->fd = launch_open_device(primary, O_RDWR | O_CLOEXEC);
    307 	if (swc.drm->fd == -1) {
    308 		ERROR("Could not open DRM device at %s\n", primary);
    309 		goto error0;
    310 	}
    311 	if (drmSetClientCap(swc.drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) < 0) {
    312 		ERROR("Could not enable DRM universal planes\n");
    313 		goto error1;
    314 	}
    315 	if (drmGetCap(swc.drm->fd, DRM_CAP_CURSOR_WIDTH, &val) < 0)
    316 		val = 64;
    317 	swc.drm->cursor_w = val;
    318 	if (drmGetCap(swc.drm->fd, DRM_CAP_CURSOR_HEIGHT, &val) < 0)
    319 		val = 64;
    320 	swc.drm->cursor_h = val;
    321 
    322 	drm.path = drmGetRenderDeviceNameFromFd(swc.drm->fd);
    323 	if (!drm.path) {
    324 		if (!find_render_node(render, sizeof(render))) {
    325 			ERROR("Could not find default render node\n");
    326 			goto error1;
    327 		}
    328 		if (!(drm.path = strdup(render))) {
    329 			ERROR("Couldn't copy render node path\n");
    330 			goto error1;
    331 		}
    332 	}
    333 
    334 	if (!(swc.drm->context = wld_drm_create_context(swc.drm->fd))) {
    335 		ERROR("Could not create WLD DRM context\n");
    336 		goto error1;
    337 	}
    338 
    339 	if (!(swc.drm->renderer = wld_create_renderer(swc.drm->context))) {
    340 		ERROR("Could not create WLD DRM renderer\n");
    341 		goto error2;
    342 	}
    343 
    344 	drm.event_source = wl_event_loop_add_fd(swc.event_loop, swc.drm->fd, WL_EVENT_READABLE, &handle_data, NULL);
    345 
    346 	if (!drm.event_source) {
    347 		ERROR("Could not create DRM event source\n");
    348 		goto error3;
    349 	}
    350 
    351 	if (!wld_drm_is_dumb(swc.drm->context)) {
    352 		drm.global = wl_global_create(swc.display, &wl_drm_interface, 2, NULL, &bind_drm);
    353 		if (!drm.global) {
    354 			ERROR("Could not create wl_drm global\n");
    355 			goto error4;
    356 		}
    357 
    358 		drm.dmabuf = swc_dmabuf_create(swc.display);
    359 		if (!drm.dmabuf) {
    360 			WARNING("Could not create wp_linux_dmabuf global\n");
    361 		}
    362 	}
    363 
    364 	return true;
    365 
    366 error4:
    367 	wl_event_source_remove(drm.event_source);
    368 error3:
    369 	wld_destroy_renderer(swc.drm->renderer);
    370 error2:
    371 	wld_destroy_context(swc.drm->context);
    372 error1:
    373 	close(swc.drm->fd);
    374 error0:
    375 	return false;
    376 }
    377 
    378 void
    379 drm_finalize(void)
    380 {
    381 	if (drm.global)
    382 		wl_global_destroy(drm.global);
    383 	wl_event_source_remove(drm.event_source);
    384 	wld_destroy_renderer(swc.drm->renderer);
    385 	wld_destroy_context(swc.drm->context);
    386 	free(drm.path);
    387 	close(swc.drm->fd);
    388 }
    389 
    390 bool
    391 drm_create_screens(struct wl_list *screens)
    392 {
    393 	drmModePlaneRes *plane_ids;
    394 	drmModeRes *resources;
    395 	drmModeConnector *connector;
    396 	struct plane *plane, *cursor_plane;
    397 	struct output *output;
    398 	uint32_t i, taken_crtcs = 0;
    399 	struct wl_list planes;
    400 
    401 	plane_ids = drmModeGetPlaneResources(swc.drm->fd);
    402 	if (!plane_ids) {
    403 		ERROR("Could not get DRM plane resources\n");
    404 		return false;
    405 	}
    406 	wl_list_init(&planes);
    407 	for (i = 0; i < plane_ids->count_planes; ++i) {
    408 		plane = plane_new(plane_ids->planes[i]);
    409 		if (plane)
    410 			wl_list_insert(&planes, &plane->link);
    411 	}
    412 	drmModeFreePlaneResources(plane_ids);
    413 
    414 	resources = drmModeGetResources(swc.drm->fd);
    415 	if (!resources) {
    416 		ERROR("Could not get DRM resources\n");
    417 		return false;
    418 	}
    419 	for (i = 0; i < resources->count_connectors; ++i, drmModeFreeConnector(connector)) {
    420 		connector = drmModeGetConnector(swc.drm->fd, resources->connectors[i]);
    421 
    422 		if (connector->connection == DRM_MODE_CONNECTED) {
    423 			int crtc_index;
    424 
    425 			if (!find_available_crtc(resources, connector, taken_crtcs, &crtc_index)) {
    426 				WARNING("Could not find CRTC for connector %d\n", i);
    427 				continue;
    428 			}
    429 
    430 			cursor_plane = NULL;
    431 			wl_list_for_each (plane, &planes, link) {
    432 				if (plane->type == DRM_PLANE_TYPE_CURSOR && plane->possible_crtcs & 1 << crtc_index) {
    433 					wl_list_remove(&plane->link);
    434 					cursor_plane = plane;
    435 					break;
    436 				}
    437 			}
    438 			if (!cursor_plane) {
    439 				WARNING("Could not find cursor plane for CRTC %d\n", crtc_index);
    440 			}
    441 
    442 			if (!(output = output_new(connector)))
    443 				continue;
    444 
    445 			output->screen = screen_new(resources->crtcs[crtc_index], output, cursor_plane);
    446 			output->screen->id = crtc_index;
    447 			taken_crtcs |= 1 << crtc_index;
    448 
    449 			wl_list_insert(screens, &output->screen->link);
    450 		}
    451 	}
    452 	drmModeFreeResources(resources);
    453 
    454 	return true;
    455 }
    456 
    457 enum {
    458 	WLD_USER_OBJECT_FRAMEBUFFER = WLD_USER_ID
    459 };
    460 
    461 struct framebuffer {
    462 	struct wld_exporter exporter;
    463 	struct wld_destructor destructor;
    464 	uint32_t id;
    465 };
    466 
    467 static bool
    468 framebuffer_export(struct wld_exporter *exporter, struct wld_buffer *buffer, uint32_t type, union wld_object *object)
    469 {
    470 	struct framebuffer *framebuffer = wl_container_of(exporter, framebuffer, exporter);
    471 
    472 	switch (type) {
    473 	case WLD_USER_OBJECT_FRAMEBUFFER:
    474 		object->u32 = framebuffer->id;
    475 		break;
    476 	default:
    477 		return false;
    478 	}
    479 
    480 	return true;
    481 }
    482 
    483 static void
    484 framebuffer_destroy(struct wld_destructor *destructor)
    485 {
    486 	struct framebuffer *framebuffer = wl_container_of(destructor, framebuffer, destructor);
    487 
    488 	drmModeRmFB(swc.drm->fd, framebuffer->id);
    489 	free(framebuffer);
    490 }
    491 
    492 uint32_t
    493 drm_get_framebuffer(struct wld_buffer *buffer)
    494 {
    495 	struct framebuffer *framebuffer;
    496 	union wld_object object;
    497 	int ret;
    498 
    499 	if (!buffer)
    500 		return 0;
    501 
    502 	if (wld_export(buffer, WLD_USER_OBJECT_FRAMEBUFFER, &object))
    503 		return object.u32;
    504 
    505 	if (!wld_export(buffer, WLD_DRM_OBJECT_HANDLE, &object)) {
    506 		ERROR("Could not get buffer handle\n");
    507 		return 0;
    508 	}
    509 
    510 	if (!(framebuffer = malloc(sizeof(*framebuffer))))
    511 		return 0;
    512 
    513 	ret = drmModeAddFB2(swc.drm->fd, buffer->width, buffer->height, buffer->format,
    514 	                    (uint32_t[4]){object.u32}, (uint32_t[4]){buffer->pitch}, (uint32_t[4]){0},
    515 	                    &framebuffer->id, 0);
    516 	if (ret < 0) {
    517 		free(framebuffer);
    518 		return 0;
    519 	}
    520 
    521 	framebuffer->exporter.export = &framebuffer_export;
    522 	wld_buffer_add_exporter(buffer, &framebuffer->exporter);
    523 	framebuffer->destructor.destroy = &framebuffer_destroy;
    524 	wld_buffer_add_destructor(buffer, &framebuffer->destructor);
    525 
    526 	return framebuffer->id;
    527 }