Skip to content
Commits on Source (7)
......@@ -2912,6 +2912,7 @@ load_drm_backend(struct weston_compositor *c, int *argc, char **argv,
const struct weston_option options[] = {
{ WESTON_OPTION_STRING, "seat", 0, &config.seat_id },
{ WESTON_OPTION_STRING, "drm-device", 0, &config.specific_device },
{ WESTON_OPTION_STRING, "additional-devices", 0, &config.additional_devices},
{ WESTON_OPTION_BOOLEAN, "current-mode", 0, &wet->drm_use_current_mode },
{ WESTON_OPTION_BOOLEAN, "use-pixman", 0, &force_pixman },
{ WESTON_OPTION_BOOLEAN, "continue-without-input", false, &without_input }
......
......@@ -250,6 +250,14 @@ struct weston_drm_backend_config {
/** Use shadow buffer if using Pixman-renderer. */
bool use_pixman_shadow;
/** Additional DRM devices to open
*
* A comma-separated list of DRM devices names, like "card1", to open.
* The devices will be used as additional scanout devices, but not as a
* rendering device.
*/
char *additional_devices;
};
#ifdef __cplusplus
......
......@@ -123,6 +123,9 @@ static void drm_output_fini_cursor_egl(struct drm_output *output)
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
/* This cursor does not have a GBM device */
if (output->gbm_cursor_fb[i] && !output->gbm_cursor_fb[i]->bo)
output->gbm_cursor_fb[i]->type = BUFFER_PIXMAN_DUMB;
drm_fb_unref(output->gbm_cursor_fb[i]);
output->gbm_cursor_fb[i] = NULL;
}
......@@ -141,19 +144,31 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
struct gbm_bo *bo;
bo = gbm_bo_create(b->gbm, device->cursor_width, device->cursor_height,
GBM_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
if (!bo)
goto err;
output->gbm_cursor_fb[i] =
drm_fb_get_from_bo(bo, device, false, BUFFER_CURSOR);
if (!output->gbm_cursor_fb[i]) {
gbm_bo_destroy(bo);
goto err;
if (gbm_device_get_fd(b->gbm) != output->device->drm.fd) {
output->gbm_cursor_fb[i] =
drm_fb_create_dumb(output->device,
device->cursor_width,
device->cursor_height,
DRM_FORMAT_ARGB8888);
/* Override buffer type, since we know it is a cursor */
output->gbm_cursor_fb[i]->type = BUFFER_CURSOR;
output->gbm_cursor_handle[i] =
output->gbm_cursor_fb[i]->handles[0];
} else {
bo = gbm_bo_create(b->gbm, device->cursor_width, device->cursor_height,
GBM_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
if (!bo)
goto err;
output->gbm_cursor_fb[i] =
drm_fb_get_from_bo(bo, device, false, BUFFER_CURSOR);
if (!output->gbm_cursor_fb[i]) {
gbm_bo_destroy(bo);
goto err;
}
output->gbm_cursor_handle[i] = gbm_bo_get_handle(bo).s32;
}
output->gbm_cursor_handle[i] = gbm_bo_get_handle(bo).s32;
}
return 0;
......@@ -194,6 +209,15 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
}
#endif
/*
* If we cannot use modifiers to allocate the GBM surface and the GBM
* device differs from the KMS display device (because we are rendering
* on a different GPU), we have to use linear buffers to make sure that
* the allocated GBM surface is correctly displayed on the KMS device.
*/
if (gbm_device_get_fd(gbm) != output->device->drm.fd)
output->gbm_bo_flags |= GBM_BO_USE_LINEAR;
/* We may allocate with no modifiers in the following situations:
*
* 1. old GBM version, so HAVE_GBM_MODIFIERS is false;
......
......@@ -300,6 +300,11 @@ struct drm_device {
dev_t devnum;
} drm;
/* Track the GEM handles if the device does not have a gbm device, which
* tracks the handles for us.
*/
struct hash_table *gem_handle_refcnt;
/* drm_crtc::link */
struct wl_list crtc_list;
......@@ -332,6 +337,9 @@ struct drm_device {
*/
int min_width, max_width;
int min_height, max_height;
/* drm_backend::kms_list */
struct wl_list link;
};
struct drm_backend {
......@@ -345,6 +353,8 @@ struct drm_backend {
struct wl_event_source *udev_drm_source;
struct drm_device *drm;
/* drm_device::link */
struct wl_list kms_list;
struct gbm_device *gbm;
struct wl_listener session_listener;
const struct pixel_format_info *format;
......@@ -378,6 +388,8 @@ enum drm_fb_type {
struct drm_fb {
enum drm_fb_type type;
struct drm_device *scanout_device;
int refcnt;
uint32_t fb_id, size;
......@@ -403,6 +415,12 @@ struct drm_fb {
struct drm_buffer_fb {
struct drm_fb *fb;
enum try_view_on_plane_failure_reasons failure_reasons;
struct drm_device *device;
struct wl_list link;
};
struct drm_fb_private {
struct wl_list buffer_fb_list;
struct wl_listener buffer_destroy_listener;
};
......
......@@ -51,6 +51,7 @@
#include <libweston/backend-drm.h>
#include <libweston/weston-log.h>
#include "drm-internal.h"
#include "shared/hash.h"
#include "shared/helpers.h"
#include "shared/timespec-util.h"
#include "shared/string-helpers.h"
......@@ -70,9 +71,9 @@
static const char default_seat[] = "seat0";
static void
drm_backend_create_faked_zpos(struct drm_backend *b)
drm_backend_create_faked_zpos(struct drm_device *device)
{
struct drm_device *device = b->drm;
struct drm_backend *b = device->backend;
struct drm_plane *plane;
uint64_t zpos = 0ULL;
uint64_t zpos_min_primary;
......@@ -629,9 +630,10 @@ static void
drm_repaint_begin(struct weston_backend *backend)
{
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device = b->drm;
struct drm_device *device;
struct drm_pending_state *pending_state;
device = b->drm;
pending_state = drm_pending_state_alloc(device);
device->repaint_data = pending_state;
......@@ -642,6 +644,19 @@ drm_repaint_begin(struct weston_backend *backend)
drm_debug(b, "%s", dbg);
free(dbg);
}
wl_list_for_each(device, &b->kms_list, link) {
pending_state = drm_pending_state_alloc(device);
device->repaint_data = pending_state;
if (weston_log_scope_is_enabled(b->debug)) {
char *dbg = weston_compositor_print_scene_graph(b->compositor);
drm_debug(b, "[repaint] Beginning repaint; pending_state %p\n",
pending_state);
drm_debug(b, "%s", dbg);
free(dbg);
}
}
}
/**
......@@ -657,10 +672,12 @@ static int
drm_repaint_flush(struct weston_backend *backend)
{
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device = b->drm;
struct drm_pending_state *pending_state = device->repaint_data;
struct drm_device *device;
struct drm_pending_state *pending_state;
int ret;
device = b->drm;
pending_state = device->repaint_data;
ret = drm_pending_state_apply(pending_state);
if (ret != 0)
weston_log("repaint-flush failed: %s\n", strerror(errno));
......@@ -668,6 +685,16 @@ drm_repaint_flush(struct weston_backend *backend)
drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state);
device->repaint_data = NULL;
wl_list_for_each(device, &b->kms_list, link) {
pending_state = device->repaint_data;
ret = drm_pending_state_apply(pending_state);
if (ret != 0)
weston_log("repaint-flush failed: %s\n", strerror(errno));
drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state);
device->repaint_data = NULL;
}
return (ret == -EACCES || ret == -EBUSY) ? ret : 0;
}
......@@ -681,12 +708,21 @@ static void
drm_repaint_cancel(struct weston_backend *backend)
{
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device = b->drm;
struct drm_pending_state *pending_state = device->repaint_data;
struct drm_device *device;
struct drm_pending_state *pending_state;
device = b->drm;
pending_state = device->repaint_data;
drm_pending_state_free(pending_state);
drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state);
device->repaint_data = NULL;
wl_list_for_each(device, &b->kms_list, link) {
pending_state = device->repaint_data;
drm_pending_state_free(pending_state);
drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state);
device->repaint_data = NULL;
}
}
static int
......@@ -2329,6 +2365,26 @@ drm_head_destroy(struct weston_head *base)
free(head);
}
static struct drm_device *
drm_device_find_by_output(struct weston_compositor *compositor, const char *name)
{
struct drm_device *device = NULL;
struct weston_head *base = NULL;
struct drm_head *head;
const char *tmp;
while ((base = weston_compositor_iterate_heads(compositor, base))) {
tmp = weston_head_get_name(base);
if (strcmp(name, tmp) != 0)
continue;
head = to_drm_head(base);
device = head->connector.device;
break;
}
return device;
}
/**
* Create a Weston output structure
*
......@@ -2346,9 +2402,13 @@ static struct weston_output *
drm_output_create(struct weston_backend *backend, const char *name)
{
struct drm_backend *b = container_of(backend, struct drm_backend, base);
struct drm_device *device = b->drm;
struct drm_device *device;
struct drm_output *output;
device = drm_device_find_by_output(b->compositor, name);
if (!device)
return NULL;
output = zalloc(sizeof *output);
if (output == NULL)
return NULL;
......@@ -2687,6 +2747,7 @@ udev_drm_event(int fd, uint32_t mask, void *data)
struct drm_backend *b = data;
struct udev_device *event;
uint32_t conn_id, prop_id;
struct drm_device *device;
event = udev_monitor_receive_device(b->udev_monitor);
......@@ -2697,6 +2758,15 @@ udev_drm_event(int fd, uint32_t mask, void *data)
drm_backend_update_connectors(b->drm, event);
}
wl_list_for_each(device, &b->kms_list, link) {
if (udev_event_is_hotplug(device, event)) {
if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id))
drm_backend_update_conn_props(b, conn_id, prop_id);
else
drm_backend_update_connectors(device, event);
}
}
udev_device_unref(event);
return 1;
......@@ -3124,6 +3194,91 @@ recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time,
}
#endif
static struct drm_device *
drm_device_create(struct drm_backend *backend, const char *name)
{
struct weston_compositor *compositor = backend->compositor;
struct udev_device *udev_device;
struct drm_device *device;
struct wl_event_loop *loop;
drmModeRes *res;
device = zalloc(sizeof *device);
if (device == NULL)
return NULL;
device->state_invalid = true;
device->drm.fd = -1;
device->backend = backend;
device->gem_handle_refcnt = hash_table_create();
udev_device = open_specific_drm_device(backend, device, name);
if (!udev_device) {
free(device);
return NULL;
}
if (init_kms_caps(device) < 0) {
weston_log("failed to initialize kms\n");
goto err;
}
res = drmModeGetResources(device->drm.fd);
if (!res) {
weston_log("Failed to get drmModeRes\n");
goto err;
}
wl_list_init(&device->crtc_list);
if (drm_backend_create_crtc_list(device, res) == -1) {
weston_log("Failed to create CRTC list for DRM-backend\n");
goto err;
}
loop = wl_display_get_event_loop(compositor->wl_display);
wl_event_loop_add_fd(loop, device->drm.fd,
WL_EVENT_READABLE, on_drm_input, device);
wl_list_init(&device->plane_list);
create_sprites(device);
wl_list_init(&device->writeback_connector_list);
if (drm_backend_discover_connectors(device, udev_device, res) < 0) {
weston_log("Failed to create heads for %s\n", device->drm.filename);
goto err;
}
/* 'compute' faked zpos values in case HW doesn't expose any */
drm_backend_create_faked_zpos(device);
return device;
err:
return NULL;
}
static void
open_additional_devices(struct drm_backend *backend, const char *cards)
{
struct drm_device *device;
char *tokenize = strdup(cards);
char *card = strtok(tokenize, ",");
while (card) {
device = drm_device_create(backend, card);
if (!device) {
weston_log("unable to use card %s\n", card);
goto next;
}
weston_log("adding secondary device %s\n",
device->drm.filename);
wl_list_insert(&backend->kms_list, &device->link);
next:
card = strtok(NULL, ",");
}
free(tokenize);
}
static const struct weston_drm_output_api api = {
drm_output_set_mode,
......@@ -3168,6 +3323,7 @@ drm_backend_create(struct weston_compositor *compositor,
device->backend = b;
b->drm = device;
wl_list_init(&b->kms_list);
b->compositor = compositor;
b->pageflip_timeout = config->pageflip_timeout;
......@@ -3216,6 +3372,9 @@ drm_backend_create(struct weston_compositor *compositor,
goto err_udev_dev;
}
if (config->additional_devices)
open_additional_devices(b, config->additional_devices);
if (config->renderer == WESTON_RENDERER_AUTO) {
#ifdef BUILD_DRM_GBM
config->renderer = WESTON_RENDERER_GL;
......@@ -3283,7 +3442,7 @@ drm_backend_create(struct weston_compositor *compositor,
drmModeFreeResources(res);
/* 'compute' faked zpos values in case HW doesn't expose any */
drm_backend_create_faked_zpos(b);
drm_backend_create_faked_zpos(b->drm);
/* A this point we have some idea of whether or not we have a working
* cursor plane. */
......
......@@ -38,6 +38,7 @@
#include <libweston/backend-drm.h>
#include <libweston/pixel-formats.h>
#include <libweston/linux-dmabuf.h>
#include "shared/hash.h"
#include "shared/helpers.h"
#include "shared/weston-drm-fourcc.h"
#include "drm-internal.h"
......@@ -68,6 +69,138 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
drm_fb_destroy(fb);
}
#ifdef BUILD_DRM_GBM
static int gem_handle_get(struct drm_device *device, int handle)
{
unsigned int *ref_count;
ref_count = hash_table_lookup(device->gem_handle_refcnt, handle);
if (!ref_count) {
ref_count = zalloc(sizeof(*ref_count));
hash_table_insert(device->gem_handle_refcnt, handle, ref_count);
}
(*ref_count)++;
return handle;
}
static void gem_handle_put(struct drm_device *device, int handle)
{
unsigned int *ref_count;
if (handle == 0)
return;
ref_count = hash_table_lookup(device->gem_handle_refcnt, handle);
if (!ref_count) {
weston_log("failed to find GEM handle %d for device %s\n",
handle, device->drm.filename);
return;
}
(*ref_count)--;
if (*ref_count == 0) {
hash_table_remove(device->gem_handle_refcnt, handle);
free(ref_count);
drmCloseBufferHandle(device->drm.fd, handle);
}
}
static int
drm_fb_import_plane(struct drm_device *device, struct drm_fb *fb, int plane)
{
int bo_fd;
uint32_t handle;
int ret;
bo_fd = gbm_bo_get_fd_for_plane(fb->bo, plane);
if (bo_fd < 0)
return bo_fd;
/*
* drmPrimeFDToHandle is dangerous, because the GEM handles are
* not reference counted by the kernel and user space needs a
* single reference counting implementation to avoid double
* closing of GEM handles.
*
* It is not desirable to use a GBM device here, because this
* requires a GBM device implementation, which might not be
* available for simple or custom DRM devices that only support
* scanout and no rendering.
*
* We are only importing the buffers from the render device to
* the scanout device if the devices are distinct, since
* otherwise no import is necessary. Therefore, we are the only
* instance using the handles and we can implement reference
* counting for the handles per device. See gem_handle_get and
* gem_handle_put for the implementation.
*/
ret = drmPrimeFDToHandle(fb->fd, bo_fd, &handle);
if (ret)
goto out;
fb->handles[plane] = gem_handle_get(device, handle);
out:
close(bo_fd);
return ret;
}
#endif
/*
* If the fb is using a GBM surface, there is a possibility that the GBM
* surface has been created on a different device than the device which
* should be used for the fb. We have to import the fd of the GBM bo
* into the scanout device.
*/
static int
drm_fb_maybe_import(struct drm_device *device, struct drm_fb *fb)
{
#ifndef BUILD_DRM_GBM
/*
* Without GBM support, the fb is always allocated on the scanout device
* and import is never necessary.
*/
return 0;
#else
struct gbm_device *gbm_device;
int ret = 0;
int plane;
/* No import possible, if there is no gbm bo */
if (!fb->bo)
return 0;
/* No import necessary, if the gbm bo and the fb use the same device */
gbm_device = gbm_bo_get_device(fb->bo);
if (gbm_device_get_fd(gbm_device) == fb->fd)
return 0;
if (fb->fd != device->drm.fd) {
weston_log("fb was not allocated for scanout device %s\n",
device->drm.filename);
return -1;
}
for (plane = 0; plane < gbm_bo_get_plane_count(fb->bo); plane++) {
ret = drm_fb_import_plane(device, fb, plane);
if (ret)
goto err;
}
fb->scanout_device = device;
return 0;
err:
for (; plane >= 0; plane--) {
gem_handle_put(device, fb->handles[plane]);
fb->handles[plane] = 0;
}
return ret;
#endif
}
static int
drm_fb_addfb(struct drm_device *device, struct drm_fb *fb)
{
......@@ -75,6 +208,10 @@ drm_fb_addfb(struct drm_device *device, struct drm_fb *fb)
uint64_t mods[4] = { };
size_t i;
ret = drm_fb_maybe_import(device, fb);
if (ret)
return ret;
/* If we have a modifier set, we must only use the WithModifiers
* entrypoint; we cannot import it through legacy ioctls. */
if (device->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) {
......@@ -209,10 +346,21 @@ drm_fb_destroy_gbm(struct gbm_bo *bo, void *data)
static void
drm_fb_destroy_dmabuf(struct drm_fb *fb)
{
int i;
/* We deliberately do not close the GEM handles here; GBM manages
* their lifetime through the BO. */
if (fb->bo)
gbm_bo_destroy(fb->bo);
/*
* If we imported the dmabuf into a scanout device, we are responsible
* for closing the GEM handle.
*/
for (i = 0; i < 4; i++)
if (fb->scanout_device && fb->handles[i] != 0)
gem_handle_put(fb->scanout_device, fb->handles[i]);
drm_fb_destroy(fb);
}
......@@ -512,18 +660,24 @@ drm_fb_compatible_with_plane(struct drm_fb *fb, struct drm_plane *plane)
static void
drm_fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
{
struct drm_buffer_fb *buf_fb =
container_of(listener, struct drm_buffer_fb, buffer_destroy_listener);
struct drm_fb_private *private =
container_of(listener, struct drm_fb_private, buffer_destroy_listener);
struct drm_buffer_fb *buf_fb;
struct drm_buffer_fb *tmp;
wl_list_remove(&buf_fb->buffer_destroy_listener.link);
wl_list_remove(&private->buffer_destroy_listener.link);
if (buf_fb->fb) {
assert(buf_fb->fb->type == BUFFER_CLIENT ||
buf_fb->fb->type == BUFFER_DMABUF);
drm_fb_unref(buf_fb->fb);
wl_list_for_each_safe(buf_fb, tmp, &private->buffer_fb_list, link) {
if (buf_fb->fb) {
assert(buf_fb->fb->type == BUFFER_CLIENT ||
buf_fb->fb->type == BUFFER_DMABUF);
drm_fb_unref(buf_fb->fb);
}
wl_list_remove(&buf_fb->link);
free(buf_fb);
}
free(buf_fb);
free(private);
}
struct drm_fb *
......@@ -535,6 +689,7 @@ drm_fb_get_from_paint_node(struct drm_output_state *state,
struct drm_device *device = output->device;
struct weston_view *ev = pnode->view;
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
struct drm_fb_private *private;
struct drm_buffer_fb *buf_fb;
bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox);
struct drm_fb *fb;
......@@ -558,16 +713,26 @@ drm_fb_get_from_paint_node(struct drm_output_state *state,
return NULL;
}
if (buffer->backend_private) {
buf_fb = buffer->backend_private;
pnode->try_view_on_plane_failure_reasons |= buf_fb->failure_reasons;
return buf_fb->fb ? drm_fb_ref(buf_fb->fb) : NULL;
if (!buffer->backend_private) {
private = zalloc(sizeof(*private));
buffer->backend_private = private;
wl_list_init(&private->buffer_fb_list);
private->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal, &private->buffer_destroy_listener);
} else {
private = buffer->backend_private;
}
wl_list_for_each(buf_fb, &private->buffer_fb_list, link) {
if (buf_fb->device == device) {
pnode->try_view_on_plane_failure_reasons |= buf_fb->failure_reasons;
return buf_fb->fb ? drm_fb_ref(buf_fb->fb) : NULL;
}
}
buf_fb = zalloc(sizeof(*buf_fb));
buffer->backend_private = buf_fb;
buf_fb->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener);
buf_fb->device = device;
wl_list_insert(&private->buffer_fb_list, &buf_fb->link);
/* GBM is used for dmabuf import as well as from client wl_buffer. */
if (!b->gbm) {
......
......@@ -36,6 +36,7 @@ deps_drm = [
dep_egl, # optional
dep_libm,
dep_libdl,
dep_libshared,
dep_libweston_private,
dep_session_helper,
dep_libdrm,
......
......@@ -188,8 +188,13 @@ cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
buffer->width * 4);
wl_shm_buffer_end_access(buffer->shm_buffer);
if (gbm_bo_write(bo, buf, sizeof buf) < 0)
weston_log("failed update cursor: %s\n", strerror(errno));
if (bo) {
if (gbm_bo_write(bo, buf, sizeof buf) < 0)
weston_log("failed update cursor: %s\n", strerror(errno));
} else {
memcpy(output->gbm_cursor_fb[output->current_cursor]->map,
buf, sizeof buf);
}
}
static struct drm_plane_state *
......
......@@ -5,6 +5,7 @@ srcs_libshared = [
'file-util.c',
'os-compatibility.c',
'process-util.c',
'hash.c',
]
deps_libshared = [dep_wayland_client, dep_wayland_server, dep_pixman]
......
......@@ -40,7 +40,7 @@
#include <libweston/libweston.h>
#include "xwayland.h"
#include "hash.h"
#include "shared/hash.h"
struct dnd_data_source {
struct weston_data_source base;
......
......@@ -14,7 +14,6 @@ srcs_xwayland = [
'window-manager.c',
'selection.c',
'dnd.c',
'hash.c',
]
dep_names_xwayland = [
......
......@@ -46,7 +46,7 @@
#include "xwayland-internal-interface.h"
#include "shared/cairo-util.h"
#include "hash.h"
#include "shared/hash.h"
#include "shared/helpers.h"
#include "shared/xcb-xwayland.h"
......