/*
* Mesa 3-D graphics library
* Version: 7.8
*
* Copyright (C) 2010 Chia-I Wu <olv@0xlab.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "util/u_memory.h"
#include "egllog.h"
#include "native_drm.h"
#include "gbm_gallium_drmint.h"
#ifdef HAVE_LIBUDEV
#include <libudev.h>
#endif
static boolean
drm_display_is_format_supported(struct native_display *ndpy,
enum pipe_format fmt, boolean is_color)
{
return ndpy->screen->is_format_supported(ndpy->screen,
fmt, PIPE_TEXTURE_2D, 0,
(is_color) ? PIPE_BIND_RENDER_TARGET :
PIPE_BIND_DEPTH_STENCIL);
}
static const struct native_config **
drm_display_get_configs(struct native_display *ndpy, int *num_configs)
{
struct drm_display *drmdpy = drm_display(ndpy);
const struct native_config **configs;
/* first time */
if (!drmdpy->config) {
struct native_config *nconf;
enum pipe_format format;
drmdpy->config = CALLOC(1, sizeof(*drmdpy->config));
if (!drmdpy->config)
return NULL;
nconf = &drmdpy->config->base;
nconf->buffer_mask =
(1 << NATIVE_ATTACHMENT_FRONT_LEFT) |
(1 << NATIVE_ATTACHMENT_BACK_LEFT);
format = PIPE_FORMAT_B8G8R8A8_UNORM;
if (!drm_display_is_format_supported(&drmdpy->base, format, TRUE)) {
format = PIPE_FORMAT_A8R8G8B8_UNORM;
if (!drm_display_is_format_supported(&drmdpy->base, format, TRUE))
format = PIPE_FORMAT_NONE;
}
if (format == PIPE_FORMAT_NONE) {
FREE(drmdpy->config);
drmdpy->config = NULL;
return NULL;
}
nconf->color_format = format;
/* support KMS */
if (drmdpy->resources)
nconf->scanout_bit = TRUE;
}
configs = MALLOC(sizeof(*configs));
if (configs) {
configs[0] = &drmdpy->config->base;
if (num_configs)
*num_configs = 1;
}
return configs;
}
static int
drm_display_get_param(struct native_display *ndpy,
enum native_param_type param)
{
int val;
switch (param) {
case NATIVE_PARAM_USE_NATIVE_BUFFER:
case NATIVE_PARAM_PRESERVE_BUFFER:
case NATIVE_PARAM_MAX_SWAP_INTERVAL:
default:
val = 0;
break;
}
return val;
}
static void
drm_display_destroy(struct native_display *ndpy)
{
struct drm_display *drmdpy = drm_display(ndpy);
if (drmdpy->config)
FREE(drmdpy->config);
drm_display_fini_modeset(&drmdpy->base);
/* gbm owns screen */
ndpy->screen = NULL;
ndpy_uninit(ndpy);
if (drmdpy->device_name)
FREE(drmdpy->device_name);
if (drmdpy->own_gbm) {
gbm_device_destroy(&drmdpy->gbmdrm->base.base);
if (drmdpy->fd >= 0)
close(drmdpy->fd);
}
FREE(drmdpy);
}
static struct native_display_buffer drm_display_buffer = {
/* use the helpers */
drm_display_import_native_buffer,
drm_display_export_native_buffer
};
static char *
drm_get_device_name(int fd)
{
char *device_name = NULL;
#ifdef HAVE_LIBUDEV
struct udev *udev;
struct udev_device *device;
struct stat buf;
const char *tmp;
udev = udev_new();
if (fstat(fd, &buf) < 0) {
_eglLog(_EGL_WARNING, "failed to stat fd %d", fd);
goto out;
}
device = udev_device_new_from_devnum(udev, 'c', buf.st_rdev);
if (device == NULL) {
_eglLog(_EGL_WARNING,
"could not create udev device for fd %d", fd);
goto out;
}
tmp = udev_device_get_devnode(device);
if (!tmp)
goto out;
device_name = strdup(tmp);
out:
udev_device_unref(device);
udev_unref(udev);
#endif
return device_name;
}
#ifdef HAVE_WAYLAND_BACKEND
static int
drm_display_authenticate(void *user_data, uint32_t magic)
{
struct native_display *ndpy = user_data;
struct drm_display *drmdpy = drm_display(ndpy);
return drmAuthMagic(drmdpy->fd, magic);
}
static struct wayland_drm_callbacks wl_drm_callbacks = {
drm_display_authenticate,
egl_g3d_wl_drm_helper_reference_buffer,
egl_g3d_wl_drm_helper_unreference_buffer
};
static boolean
drm_display_bind_wayland_display(struct native_display *ndpy,
struct wl_display *wl_dpy)
{
struct drm_display *drmdpy = drm_display(ndpy);
if (drmdpy->wl_server_drm)
return FALSE;
drmdpy->wl_server_drm = wayland_drm_init(wl_dpy,
drmdpy->device_name,
&wl_drm_callbacks, ndpy);
if (!drmdpy->wl_server_drm)
return FALSE;
return TRUE;
}
static boolean
drm_display_unbind_wayland_display(struct native_display *ndpy,
struct wl_display *wl_dpy)
{
struct drm_display *drmdpy = drm_display(ndpy);
if (!drmdpy->wl_server_drm)
return FALSE;
wayland_drm_uninit(drmdpy->wl_server_drm);
drmdpy->wl_server_drm = NULL;
return TRUE;
}
static struct native_display_wayland_bufmgr drm_display_wayland_bufmgr = {
drm_display_bind_wayland_display,
drm_display_unbind_wayland_display,
egl_g3d_wl_drm_common_wl_buffer_get_resource,
egl_g3d_wl_drm_common_query_buffer
};
#endif /* HAVE_WAYLAND_BACKEND */
static struct native_surface *
drm_create_pixmap_surface(struct native_display *ndpy,
EGLNativePixmapType pix,
const struct native_config *nconf)
{
struct gbm_gallium_drm_bo *bo = (void *) pix;
return drm_display_create_surface_from_resource(ndpy, bo->resource);
}
static boolean
drm_display_init_screen(struct native_display *ndpy)
{
return TRUE;
}
static struct native_display *
drm_create_display(struct gbm_gallium_drm_device *gbmdrm, int own_gbm,
const struct native_event_handler *event_handler)
{
struct drm_display *drmdpy;
drmdpy = CALLOC_STRUCT(drm_display);
if (!drmdpy)
return NULL;
drmdpy->gbmdrm = gbmdrm;
drmdpy->own_gbm = own_gbm;
drmdpy->fd = gbmdrm->base.base.fd;
drmdpy->device_name = drm_get_device_name(drmdpy->fd);
gbmdrm->lookup_egl_image = (struct pipe_resource *(*)(void *, void *))
event_handler->lookup_egl_image;
gbmdrm->lookup_egl_image_data = &drmdpy->base;
drmdpy->event_handler = event_handler;
drmdpy->base.screen = gbmdrm->screen;
drmdpy->base.init_screen = drm_display_init_screen;
drmdpy->base.destroy = drm_display_destroy;
drmdpy->base.get_param = drm_display_get_param;
drmdpy->base.get_configs = drm_display_get_configs;
drmdpy->base.create_pixmap_surface = drm_create_pixmap_surface;
drmdpy->base.buffer = &drm_display_buffer;
#ifdef HAVE_WAYLAND_BACKEND
if (drmdpy->device_name)
drmdpy->base.wayland_bufmgr = &drm_display_wayland_bufmgr;
#endif
drm_display_init_modeset(&drmdpy->base);
return &drmdpy->base;
}
static const struct native_event_handler *drm_event_handler;
static struct native_display *
native_create_display(void *dpy, boolean use_sw)
{
struct gbm_gallium_drm_device *gbm;
int fd;
int own_gbm = 0;
gbm = dpy;
if (gbm == NULL) {
const char *device_name="/dev/dri/card0";
#ifdef O_CLOEXEC
fd = open(device_name, O_RDWR | O_CLOEXEC);
if (fd == -1 && errno == EINVAL)
#endif
{
fd = open(device_name, O_RDWR);
if (fd != -1)
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
}
/* FIXME: Use an internal constructor to create a gbm
* device with gallium backend directly, without setenv */
setenv("GBM_BACKEND", "gbm_gallium_drm.so", 1);
gbm = gbm_gallium_drm_device(gbm_create_device(fd));
own_gbm = 1;
}
if (gbm == NULL)
return NULL;
if (strcmp(gbm_device_get_backend_name(&gbm->base.base), "drm") != 0 ||
gbm->base.type != GBM_DRM_DRIVER_TYPE_GALLIUM) {
if (own_gbm)
gbm_device_destroy(&gbm->base.base);
return NULL;
}
return drm_create_display(gbm, own_gbm, drm_event_handler);
}
static const struct native_platform drm_platform = {
"DRM", /* name */
native_create_display
};
const struct native_platform *
native_get_drm_platform(const struct native_event_handler *event_handler)
{
drm_event_handler = event_handler;
return &drm_platform;
}