uterm: rename drm-dumb backend into drm2d module
[platform/upstream/kmscon.git] / src / uterm_drm2d_video.c
1 /*
2  * uterm - Linux User-Space Terminal drm2d module
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * 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
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * DRM Video backend using dumb buffer objects
28  */
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <inttypes.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/mman.h>
37 #include <unistd.h>
38 #include <xf86drm.h>
39 #include <xf86drmMode.h>
40 #include "eloop.h"
41 #include "log.h"
42 #include "uterm_drm_shared_internal.h"
43 #include "uterm_drm2d_internal.h"
44 #include "uterm_video.h"
45 #include "uterm_video_internal.h"
46
47 #define LOG_SUBSYSTEM "video_drm2d"
48
49 static int display_init(struct uterm_display *disp)
50 {
51         struct uterm_drm2d_display *d2d;
52         int ret;
53
54         d2d = malloc(sizeof(*d2d));
55         if (!d2d)
56                 return -ENOMEM;
57         memset(d2d, 0, sizeof(*d2d));
58
59         ret = uterm_drm_display_init(disp, d2d);
60         if (ret) {
61                 free(d2d);
62                 return ret;
63         }
64
65         return 0;
66 }
67
68 static void display_destroy(struct uterm_display *disp)
69 {
70         free(uterm_drm_display_get_data(disp));
71         uterm_drm_display_destroy(disp);
72 }
73
74 static int init_rb(struct uterm_display *disp, struct uterm_drm2d_rb *rb)
75 {
76         int ret, r;
77         struct uterm_video *video = disp->video;
78         struct uterm_drm_video *vdrm = video->data;
79         struct drm_mode_create_dumb req;
80         struct drm_mode_destroy_dumb dreq;
81         struct drm_mode_map_dumb mreq;
82
83         memset(&req, 0, sizeof(req));
84         req.width = uterm_drm_mode_get_width(disp->current_mode);
85         req.height = uterm_drm_mode_get_height(disp->current_mode);
86         req.bpp = 32;
87         req.flags = 0;
88
89         ret = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &req);
90         if (ret < 0) {
91                 log_err("cannot create dumb drm buffer");
92                 return -EFAULT;
93         }
94
95         rb->handle = req.handle;
96         rb->stride = req.pitch;
97         rb->size = req.size;
98
99         ret = drmModeAddFB(vdrm->fd, req.width, req.height,
100                            24, 32, rb->stride, rb->handle, &rb->fb);
101         if (ret) {
102                 log_err("cannot add drm-fb");
103                 ret = -EFAULT;
104                 goto err_buf;
105         }
106
107         memset(&mreq, 0, sizeof(mreq));
108         mreq.handle = rb->handle;
109
110         ret = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
111         if (ret) {
112                 log_err("cannot map dumb buffer");
113                 ret = -EFAULT;
114                 goto err_fb;
115         }
116
117         rb->map = mmap(0, rb->size, PROT_READ | PROT_WRITE, MAP_SHARED,
118                        vdrm->fd, mreq.offset);
119         if (rb->map == MAP_FAILED) {
120                 log_err("cannot mmap dumb buffer");
121                 ret = -EFAULT;
122                 goto err_fb;
123         }
124         memset(rb->map, 0, rb->size);
125
126         return 0;
127
128 err_fb:
129         drmModeRmFB(vdrm->fd, rb->fb);
130 err_buf:
131         memset(&dreq, 0, sizeof(dreq));
132         dreq.handle = rb->handle;
133         r = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
134         if (r)
135                 log_warning("cannot destroy dumb buffer (%d/%d): %m",
136                             ret, errno);
137
138         return ret;
139 }
140
141 static void destroy_rb(struct uterm_display *disp, struct uterm_drm2d_rb *rb)
142 {
143         struct uterm_drm_video *vdrm = disp->video->data;
144         struct drm_mode_destroy_dumb dreq;
145         int ret;
146
147         munmap(rb->map, rb->size);
148         drmModeRmFB(vdrm->fd, rb->fb);
149         memset(&dreq, 0, sizeof(dreq));
150         dreq.handle = rb->handle;
151         ret = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_DESTROY_DUMB,
152                        &dreq);
153         if (ret)
154                 log_warning("cannot destroy dumb buffer (%d/%d): %m",
155                             ret, errno);
156 }
157
158 static int display_activate(struct uterm_display *disp, struct uterm_mode *mode)
159 {
160         struct uterm_video *video = disp->video;
161         struct uterm_drm_video *vdrm = video->data;
162         struct uterm_drm_display *ddrm = disp->data;
163         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
164         int ret;
165         drmModeModeInfo *minfo;
166
167         if (!mode)
168                 return -EINVAL;
169
170         minfo = uterm_drm_mode_get_info(mode);;
171         log_info("activating display %p to %ux%u", disp,
172                  minfo->hdisplay, minfo->vdisplay);
173
174         ret = uterm_drm_display_activate(disp, vdrm->fd);
175         if (ret)
176                 return ret;
177
178         d2d->current_rb = 0;
179         disp->current_mode = mode;
180
181         ret = init_rb(disp, &d2d->rb[0]);
182         if (ret)
183                 goto err_saved;
184
185         ret = init_rb(disp, &d2d->rb[1]);
186         if (ret)
187                 goto err_rb;
188
189         ret = drmModeSetCrtc(vdrm->fd, ddrm->crtc_id,
190                              d2d->rb[0].fb, 0, 0, &ddrm->conn_id, 1,
191                              minfo);
192         if (ret) {
193                 log_err("cannot set drm-crtc");
194                 ret = -EFAULT;
195                 goto err_fb;
196         }
197
198         disp->flags |= DISPLAY_ONLINE;
199         return 0;
200
201 err_fb:
202         destroy_rb(disp, &d2d->rb[1]);
203 err_rb:
204         destroy_rb(disp, &d2d->rb[0]);
205 err_saved:
206         disp->current_mode = NULL;
207         uterm_drm_display_deactivate(disp, vdrm->fd);
208         return ret;
209 }
210
211 static void display_deactivate(struct uterm_display *disp)
212 {
213         struct uterm_drm_video *vdrm;
214         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
215
216         vdrm = disp->video->data;
217         log_info("deactivating display %p", disp);
218
219         uterm_drm_display_deactivate(disp, vdrm->fd);
220
221         destroy_rb(disp, &d2d->rb[1]);
222         destroy_rb(disp, &d2d->rb[0]);
223         disp->current_mode = NULL;
224 }
225
226 static int display_use(struct uterm_display *disp, bool *opengl)
227 {
228         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
229
230         if (opengl)
231                 *opengl = false;
232
233         return d2d->current_rb ^ 1;
234 }
235
236 static int display_get_buffers(struct uterm_display *disp,
237                                struct uterm_video_buffer *buffer,
238                                unsigned int formats)
239 {
240         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
241         struct uterm_drm2d_rb *rb;
242         int i;
243
244         if (!(formats & UTERM_FORMAT_XRGB32))
245                 return -EOPNOTSUPP;
246
247         for (i = 0; i < 2; ++i) {
248                 rb = &d2d->rb[i];
249                 buffer[i].width = uterm_drm_mode_get_width(disp->current_mode);
250                 buffer[i].height = uterm_drm_mode_get_height(disp->current_mode);
251                 buffer[i].stride = rb->stride;
252                 buffer[i].format = UTERM_FORMAT_XRGB32;
253                 buffer[i].data = rb->map;
254         }
255
256         return 0;
257 }
258
259 static int display_swap(struct uterm_display *disp, bool immediate)
260 {
261         int ret, rb;
262         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
263
264         rb = d2d->current_rb ^ 1;
265         ret = uterm_drm_display_swap(disp, d2d->rb[rb].fb, immediate);
266         if (ret)
267                 return ret;
268
269         d2d->current_rb = rb;
270         return 0;
271 }
272
273 static const struct display_ops drm2d_display_ops = {
274         .init = display_init,
275         .destroy = display_destroy,
276         .activate = display_activate,
277         .deactivate = display_deactivate,
278         .set_dpms = uterm_drm_display_set_dpms,
279         .use = display_use,
280         .get_buffers = display_get_buffers,
281         .swap = display_swap,
282         .blit = uterm_drm2d_display_blit,
283         .fake_blendv = uterm_drm2d_display_fake_blendv,
284         .fill = uterm_drm2d_display_fill,
285 };
286
287 static void show_displays(struct uterm_video *video)
288 {
289         struct uterm_display *iter;
290         struct uterm_drm2d_display *d2d;
291         struct uterm_drm2d_rb *rb;
292         struct shl_dlist *i;
293
294         if (!video_is_awake(video))
295                 return;
296
297         shl_dlist_for_each(i, &video->displays) {
298                 iter = shl_dlist_entry(i, struct uterm_display, list);
299
300                 if (!display_is_online(iter))
301                         continue;
302                 if (iter->dpms != UTERM_DPMS_ON)
303                         continue;
304
305                 /* We use double-buffering so there might be no free back-buffer
306                  * here. Hence, draw into the current (pending) front-buffer and
307                  * wait for possible page-flips to complete. This might cause
308                  * tearing but that's acceptable as this is only called during
309                  * wakeup/sleep. */
310
311                 d2d = uterm_drm_display_get_data(iter);
312                 rb = &d2d->rb[d2d->current_rb];
313                 memset(rb->map, 0, rb->size);
314                 uterm_drm_display_wait_pflip(iter);
315         }
316 }
317
318 static int video_init(struct uterm_video *video, const char *node)
319 {
320         int ret;
321         uint64_t has_dumb;
322         struct uterm_drm_video *vdrm;
323
324         ret = uterm_drm_video_init(video, node, NULL, NULL);
325         if (ret)
326                 return ret;
327         vdrm = video->data;
328
329         log_debug("initialize 2D layer on %p", video);
330
331         if (drmGetCap(vdrm->fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 ||
332             !has_dumb) {
333                 log_err("driver does not support dumb buffers");
334                 uterm_drm_video_destroy(video);
335                 return -EOPNOTSUPP;
336         }
337
338         return 0;
339 }
340
341 static void video_destroy(struct uterm_video *video)
342 {
343         log_info("free drm video device %p", video);
344         uterm_drm_video_destroy(video);
345 }
346
347 static int video_poll(struct uterm_video *video)
348 {
349         return uterm_drm_video_poll(video, &drm2d_display_ops);
350 }
351
352 static void video_sleep(struct uterm_video *video)
353 {
354         show_displays(video);
355         uterm_drm_video_sleep(video);
356 }
357
358 static int video_wake_up(struct uterm_video *video)
359 {
360         int ret;
361
362         ret = uterm_drm_video_wake_up(video, &drm2d_display_ops);
363         if (ret)
364                 return ret;
365
366         show_displays(video);
367         return 0;
368 }
369
370 static const struct video_ops drm2d_video_ops = {
371         .init = video_init,
372         .destroy = video_destroy,
373         .segfault = NULL, /* TODO: reset all saved CRTCs on segfault */
374         .poll = video_poll,
375         .sleep = video_sleep,
376         .wake_up = video_wake_up,
377 };
378
379 static const struct uterm_video_module drm2d_module = {
380         .ops = &drm2d_video_ops,
381 };
382
383 const struct uterm_video_module *UTERM_VIDEO_DRM2D = &drm2d_module;