backend-drm: import GBM bo to scanout device if necessary
If the GBM bo was allocated on a different device than the device that is used for the fb, we have to import the fd first and update the handle. Use drmPrimeFDToHandle directly instead of using a gbm device for the scanout device, since a gbm device would require a gbm implementation, which is often not available for devices that only support scanout. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
This commit is contained in:
parent
575804c7e1
commit
7887d3fb48
@ -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;
|
||||
|
||||
@ -378,6 +383,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;
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ deps_drm = [
|
||||
dep_egl, # optional
|
||||
dep_libm,
|
||||
dep_libdl,
|
||||
dep_libshared,
|
||||
dep_libweston_private,
|
||||
dep_session_helper,
|
||||
dep_libdrm,
|
||||
|
Loading…
Reference in New Issue
Block a user