native_drm.c revision 23aa978a9d76a48f4b93e9a8911ec50c0e5d94ab
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.11
4 *
5 * Copyright (C) 2011 Benjamin Franzke <benjaminfranzke@googlemail.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26#include "util/u_memory.h"
27#include "util/u_inlines.h"
28
29#include "pipe/p_compiler.h"
30#include "pipe/p_screen.h"
31#include "pipe/p_context.h"
32#include "pipe/p_state.h"
33#include "state_tracker/drm_driver.h"
34
35#include "egllog.h"
36#include <errno.h>
37
38#include "native_wayland.h"
39
40#include <wayland-client.h>
41#include "wayland-drm-client-protocol.h"
42#include "wayland-egl-priv.h"
43
44#include "common/native_wayland_drm_bufmgr_helper.h"
45
46#include <xf86drm.h>
47#include <sys/types.h>
48#include <sys/stat.h>
49#include <fcntl.h>
50
51struct wayland_drm_display {
52   struct wayland_display base;
53
54   const struct native_event_handler *event_handler;
55
56   struct wl_drm *wl_drm;
57   struct wl_drm *wl_server_drm; /* for EGL_WL_bind_wayland_display */
58   int fd;
59   char *device_name;
60   boolean authenticated;
61
62   /* supported formats */
63   boolean argb32;
64   boolean argb32_pre;
65   boolean xrgb32;
66};
67
68static INLINE struct wayland_drm_display *
69wayland_drm_display(const struct native_display *ndpy)
70{
71   return (struct wayland_drm_display *) ndpy;
72}
73
74static void
75wayland_drm_display_destroy(struct native_display *ndpy)
76{
77   struct wayland_drm_display *drmdpy = wayland_drm_display(ndpy);
78
79   if (drmdpy->fd)
80      close(drmdpy->fd);
81   if (drmdpy->wl_drm)
82      wl_drm_destroy(drmdpy->wl_drm);
83   if (drmdpy->device_name)
84      FREE(drmdpy->device_name);
85   if (drmdpy->base.configs)
86      FREE(drmdpy->base.configs);
87   if (drmdpy->base.own_dpy)
88      wl_display_destroy(drmdpy->base.dpy);
89
90   ndpy_uninit(ndpy);
91
92   FREE(drmdpy);
93}
94
95static struct wl_buffer *
96wayland_create_drm_buffer(struct wayland_display *display,
97                          struct wayland_surface *surface,
98                          enum native_attachment attachment)
99{
100   struct wayland_drm_display *drmdpy = (struct wayland_drm_display *) display;
101   struct pipe_screen *screen = drmdpy->base.base.screen;
102   struct pipe_resource *resource;
103   struct winsys_handle wsh;
104   uint width, height;
105   uint32_t format;
106
107   resource = resource_surface_get_single_resource(surface->rsurf, attachment);
108   resource_surface_get_size(surface->rsurf, &width, &height);
109
110   wsh.type = DRM_API_HANDLE_TYPE_SHARED;
111   screen->resource_get_handle(screen, resource, &wsh);
112
113   pipe_resource_reference(&resource, NULL);
114
115   switch (surface->color_format) {
116   case PIPE_FORMAT_B8G8R8A8_UNORM:
117      format = (surface->premultiplied_alpha) ?
118         WL_DRM_FORMAT_PREMULTIPLIED_ARGB32 : WL_DRM_FORMAT_ARGB32;
119      break;
120   case PIPE_FORMAT_B8G8R8X8_UNORM:
121      format = WL_DRM_FORMAT_XRGB32;
122      break;
123   default:
124      return NULL;
125      break;
126   }
127
128   return wl_drm_create_buffer(drmdpy->wl_drm, wsh.handle,
129                               width, height, wsh.stride, format);
130}
131
132static boolean
133wayland_drm_display_add_configs(struct wayland_drm_display *drmdpy)
134{
135   struct wayland_config *configs;
136   enum pipe_format formats[2];
137   int i, num_formats = 0;
138
139   /*
140    * Only argb32 counts here.  If we make (!argbb32 && argb32_pre) count, we
141    * will not be able to support the case where
142    * native_present_control::premultiplied_alpha is FALSE.
143    */
144   if (drmdpy->argb32)
145      formats[num_formats++] = PIPE_FORMAT_B8G8R8A8_UNORM;
146
147   if (drmdpy->xrgb32)
148      formats[num_formats++] = PIPE_FORMAT_B8G8R8X8_UNORM;
149
150   if (!num_formats)
151      return FALSE;
152
153   configs = CALLOC(num_formats, sizeof(*configs));
154   if (!configs)
155      return FALSE;
156
157   for (i = 0; i < num_formats; i++) {
158      struct native_config *nconf = &configs[i].base;
159
160      nconf->buffer_mask =
161         (1 << NATIVE_ATTACHMENT_FRONT_LEFT) |
162         (1 << NATIVE_ATTACHMENT_BACK_LEFT);
163
164      nconf->color_format = formats[i];
165
166      nconf->window_bit = TRUE;
167      nconf->pixmap_bit = TRUE;
168   }
169
170   drmdpy->base.configs = configs;
171   drmdpy->base.num_configs = num_formats;
172
173   return TRUE;
174}
175
176static void
177drm_handle_device(void *data, struct wl_drm *drm, const char *device)
178{
179   struct wayland_drm_display *drmdpy = data;
180   drm_magic_t magic;
181
182   drmdpy->device_name = strdup(device);
183   if (!drmdpy->device_name)
184      return;
185
186   drmdpy->fd = open(drmdpy->device_name, O_RDWR);
187   if (drmdpy->fd == -1) {
188      _eglLog(_EGL_WARNING, "wayland-egl: could not open %s (%s)",
189              drmdpy->device_name, strerror(errno));
190      return;
191   }
192
193   drmGetMagic(drmdpy->fd, &magic);
194   wl_drm_authenticate(drmdpy->wl_drm, magic);
195}
196
197static void
198drm_handle_format(void *data, struct wl_drm *drm, uint32_t format)
199{
200   struct wayland_drm_display *drmdpy = data;
201
202   switch (format) {
203   case WL_DRM_FORMAT_ARGB32:
204      drmdpy->argb32 = TRUE;
205      break;
206   case WL_DRM_FORMAT_PREMULTIPLIED_ARGB32:
207      drmdpy->argb32_pre = TRUE;
208      break;
209   case WL_DRM_FORMAT_XRGB32:
210      drmdpy->xrgb32 = TRUE;
211      break;
212   }
213}
214
215static void
216drm_handle_authenticated(void *data, struct wl_drm *drm)
217{
218   struct wayland_drm_display *drmdpy = data;
219
220   drmdpy->authenticated = true;
221}
222
223static const struct wl_drm_listener drm_listener = {
224   drm_handle_device,
225   drm_handle_format,
226   drm_handle_authenticated
227};
228
229static boolean
230wayland_drm_display_init_screen(struct native_display *ndpy)
231{
232   struct wayland_drm_display *drmdpy = wayland_drm_display(ndpy);
233   uint32_t id;
234
235   id = wl_display_get_global(drmdpy->base.dpy, "wl_drm", 1);
236   if (id == 0)
237      wl_display_roundtrip(drmdpy->base.dpy);
238   id = wl_display_get_global(drmdpy->base.dpy, "wl_drm", 1);
239   if (id == 0)
240      return FALSE;
241
242   drmdpy->wl_drm = wl_display_bind(drmdpy->base.dpy, id, &wl_drm_interface);
243   if (!drmdpy->wl_drm)
244      return FALSE;
245
246   wl_drm_add_listener(drmdpy->wl_drm, &drm_listener, drmdpy);
247   wl_display_roundtrip(drmdpy->base.dpy);
248   if (drmdpy->fd == -1)
249      return FALSE;
250
251   wl_display_roundtrip(drmdpy->base.dpy);
252   if (!drmdpy->authenticated)
253      return FALSE;
254
255   if (!wayland_drm_display_add_configs(drmdpy))
256      return FALSE;
257
258   /* check that premultiplied alpha is supported for all formats with alpha */
259   if (!drmdpy->argb32 || drmdpy->argb32_pre)
260      drmdpy->base.param_premultiplied_alpha = TRUE;
261
262   drmdpy->base.base.screen =
263      drmdpy->event_handler->new_drm_screen(&drmdpy->base.base,
264                                            NULL, drmdpy->fd);
265   if (!drmdpy->base.base.screen) {
266      _eglLog(_EGL_WARNING, "failed to create DRM screen");
267      return FALSE;
268   }
269
270   return TRUE;
271}
272
273static struct native_display_buffer wayland_drm_display_buffer = {
274   /* use the helpers */
275   drm_display_import_native_buffer,
276   drm_display_export_native_buffer
277};
278
279static int
280wayland_drm_display_authenticate(void *user_data, uint32_t magic)
281{
282   struct native_display *ndpy = user_data;
283   struct wayland_drm_display *drmdpy = wayland_drm_display(ndpy);
284   boolean current_authenticate, authenticated;
285
286   current_authenticate = drmdpy->authenticated;
287
288   wl_drm_authenticate(drmdpy->wl_drm, magic);
289   wl_display_roundtrip(drmdpy->base.dpy);
290   authenticated = drmdpy->authenticated;
291
292   drmdpy->authenticated = current_authenticate;
293
294   return authenticated ? 0 : -1;
295}
296
297static struct wayland_drm_callbacks wl_drm_callbacks = {
298   wayland_drm_display_authenticate,
299   egl_g3d_wl_drm_helper_reference_buffer,
300   egl_g3d_wl_drm_helper_unreference_buffer
301};
302
303static boolean
304wayland_drm_display_bind_wayland_display(struct native_display *ndpy,
305                                         struct wl_display *wl_dpy)
306{
307   struct wayland_drm_display *drmdpy = wayland_drm_display(ndpy);
308
309   if (drmdpy->wl_server_drm)
310      return FALSE;
311
312   drmdpy->wl_server_drm =
313      wayland_drm_init(wl_dpy, drmdpy->device_name,
314                       &wl_drm_callbacks, ndpy);
315
316   if (!drmdpy->wl_server_drm)
317      return FALSE;
318
319   return TRUE;
320}
321
322static boolean
323wayland_drm_display_unbind_wayland_display(struct native_display *ndpy,
324                                           struct wl_display *wl_dpy)
325{
326   struct wayland_drm_display *drmdpy = wayland_drm_display(ndpy);
327
328   if (!drmdpy->wl_server_drm)
329      return FALSE;
330
331   wayland_drm_uninit(drmdpy->wl_server_drm);
332   drmdpy->wl_server_drm = NULL;
333
334   return TRUE;
335}
336
337static struct native_display_wayland_bufmgr wayland_drm_display_wayland_bufmgr = {
338   wayland_drm_display_bind_wayland_display,
339   wayland_drm_display_unbind_wayland_display,
340   egl_g3d_wl_drm_common_wl_buffer_get_resource
341};
342
343
344struct wayland_display *
345wayland_create_drm_display(struct wl_display *dpy,
346                           const struct native_event_handler *event_handler)
347{
348   struct wayland_drm_display *drmdpy;
349
350   drmdpy = CALLOC_STRUCT(wayland_drm_display);
351   if (!drmdpy)
352      return NULL;
353
354   drmdpy->event_handler = event_handler;
355
356   drmdpy->base.dpy = dpy;
357   if (!drmdpy->base.dpy) {
358      wayland_drm_display_destroy(&drmdpy->base.base);
359      return NULL;
360   }
361
362   drmdpy->base.base.init_screen = wayland_drm_display_init_screen;
363   drmdpy->base.base.destroy = wayland_drm_display_destroy;
364   drmdpy->base.base.buffer = &wayland_drm_display_buffer;
365   drmdpy->base.base.wayland_bufmgr = &wayland_drm_display_wayland_bufmgr;
366
367   drmdpy->base.create_buffer = wayland_create_drm_buffer;
368
369   return &drmdpy->base;
370}
371
372/* vim: set sw=3 ts=8 sts=3 expandtab: */
373