1 /*
2 * Copyright (C) 2011 Texas Instruments
3 * Author: Nikhil Devshatwar <nikhil.nd@ti.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #include "util.h"
23 #include <pthread.h>
24 #include <xf86drmMode.h>
25 #include <wayland-client.h>
26 #include <wayland-drm-client-protocol.h>
27 #include <viewporter-client-protocol.h>
29 /* NOTE: healthy dose of recycling from libdrm modetest app.. */
32 #define to_display_wl(x) container_of(x, struct display_wl, base)
33 struct display_wl {
34 struct display base;
36 uint32_t bo_flags;
37 struct wl_display *display;
38 struct wl_compositor *compositor;
39 struct wl_surface *surface;
40 struct wl_shell *shell;
41 struct wl_drm *drm;
42 struct wl_callback *callback;
43 struct wp_viewport *viewport;
44 struct wp_viewporter *viewporter;
45 pthread_t threadID;
46 };
48 #define to_buffer_wl(x) container_of(x, struct buffer_wl, base)
49 struct buffer_wl {
50 struct buffer base;
52 struct wl_buffer *wl_buf;
53 uint32_t name;
54 };
56 static int global_fd = 0;
57 static uint32_t used_planes = 0;
58 static int ndisplays = 0;
60 int wayland_process_events(struct display_wl *disp_wl)
61 {
63 wl_display_dispatch(disp_wl->display);
65 return 0;
66 }
67 void* wayland_ev_task(void *arg)
68 {
69 int ret;
70 while(1) {
71 ret = wayland_process_events((struct display_wl *)arg);
72 }
74 return NULL;
75 }
77 static struct omap_bo *
78 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
79 uint32_t *bo_handle, uint32_t *pitch)
80 {
81 struct display_wl *disp_wl = to_display_wl(disp);
82 struct omap_bo *bo;
83 uint32_t bo_flags = disp_wl->bo_flags;
85 bo_flags |= OMAP_BO_WC;
86 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
88 if (bo) {
89 *bo_handle = omap_bo_handle(bo);
90 *pitch = width * bpp / 8;
91 if (bo_flags & OMAP_BO_TILED)
92 *pitch = ALIGN2(*pitch, PAGE_SHIFT);
93 }
95 return bo;
96 }
98 static struct buffer *
99 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
100 {
101 struct display_wl *disp_wl = to_display_wl(disp);
102 struct buffer_wl *buf_wl;
103 struct buffer *buf;
104 uint32_t bo_handles[4] = {0};
105 uint32_t wl_fmt = WL_DRM_FORMAT_NV12;
107 buf_wl = calloc(1, sizeof(*buf_wl));
108 if (!buf_wl) {
109 ERROR("allocation failed");
110 return NULL;
111 }
112 buf = &buf_wl->base;
114 buf->fourcc = fourcc;
115 buf->width = w;
116 buf->height = h;
117 buf->multiplanar = false;
119 buf->nbo = 1;
121 if (!fourcc)
122 fourcc = FOURCC('A','R','2','4');
124 switch (fourcc) {
125 case FOURCC('A','R','2','4'):
126 wl_fmt = WL_DRM_FORMAT_ARGB8888;
127 buf->nbo = 1;
128 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
129 &bo_handles[0], &buf->pitches[0]);
130 omap_bo_get_name(buf->bo[0], &buf_wl->name);
131 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
133 /* ARGB: stride is four times width */
134 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
135 buf_wl->name, buf->width, buf->height,
136 buf->width * 4, wl_fmt);
137 break;
138 case FOURCC('U','Y','V','Y'):
139 wl_fmt = WL_DRM_FORMAT_UYVY;
140 buf->nbo = 1;
141 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
142 &bo_handles[0], &buf->pitches[0]);
144 omap_bo_get_name(buf->bo[0], &buf_wl->name);
145 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
146 /* YUYV; stride is double width */
147 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
148 buf_wl->name, buf->width, buf->height,
149 buf->width * 2, wl_fmt);
150 break;
151 case FOURCC('Y','U','Y','V'):
152 wl_fmt = WL_DRM_FORMAT_YUYV;
153 buf->nbo = 1;
154 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
155 &bo_handles[0], &buf->pitches[0]);
157 omap_bo_get_name(buf->bo[0], &buf_wl->name);
158 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
159 /* YUYV; stride is double width */
160 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
161 buf_wl->name, buf->width, buf->height,
162 buf->width * 2, wl_fmt);
163 break;
164 case FOURCC('N','V','1','2'):
165 wl_fmt = WL_DRM_FORMAT_NV12;
166 buf->nbo = 1;
167 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
168 &bo_handles[0], &buf->pitches[0]);
170 omap_bo_get_name(buf->bo[0], &buf_wl->name);
171 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
172 /* NV12: Create a planar buffer */
173 buf_wl->wl_buf = wl_drm_create_planar_buffer(disp_wl->drm,
174 buf_wl->name, buf->width, buf->height, wl_fmt,
175 0, buf->width,
176 buf->width * buf->height, buf->width,
177 0, 0);
178 break;
179 default:
180 ERROR("invalid format: 0x%08x", fourcc);
181 goto fail;
182 }
184 return buf;
186 fail:
187 // XXX cleanup
188 return NULL;
189 }
191 static void
192 free_buffers(struct display *disp, uint32_t n)
193 {
194 uint32_t i;
195 for (i = 0; i < n; i++) {
196 if (disp->buf[i]) {
197 close(disp->buf[i]->fd[0]);
198 omap_bo_del(disp->buf[i]->bo[0]);
199 if (disp->multiplanar) {
200 close(disp->buf[i]->fd[1]);
201 omap_bo_del(disp->buf[i]->bo[1]);
202 }
203 }
204 }
205 free(disp->buf);
206 }
208 static struct buffer **
209 alloc_buffers(struct display *disp, uint32_t n,
210 uint32_t fourcc, uint32_t w, uint32_t h)
211 {
212 struct buffer **bufs;
213 uint32_t i = 0;
215 bufs = calloc(n, sizeof(*bufs));
216 if (!bufs) {
217 ERROR("allocation failed");
218 goto fail;
219 }
221 for (i = 0; i < n; i++) {
222 bufs[i] = alloc_buffer(disp, fourcc, w, h);
223 if (!bufs[i]) {
224 ERROR("allocation failed");
225 goto fail;
226 }
227 }
228 disp->buf = bufs;
229 return bufs;
231 fail:
232 // XXX cleanup
233 return NULL;
234 }
236 static struct buffer **
237 get_buffers(struct display *disp, uint32_t n)
238 {
239 return alloc_buffers(disp, n, 0, disp->width, disp->height);
240 }
242 static struct buffer **
243 get_vid_buffers(struct display *disp, uint32_t n,
244 uint32_t fourcc, uint32_t w, uint32_t h)
245 {
246 return alloc_buffers(disp, n, fourcc, w, h);
247 }
249 static int
250 post_buffer(struct display *disp, struct buffer *buf)
251 {
252 return -1;
253 }
254 static void redraw(void *data, struct wl_callback *callback, uint32_t time) {
255 struct display_wl *disp_wl = (struct display_wl *) data;
257 wl_callback_destroy(disp_wl->callback);
258 disp_wl->callback = NULL;
259 }
261 static const struct wl_callback_listener frame_listener = { redraw };
263 static int
264 post_vid_buffer(struct display *disp, struct buffer *buf,
265 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
266 {
267 struct display_wl *disp_wl = to_display_wl(disp);
268 struct buffer_wl *buf_wl = to_buffer_wl(buf);
269 int ret = 0;
271 wl_surface_attach(disp_wl->surface, buf_wl->wl_buf, 0, 0);
272 wl_surface_damage(disp_wl->surface,
273 0, 0, disp->width, disp->height);
274 wp_viewport_set_source(disp_wl->viewport,
275 wl_fixed_from_int(x), wl_fixed_from_int(y),
276 wl_fixed_from_int(w), wl_fixed_from_int(h));
278 if (disp_wl->callback)
279 wl_callback_destroy(disp_wl->callback);
280 disp_wl->callback = wl_surface_frame(disp_wl->surface);
282 wl_callback_add_listener(disp_wl->callback, &frame_listener, disp_wl);
284 wl_surface_commit(disp_wl->surface);
285 wl_display_flush(disp_wl->display);
287 return ret;
288 }
290 static void
291 close_kms(struct display *disp)
292 {
293 struct display_wl *disp_wl = to_display_wl(disp);
294 pthread_cancel(disp_wl->threadID);
296 omap_device_del(disp->dev);
297 disp->dev = NULL;
298 if (used_planes) {
299 used_planes >>= 1;
300 }
301 if (--ndisplays == 0) {
302 close(global_fd);
303 }
304 }
306 void
307 disp_wayland_usage(void)
308 {
309 MSG("WAYLAND Display Options:");
310 MSG("\t-w <width>x<height>\tset the dimensions of client window");
311 }
313 static void
314 registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
315 const char *interface, uint32_t version)
316 {
317 struct display_wl *disp_wl = (struct display_wl *) data;
319 if (strcmp(interface, "wl_compositor") == 0) {
320 disp_wl->compositor = wl_registry_bind(registry, id,
321 &wl_compositor_interface, 3);
322 } else if (strcmp(interface, "wl_shell") == 0) {
323 disp_wl->shell = wl_registry_bind(registry, id,
324 &wl_shell_interface, 1);
325 } else if (strcmp(interface, "wl_drm") == 0) {
326 disp_wl->drm = wl_registry_bind(registry, id,
327 &wl_drm_interface, 1);
328 } else if (strcmp(interface, "wp_viewporter") == 0) {
329 disp_wl->viewporter = wl_registry_bind(registry, id,
330 &wp_viewporter_interface, 1);
331 }
332 }
334 static void
335 registry_handle_global_remove(void *data, struct wl_registry *registry,
336 uint32_t name)
337 {
338 }
339 static const struct wl_registry_listener registry_listener = {
340 registry_handle_global,
341 registry_handle_global_remove
342 };
345 struct display *
346 disp_wayland_open(int argc, char **argv)
347 {
348 struct display_wl *disp_wl = NULL;
349 struct display *disp;
350 int i, enabled = 0, width, height;
352 struct wl_registry *registry;
353 struct wl_shell_surface *shell_surface;
354 struct wl_region *region;
355 int ret;
357 /* note: set args to NULL after we've parsed them so other modules know
358 * that it is already parsed (since the arg parsing is decentralized)
359 */
360 for (i = 1; i < argc; i++) {
361 if (!argv[i]) {
362 continue;
363 }
364 if (!strcmp("-w", argv[i])) {
365 argv[i++] = NULL;
366 if (sscanf(argv[i], "%dx%d", &width, &height) != 2) {
367 ERROR("invalid arg: -w %s", argv[i]);
368 goto fail;
369 }
370 enabled = 1;
371 } else {
372 /* ignore */
373 continue;
374 }
375 argv[i] = NULL;
376 }
377 // If not explicitely enabled from command line, fail.
378 if (!enabled)
379 goto fail;
381 disp_wl = calloc(1, sizeof(*disp_wl));
382 if (!disp_wl) {
383 ERROR("allocation failed");
384 goto fail;
385 }
386 disp = &disp_wl->base;
388 if (!global_fd) {
389 global_fd = drmOpen("omapdrm", NULL);
390 if (global_fd < 0) {
391 ERROR("could not open drm device: %s (%d)",
392 strerror(errno), errno);
393 goto fail;
394 }
395 }
397 disp->fd = global_fd;
398 ndisplays++;
400 disp->dev = omap_device_new(disp->fd);
401 if (!disp->dev) {
402 ERROR("couldn't create device");
403 goto fail;
404 }
406 disp->get_buffers = get_buffers;
407 disp->get_vid_buffers = get_vid_buffers;
408 disp->post_buffer = post_buffer;
409 disp->post_vid_buffer = post_vid_buffer;
410 disp->close = close_kms;
411 disp->disp_free_buf = free_buffers;
413 disp->multiplanar = false;
414 disp->width = width;
415 disp->height = height;
417 disp_wl->bo_flags = OMAP_BO_SCANOUT|OMAP_BO_WC;
419 disp_wl->display = wl_display_connect(NULL);
420 if (disp_wl->display == NULL) {
421 ERROR("failed to connect to Wayland display: %m\n");
422 goto fail;
423 } else {
424 MSG("wayland display opened\n");
425 }
427 /* Find out what interfaces are implemented, and initialize */
428 registry = wl_display_get_registry(disp_wl->display);
429 wl_registry_add_listener(registry, ®istry_listener, disp_wl);
430 wl_display_roundtrip(disp_wl->display);
431 MSG("wayland registries obtained\n");
433 disp_wl->surface = wl_compositor_create_surface(disp_wl->compositor);
434 shell_surface = wl_shell_get_shell_surface(disp_wl->shell,
435 disp_wl->surface);
436 wl_shell_surface_set_toplevel(shell_surface);
438 region = wl_compositor_create_region(disp_wl->compositor);
439 wl_region_add(region, 0, 0, disp->width, disp->height);
441 disp_wl->viewport = wp_viewporter_get_viewport(disp_wl->viewporter,
442 disp_wl->surface);
443 wp_viewport_set_destination(disp_wl->viewport, width, height);
445 ret = pthread_create(&disp_wl->threadID, NULL, wayland_ev_task, disp_wl);
446 if(ret) {
447 MSG("could not create task for wayland event processing");
448 }
450 return disp;
452 fail:
453 // XXX cleanup
454 return NULL;
455 }