uterm: drm: move display_ops into vdrm object
[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 "shl_log.h"
42 #include "shl_misc.h"
43 #include "uterm_drm_shared_internal.h"
44 #include "uterm_drm2d_internal.h"
45 #include "uterm_video.h"
46 #include "uterm_video_internal.h"
47
48 #define LOG_SUBSYSTEM "video_drm2d"
49
50 static int display_init(struct uterm_display *disp)
51 {
52         struct uterm_drm2d_display *d2d;
53         int ret;
54
55         d2d = malloc(sizeof(*d2d));
56         if (!d2d)
57                 return -ENOMEM;
58         memset(d2d, 0, sizeof(*d2d));
59
60         ret = uterm_drm_display_init(disp, d2d);
61         if (ret) {
62                 free(d2d);
63                 return ret;
64         }
65
66         return 0;
67 }
68
69 static void display_destroy(struct uterm_display *disp)
70 {
71         free(uterm_drm_display_get_data(disp));
72         uterm_drm_display_destroy(disp);
73 }
74
75 static int init_rb(struct uterm_display *disp, struct uterm_drm2d_rb *rb)
76 {
77         int ret, r;
78         struct uterm_video *video = disp->video;
79         struct uterm_drm_video *vdrm = video->data;
80         struct drm_mode_create_dumb req;
81         struct drm_mode_destroy_dumb dreq;
82         struct drm_mode_map_dumb mreq;
83
84         memset(&req, 0, sizeof(req));
85         req.width = uterm_drm_mode_get_width(disp->current_mode);
86         req.height = uterm_drm_mode_get_height(disp->current_mode);
87         req.bpp = 32;
88         req.flags = 0;
89
90         ret = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &req);
91         if (ret < 0) {
92                 log_err("cannot create dumb drm buffer");
93                 return -EFAULT;
94         }
95
96         rb->handle = req.handle;
97         rb->stride = req.pitch;
98         rb->size = req.size;
99
100         ret = drmModeAddFB(vdrm->fd, req.width, req.height,
101                            24, 32, rb->stride, rb->handle, &rb->fb);
102         if (ret) {
103                 log_err("cannot add drm-fb");
104                 ret = -EFAULT;
105                 goto err_buf;
106         }
107
108         memset(&mreq, 0, sizeof(mreq));
109         mreq.handle = rb->handle;
110
111         ret = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
112         if (ret) {
113                 log_err("cannot map dumb buffer");
114                 ret = -EFAULT;
115                 goto err_fb;
116         }
117
118         rb->map = mmap(0, rb->size, PROT_READ | PROT_WRITE, MAP_SHARED,
119                        vdrm->fd, mreq.offset);
120         if (rb->map == MAP_FAILED) {
121                 log_err("cannot mmap dumb buffer");
122                 ret = -EFAULT;
123                 goto err_fb;
124         }
125         memset(rb->map, 0, rb->size);
126
127         return 0;
128
129 err_fb:
130         drmModeRmFB(vdrm->fd, rb->fb);
131 err_buf:
132         memset(&dreq, 0, sizeof(dreq));
133         dreq.handle = rb->handle;
134         r = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
135         if (r)
136                 log_warning("cannot destroy dumb buffer (%d/%d): %m",
137                             ret, errno);
138
139         return ret;
140 }
141
142 static void destroy_rb(struct uterm_display *disp, struct uterm_drm2d_rb *rb)
143 {
144         struct uterm_drm_video *vdrm = disp->video->data;
145         struct drm_mode_destroy_dumb dreq;
146         int ret;
147
148         munmap(rb->map, rb->size);
149         drmModeRmFB(vdrm->fd, rb->fb);
150         memset(&dreq, 0, sizeof(dreq));
151         dreq.handle = rb->handle;
152         ret = drmIoctl(vdrm->fd, DRM_IOCTL_MODE_DESTROY_DUMB,
153                        &dreq);
154         if (ret)
155                 log_warning("cannot destroy dumb buffer (%d/%d): %m",
156                             ret, errno);
157 }
158
159 static int display_activate(struct uterm_display *disp, struct uterm_mode *mode)
160 {
161         struct uterm_video *video = disp->video;
162         struct uterm_drm_video *vdrm = video->data;
163         struct uterm_drm_display *ddrm = disp->data;
164         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
165         int ret;
166         drmModeModeInfo *minfo;
167
168         if (!mode)
169                 return -EINVAL;
170
171         minfo = uterm_drm_mode_get_info(mode);;
172         log_info("activating display %p to %ux%u", disp,
173                  minfo->hdisplay, minfo->vdisplay);
174
175         ret = uterm_drm_display_activate(disp, vdrm->fd);
176         if (ret)
177                 return ret;
178
179         d2d->current_rb = 0;
180         disp->current_mode = mode;
181
182         ret = init_rb(disp, &d2d->rb[0]);
183         if (ret)
184                 goto err_saved;
185
186         ret = init_rb(disp, &d2d->rb[1]);
187         if (ret)
188                 goto err_rb;
189
190         ret = drmModeSetCrtc(vdrm->fd, ddrm->crtc_id,
191                              d2d->rb[0].fb, 0, 0, &ddrm->conn_id, 1,
192                              minfo);
193         if (ret) {
194                 log_err("cannot set drm-crtc");
195                 ret = -EFAULT;
196                 goto err_fb;
197         }
198
199         disp->flags |= DISPLAY_ONLINE;
200         return 0;
201
202 err_fb:
203         destroy_rb(disp, &d2d->rb[1]);
204 err_rb:
205         destroy_rb(disp, &d2d->rb[0]);
206 err_saved:
207         disp->current_mode = NULL;
208         uterm_drm_display_deactivate(disp, vdrm->fd);
209         return ret;
210 }
211
212 static void display_deactivate(struct uterm_display *disp)
213 {
214         struct uterm_drm_video *vdrm;
215         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
216
217         vdrm = disp->video->data;
218         log_info("deactivating display %p", disp);
219
220         uterm_drm_display_deactivate(disp, vdrm->fd);
221
222         destroy_rb(disp, &d2d->rb[1]);
223         destroy_rb(disp, &d2d->rb[0]);
224         disp->current_mode = NULL;
225 }
226
227 static int display_use(struct uterm_display *disp, bool *opengl)
228 {
229         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
230
231         if (opengl)
232                 *opengl = false;
233
234         return d2d->current_rb ^ 1;
235 }
236
237 static int display_get_buffers(struct uterm_display *disp,
238                                struct uterm_video_buffer *buffer,
239                                unsigned int formats)
240 {
241         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
242         struct uterm_drm2d_rb *rb;
243         int i;
244
245         if (!(formats & UTERM_FORMAT_XRGB32))
246                 return -EOPNOTSUPP;
247
248         for (i = 0; i < 2; ++i) {
249                 rb = &d2d->rb[i];
250                 buffer[i].width = uterm_drm_mode_get_width(disp->current_mode);
251                 buffer[i].height = uterm_drm_mode_get_height(disp->current_mode);
252                 buffer[i].stride = rb->stride;
253                 buffer[i].format = UTERM_FORMAT_XRGB32;
254                 buffer[i].data = rb->map;
255         }
256
257         return 0;
258 }
259
260 static int display_swap(struct uterm_display *disp, bool immediate)
261 {
262         int ret, rb;
263         struct uterm_drm2d_display *d2d = uterm_drm_display_get_data(disp);
264
265         rb = d2d->current_rb ^ 1;
266         ret = uterm_drm_display_swap(disp, d2d->rb[rb].fb, immediate);
267         if (ret)
268                 return ret;
269
270         d2d->current_rb = rb;
271         return 0;
272 }
273
274 static const struct display_ops drm2d_display_ops = {
275         .init = display_init,
276         .destroy = display_destroy,
277         .activate = display_activate,
278         .deactivate = display_deactivate,
279         .set_dpms = uterm_drm_display_set_dpms,
280         .use = display_use,
281         .get_buffers = display_get_buffers,
282         .swap = display_swap,
283         .blit = uterm_drm2d_display_blit,
284         .fake_blendv = uterm_drm2d_display_fake_blendv,
285         .fill = uterm_drm2d_display_fill,
286 };
287
288 static void show_displays(struct uterm_video *video)
289 {
290         struct uterm_display *iter;
291         struct uterm_drm2d_display *d2d;
292         struct uterm_drm2d_rb *rb;
293         struct shl_dlist *i;
294
295         if (!video_is_awake(video))
296                 return;
297
298         shl_dlist_for_each(i, &video->displays) {
299                 iter = shl_dlist_entry(i, struct uterm_display, list);
300
301                 if (!display_is_online(iter))
302                         continue;
303                 if (iter->dpms != UTERM_DPMS_ON)
304                         continue;
305
306                 /* We use double-buffering so there might be no free back-buffer
307                  * here. Hence, draw into the current (pending) front-buffer and
308                  * wait for possible page-flips to complete. This might cause
309                  * tearing but that's acceptable as this is only called during
310                  * wakeup/sleep. */
311
312                 d2d = uterm_drm_display_get_data(iter);
313                 rb = &d2d->rb[d2d->current_rb];
314                 memset(rb->map, 0, rb->size);
315                 uterm_drm_display_wait_pflip(iter);
316         }
317 }
318
319 static int video_init(struct uterm_video *video, const char *node)
320 {
321         int ret;
322         uint64_t has_dumb;
323         struct uterm_drm_video *vdrm;
324
325         ret = uterm_drm_video_init(video, node, &drm2d_display_ops,
326                                    NULL, NULL);
327         if (ret)
328                 return ret;
329         vdrm = video->data;
330
331         log_debug("initialize 2D layer on %p", video);
332
333         if (drmGetCap(vdrm->fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 ||
334             !has_dumb) {
335                 log_err("driver does not support dumb buffers");
336                 uterm_drm_video_destroy(video);
337                 return -EOPNOTSUPP;
338         }
339
340         return 0;
341 }
342
343 static void video_destroy(struct uterm_video *video)
344 {
345         log_info("free drm video device %p", video);
346         uterm_drm_video_destroy(video);
347 }
348
349 static int video_poll(struct uterm_video *video)
350 {
351         return uterm_drm_video_poll(video);
352 }
353
354 static void video_sleep(struct uterm_video *video)
355 {
356         show_displays(video);
357         uterm_drm_video_sleep(video);
358 }
359
360 static int video_wake_up(struct uterm_video *video)
361 {
362         int ret;
363
364         ret = uterm_drm_video_wake_up(video);
365         if (ret)
366                 return ret;
367
368         show_displays(video);
369         return 0;
370 }
371
372 static const struct video_ops drm2d_video_ops = {
373         .init = video_init,
374         .destroy = video_destroy,
375         .segfault = NULL, /* TODO: reset all saved CRTCs on segfault */
376         .poll = video_poll,
377         .sleep = video_sleep,
378         .wake_up = video_wake_up,
379 };
380
381 static const struct uterm_video_module drm2d_module = {
382         .ops = &drm2d_video_ops,
383 };
384
385 SHL_EXPORT
386 const struct uterm_video_module *UTERM_VIDEO_DRM2D = &drm2d_module;