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"
24 #include <xf86drmMode.h>
25 #include <wayland-client.h>
26 #include <wayland-drm-client-protocol.h>
27 #include <wayland-scaler-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 wl_viewport *viewport;
44 struct wl_scaler *scaler;
45 };
47 #define to_buffer_wl(x) container_of(x, struct buffer_wl, base)
48 struct buffer_wl {
49 struct buffer base;
51 struct wl_buffer *wl_buf;
52 uint32_t name;
53 };
55 static int global_fd = 0;
56 static uint32_t used_planes = 0;
57 static int ndisplays = 0;
59 static struct omap_bo *
60 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
61 uint32_t *bo_handle, uint32_t *pitch)
62 {
63 struct display_wl *disp_wl = to_display_wl(disp);
64 struct omap_bo *bo;
65 uint32_t bo_flags = disp_wl->bo_flags;
67 bo_flags |= OMAP_BO_WC;
68 bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
70 if (bo) {
71 *bo_handle = omap_bo_handle(bo);
72 *pitch = width * bpp / 8;
73 if (bo_flags & OMAP_BO_TILED)
74 *pitch = ALIGN2(*pitch, PAGE_SHIFT);
75 }
77 return bo;
78 }
80 static struct buffer *
81 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
82 {
83 struct display_wl *disp_wl = to_display_wl(disp);
84 struct buffer_wl *buf_wl;
85 struct buffer *buf;
86 uint32_t bo_handles[4] = {0};
87 uint32_t wl_fmt = WL_DRM_FORMAT_NV12;
89 buf_wl = calloc(1, sizeof(*buf_wl));
90 if (!buf_wl) {
91 ERROR("allocation failed");
92 return NULL;
93 }
94 buf = &buf_wl->base;
96 buf->fourcc = fourcc;
97 buf->width = w;
98 buf->height = h;
99 buf->multiplanar = false;
101 buf->nbo = 1;
103 if (!fourcc)
104 fourcc = FOURCC('A','R','2','4');
106 switch (fourcc) {
107 case FOURCC('A','R','2','4'):
108 wl_fmt = WL_DRM_FORMAT_ARGB8888;
109 buf->nbo = 1;
110 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
111 &bo_handles[0], &buf->pitches[0]);
112 omap_bo_get_name(buf->bo[0], &buf_wl->name);
113 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
115 /* ARGB: stride is four times width */
116 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
117 buf_wl->name, buf->width, buf->height,
118 buf->width * 4, wl_fmt);
119 break;
120 case FOURCC('U','Y','V','Y'):
121 case FOURCC('Y','U','Y','V'):
122 wl_fmt = WL_DRM_FORMAT_YUYV;
123 buf->nbo = 1;
124 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
125 &bo_handles[0], &buf->pitches[0]);
127 omap_bo_get_name(buf->bo[0], &buf_wl->name);
128 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
129 /* YUYV; stride is double width */
130 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
131 buf_wl->name, buf->width, buf->height,
132 buf->width * 2, wl_fmt);
133 break;
134 case FOURCC('N','V','1','2'):
135 wl_fmt = WL_DRM_FORMAT_NV12;
136 buf->nbo = 1;
137 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
138 &bo_handles[0], &buf->pitches[0]);
140 omap_bo_get_name(buf->bo[0], &buf_wl->name);
141 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
142 /* NV12: Create a planar buffer */
143 buf_wl->wl_buf = wl_drm_create_planar_buffer(disp_wl->drm,
144 buf_wl->name, buf->width, buf->height, wl_fmt,
145 0, buf->width,
146 buf->width * buf->height, buf->width,
147 0, 0);
148 break;
149 default:
150 ERROR("invalid format: 0x%08x", fourcc);
151 goto fail;
152 }
154 return buf;
156 fail:
157 // XXX cleanup
158 return NULL;
159 }
161 static void
162 free_buffers(struct display *disp, uint32_t n)
163 {
164 uint32_t i;
165 for (i = 0; i < n; i++) {
166 if (disp->buf[i]) {
167 close(disp->buf[i]->fd[0]);
168 omap_bo_del(disp->buf[i]->bo[0]);
169 if (disp->multiplanar) {
170 close(disp->buf[i]->fd[1]);
171 omap_bo_del(disp->buf[i]->bo[1]);
172 }
173 }
174 }
175 free(disp->buf);
176 }
178 static struct buffer **
179 alloc_buffers(struct display *disp, uint32_t n,
180 uint32_t fourcc, uint32_t w, uint32_t h)
181 {
182 struct buffer **bufs;
183 uint32_t i = 0;
185 bufs = calloc(n, sizeof(*bufs));
186 if (!bufs) {
187 ERROR("allocation failed");
188 goto fail;
189 }
191 for (i = 0; i < n; i++) {
192 bufs[i] = alloc_buffer(disp, fourcc, w, h);
193 if (!bufs[i]) {
194 ERROR("allocation failed");
195 goto fail;
196 }
197 }
198 disp->buf = bufs;
199 return bufs;
201 fail:
202 // XXX cleanup
203 return NULL;
204 }
206 static struct buffer **
207 get_buffers(struct display *disp, uint32_t n)
208 {
209 return alloc_buffers(disp, n, 0, disp->width, disp->height);
210 }
212 static struct buffer **
213 get_vid_buffers(struct display *disp, uint32_t n,
214 uint32_t fourcc, uint32_t w, uint32_t h)
215 {
216 return alloc_buffers(disp, n, fourcc, w, h);
217 }
219 static int
220 post_buffer(struct display *disp, struct buffer *buf)
221 {
222 ERROR("post_buffer not supported");
224 return -1;
225 }
226 static void redraw(void *data, struct wl_callback *callback, uint32_t time) {
227 struct display_wl *disp_wl = (struct display_wl *) data;
229 wl_callback_destroy(disp_wl->callback);
230 disp_wl->callback = NULL;
231 }
233 static const struct wl_callback_listener frame_listener = { redraw };
235 static int
236 post_vid_buffer(struct display *disp, struct buffer *buf,
237 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
238 {
239 struct display_wl *disp_wl = to_display_wl(disp);
240 struct buffer_wl *buf_wl = to_buffer_wl(buf);
241 int ret = 0;
243 wl_surface_attach(disp_wl->surface, buf_wl->wl_buf, 0, 0);
245 if (disp_wl->callback)
246 wl_callback_destroy(disp_wl->callback);
247 disp_wl->callback = wl_surface_frame(disp_wl->surface);
249 wl_callback_add_listener(disp_wl->callback, &frame_listener, disp_wl);
251 wl_surface_commit(disp_wl->surface);
252 wl_display_flush(disp_wl->display);
254 while (disp_wl->callback) {
255 wl_display_dispatch(disp_wl->display);
256 }
257 return ret;
258 }
260 static void
261 close_kms(struct display *disp)
262 {
263 omap_device_del(disp->dev);
264 disp->dev = NULL;
265 if (used_planes) {
266 used_planes >>= 1;
267 }
268 if (--ndisplays == 0) {
269 close(global_fd);
270 }
271 }
273 void
274 disp_wayland_usage(void)
275 {
276 MSG("WAYLAND Display Options:");
277 MSG("\t-w <width>x<height>\tset the dimensions of client window");
278 }
280 static void
281 registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
282 const char *interface, uint32_t version)
283 {
284 struct display_wl *disp_wl = (struct display_wl *) data;
286 if (strcmp(interface, "wl_compositor") == 0) {
287 disp_wl->compositor = wl_registry_bind(registry, id,
288 &wl_compositor_interface, 3);
289 } else if (strcmp(interface, "wl_shell") == 0) {
290 disp_wl->shell = wl_registry_bind(registry, id,
291 &wl_shell_interface, 1);
292 } else if (strcmp(interface, "wl_drm") == 0) {
293 disp_wl->drm = wl_registry_bind(registry, id,
294 &wl_drm_interface, 1);
295 } else if (strcmp(interface, "wl_scaler") == 0) {
296 disp_wl->scaler = wl_registry_bind(registry, id,
297 &wl_scaler_interface, 2);
298 }
299 }
301 static void
302 registry_handle_global_remove(void *data, struct wl_registry *registry,
303 uint32_t name)
304 {
305 }
306 static const struct wl_registry_listener registry_listener = {
307 registry_handle_global,
308 registry_handle_global_remove
309 };
312 struct display *
313 disp_wayland_open(int argc, char **argv)
314 {
315 struct display_wl *disp_wl = NULL;
316 struct display *disp;
317 int i, enabled = 0, width, height;
319 struct wl_registry *registry;
320 struct wl_shell_surface *shell_surface;
321 struct wl_region *region;
323 /* note: set args to NULL after we've parsed them so other modules know
324 * that it is already parsed (since the arg parsing is decentralized)
325 */
326 for (i = 1; i < argc; i++) {
327 if (!argv[i]) {
328 continue;
329 }
330 if (!strcmp("-w", argv[i])) {
331 argv[i++] = NULL;
332 if (sscanf(argv[i], "%dx%d", &width, &height) != 2) {
333 ERROR("invalid arg: -w %s", argv[i]);
334 goto fail;
335 }
336 enabled = 1;
337 } else {
338 /* ignore */
339 continue;
340 }
341 argv[i] = NULL;
342 }
343 // If not explicitely enabled from command line, fail.
344 if (!enabled)
345 goto fail;
347 disp_wl = calloc(1, sizeof(*disp_wl));
348 if (!disp_wl) {
349 ERROR("allocation failed");
350 goto fail;
351 }
352 disp = &disp_wl->base;
354 if (!global_fd) {
355 global_fd = drmOpen("omapdrm", NULL);
356 if (global_fd < 0) {
357 ERROR("could not open drm device: %s (%d)",
358 strerror(errno), errno);
359 goto fail;
360 }
361 }
363 disp->fd = global_fd;
364 ndisplays++;
366 disp->dev = omap_device_new(disp->fd);
367 if (!disp->dev) {
368 ERROR("couldn't create device");
369 goto fail;
370 }
372 disp->get_buffers = get_buffers;
373 disp->get_vid_buffers = get_vid_buffers;
374 disp->post_buffer = post_buffer;
375 disp->post_vid_buffer = post_vid_buffer;
376 disp->close = close_kms;
377 disp->disp_free_buf = free_buffers;
379 disp->multiplanar = false;
380 disp->width = width;
381 disp->height = height;
383 disp_wl->bo_flags = OMAP_BO_SCANOUT|OMAP_BO_WC;
385 disp_wl->display = wl_display_connect(NULL);
386 if (disp_wl->display == NULL) {
387 ERROR("failed to connect to Wayland display: %m\n");
388 goto fail;
389 } else {
390 MSG("wayland display opened\n");
391 }
393 /* Find out what interfaces are implemented, and initialize */
394 registry = wl_display_get_registry(disp_wl->display);
395 wl_registry_add_listener(registry, ®istry_listener, disp_wl);
396 wl_display_roundtrip(disp_wl->display);
397 MSG("wayland registries obtained\n");
399 disp_wl->surface = wl_compositor_create_surface(disp_wl->compositor);
400 shell_surface = wl_shell_get_shell_surface(disp_wl->shell,
401 disp_wl->surface);
402 wl_shell_surface_set_toplevel(shell_surface);
404 region = wl_compositor_create_region(disp_wl->compositor);
405 wl_region_add(region, 0, 0, disp->width, disp->height);
407 disp_wl->viewport = wl_scaler_get_viewport(disp_wl->scaler,
408 disp_wl->surface);
409 wl_viewport_set_destination(disp_wl->viewport, width, height);
411 return disp;
413 fail:
414 // XXX cleanup
415 return NULL;
416 }