wlt: fix shl_hook API changes
[platform/upstream/kmscon.git] / src / uterm_fbdev_video.c
1 /*
2  * uterm - Linux User-Space Terminal fbdev 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  * FBDEV Video backend
28  */
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <linux/fb.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <unistd.h>
39 #include "log.h"
40 #include "uterm_fbdev_internal.h"
41 #include "uterm_video.h"
42 #include "uterm_video_internal.h"
43
44 #define LOG_SUBSYSTEM "video_fbdev"
45
46 static int mode_init(struct uterm_mode *mode)
47 {
48         struct fbdev_mode *fbdev;
49
50         fbdev = malloc(sizeof(*fbdev));
51         if (!fbdev)
52                 return -ENOMEM;
53         memset(fbdev, 0, sizeof(*fbdev));
54         mode->data = fbdev;
55
56         return 0;
57 }
58
59 static void mode_destroy(struct uterm_mode *mode)
60 {
61         free(mode->data);
62 }
63
64 static const char *mode_get_name(const struct uterm_mode *mode)
65 {
66         return "<default>";
67 }
68
69 static unsigned int mode_get_width(const struct uterm_mode *mode)
70 {
71         struct fbdev_mode *fbdev = mode->data;
72
73         return fbdev->width;
74 }
75
76 static unsigned int mode_get_height(const struct uterm_mode *mode)
77 {
78         struct fbdev_mode *fbdev = mode->data;
79
80         return fbdev->height;
81 }
82
83 static const struct mode_ops fbdev_mode_ops = {
84         .init = mode_init,
85         .destroy = mode_destroy,
86         .get_name = mode_get_name,
87         .get_width = mode_get_width,
88         .get_height = mode_get_height,
89 };
90
91 static int display_init(struct uterm_display *disp)
92 {
93         struct fbdev_display *fbdev;
94
95         fbdev = malloc(sizeof(*fbdev));
96         if (!fbdev)
97                 return -ENOMEM;
98         memset(fbdev, 0, sizeof(*fbdev));
99         disp->data = fbdev;
100         disp->dpms = UTERM_DPMS_UNKNOWN;
101
102         return 0;
103 }
104
105 static void display_destroy(struct uterm_display *disp)
106 {
107         free(disp->data);
108 }
109
110 static int refresh_info(struct uterm_display *disp)
111 {
112         int ret;
113         struct fbdev_display *dfb = disp->data;
114
115         ret = ioctl(dfb->fd, FBIOGET_FSCREENINFO, &dfb->finfo);
116         if (ret) {
117                 log_err("cannot get finfo (%d): %m", errno);
118                 return -EFAULT;
119         }
120
121         ret = ioctl(dfb->fd, FBIOGET_VSCREENINFO, &dfb->vinfo);
122         if (ret) {
123                 log_err("cannot get vinfo (%d): %m", errno);
124                 return -EFAULT;
125         }
126
127         return 0;
128 }
129
130 static int display_activate_force(struct uterm_display *disp,
131                                   struct uterm_mode *mode,
132                                   bool force)
133 {
134         /* TODO: Add support for 24-bpp. However, we need to check how 3-bytes
135          * integers are assembled in big/little/mixed endian systems. */
136         static const char depths[] = { 32, 16, 0 };
137         struct fbdev_display *dfb = disp->data;
138         struct uterm_mode *m;
139         struct fbdev_mode *mfb;
140         struct fb_var_screeninfo *vinfo;
141         struct fb_fix_screeninfo *finfo;
142         int ret, i;
143         uint64_t quot;
144         size_t len;
145         unsigned int val;
146
147         if (!force && (disp->flags & DISPLAY_ONLINE))
148                 return 0;
149
150         /* TODO: We do not support explicit modesetting in fbdev, so we require
151          * @mode to be NULL. You can still switch modes via "fbset" on the
152          * console and then restart the app. It will automatically adapt to the
153          * new mode. The only values changed here are bpp and color mode. */
154         if (mode)
155                 return -EINVAL;
156
157         dfb->fd = open(dfb->node, O_RDWR | O_CLOEXEC | O_NONBLOCK);
158         if (dfb->fd < 0) {
159                 log_err("cannot open %s (%d): %m", dfb->node, errno);
160                 return -EFAULT;
161         }
162
163         ret = refresh_info(disp);
164         if (ret)
165                 goto err_close;
166
167         finfo = &dfb->finfo;
168         vinfo = &dfb->vinfo;
169
170         vinfo->xoffset = 0;
171         vinfo->yoffset = 0;
172         vinfo->activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
173         vinfo->xres_virtual = vinfo->xres;
174         vinfo->yres_virtual = vinfo->yres * 2;
175         disp->flags |= DISPLAY_DBUF;
176
177         /* udlfb is broken as it reports the sizes of the virtual framebuffer
178          * (even mmap() accepts it) but the actual size that we can access
179          * without segfaults is the _real_ framebuffer. Therefore, disable
180          * double-buffering for it.
181          * TODO: fix this kernel-side!
182          * TODO: There are so many broken fbdev drivers that just accept any
183          * virtual FB sizes and then break mmap that we now disable
184          * double-buffering entirely. We might instead add a white-list or
185          * optional command-line argument to re-enable it. */
186         if (true || !strcmp(finfo->id, "udlfb")) {
187                 disp->flags &= ~DISPLAY_DBUF;
188                 vinfo->yres_virtual = vinfo->yres;
189         }
190
191         ret = ioctl(dfb->fd, FBIOPUT_VSCREENINFO, vinfo);
192         if (ret) {
193                 disp->flags &= ~DISPLAY_DBUF;
194                 vinfo->yres_virtual = vinfo->yres;
195                 ret = ioctl(dfb->fd, FBIOPUT_VSCREENINFO, vinfo);
196                 if (ret) {
197                         log_debug("cannot reset fb offsets (%d): %m", errno);
198                         goto err_close;
199                 }
200         }
201
202         if (disp->flags & DISPLAY_DBUF)
203                 log_debug("enable double buffering");
204         else
205                 log_debug("disable double buffering");
206
207         ret = refresh_info(disp);
208         if (ret)
209                 goto err_close;
210
211         /* We require TRUECOLOR mode here. That is, each pixel has a color value
212          * that is split into rgba values that we can set directly. Other visual
213          * modes like pseudocolor or direct-color do not provide this. As I have
214          * never seen a device that does not support TRUECOLOR, I think we can
215          * ignore them here. */
216         if (finfo->visual != FB_VISUAL_TRUECOLOR ||
217             vinfo->bits_per_pixel != 32) {
218                 for (i = 0; depths[i]; ++i) {
219                         vinfo->bits_per_pixel = depths[i];
220                         vinfo->activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
221
222                         ret = ioctl(dfb->fd, FBIOPUT_VSCREENINFO,
223                                     vinfo);
224                         if (ret < 0)
225                                 continue;
226
227                         ret = refresh_info(disp);
228                         if (ret)
229                                 goto err_close;
230
231                         if (finfo->visual == FB_VISUAL_TRUECOLOR)
232                                 break;
233                 }
234         }
235
236         if (vinfo->bits_per_pixel != 32 &&
237             vinfo->bits_per_pixel != 16) {
238                 log_error("device %s does not support 16/32 bpp but: %u",
239                           dfb->node, vinfo->bits_per_pixel);
240                 ret = -EFAULT;
241                 goto err_close;
242         }
243
244         if (vinfo->xres_virtual < vinfo->xres ||
245             (disp->flags & DISPLAY_DBUF &&
246              vinfo->yres_virtual < vinfo->yres * 2) ||
247             vinfo->yres_virtual < vinfo->yres) {
248                 log_warning("device %s has weird virtual buffer sizes (%d %d %d %d)",
249                             dfb->node, vinfo->xres, vinfo->xres_virtual,
250                             vinfo->yres, vinfo->yres_virtual);
251         }
252
253         if (finfo->visual != FB_VISUAL_TRUECOLOR) {
254                 log_error("device %s does not support true-color",
255                           dfb->node);
256                 ret = -EFAULT;
257                 goto err_close;
258         }
259
260         if (vinfo->red.length > 8 ||
261             vinfo->green.length > 8 ||
262             vinfo->blue.length > 8) {
263                 log_error("device %s uses unusual color-ranges",
264                           dfb->node);
265                 ret = -EFAULT;
266                 goto err_close;
267         }
268
269         log_info("activating display %s to %ux%u %u bpp", dfb->node,
270                  vinfo->xres, vinfo->yres, vinfo->bits_per_pixel);
271
272         /* calculate monitor rate, default is 60 Hz */
273         quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
274         quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
275         quot *= vinfo->pixclock;
276         if (quot) {
277                 dfb->rate = 1000000000000000LLU / quot;
278         } else {
279                 dfb->rate = 60 * 1000;
280                 log_warning("cannot read monitor refresh rate, forcing 60 Hz");
281         }
282
283         if (dfb->rate == 0) {
284                 log_warning("monitor refresh rate is 0 Hz, forcing it to 1 Hz");
285                 dfb->rate = 1;
286         } else if (dfb->rate > 200000) {
287                 log_warning("monitor refresh rate is >200 Hz (%u Hz), forcing it to 200 Hz",
288                             dfb->rate / 1000);
289                 dfb->rate = 200000;
290         }
291
292         val = 1000000 / dfb->rate;
293         display_set_vblank_timer(disp, val);
294         log_debug("vblank timer: %u ms, monitor refresh rate: %u Hz", val,
295                   dfb->rate / 1000);
296
297         len = finfo->line_length * vinfo->yres;
298         if (disp->flags & DISPLAY_DBUF)
299                 len *= 2;
300
301         dfb->map = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, dfb->fd, 0);
302         if (dfb->map == MAP_FAILED) {
303                 log_error("cannot mmap device %s (%d): %m", dfb->node,
304                           errno);
305                 ret = -EFAULT;
306                 goto err_close;
307         }
308
309         memset(dfb->map, 0, len);
310         dfb->xres = vinfo->xres;
311         dfb->yres = vinfo->yres;
312         dfb->len = len;
313         dfb->stride = finfo->line_length;
314         dfb->bufid = 0;
315         dfb->Bpp = vinfo->bits_per_pixel / 8;
316         dfb->off_r = vinfo->red.offset;
317         dfb->len_r = vinfo->red.length;
318         dfb->off_g = vinfo->green.offset;
319         dfb->len_g = vinfo->green.length;
320         dfb->off_b = vinfo->blue.offset;
321         dfb->len_b = vinfo->blue.length;
322         dfb->dither_r = 0;
323         dfb->dither_g = 0;
324         dfb->dither_b = 0;
325         dfb->xrgb32 = false;
326         dfb->rgb16 = false;
327         if (dfb->len_r == 8 && dfb->len_g == 8 && dfb->len_b == 8 &&
328             dfb->off_r == 16 && dfb->off_g ==  8 && dfb->off_b ==  0 &&
329             dfb->Bpp == 4)
330                 dfb->xrgb32 = true;
331         else if (dfb->len_r == 5 && dfb->len_g == 6 && dfb->len_b == 5 &&
332                  dfb->off_r == 11 && dfb->off_g == 5 && dfb->off_b == 0 &&
333                  dfb->Bpp == 2)
334                 dfb->rgb16 = true;
335
336         /* TODO: make dithering configurable */
337         disp->flags |= DISPLAY_DITHERING;
338
339         if (disp->current_mode) {
340                 m = disp->current_mode;
341         } else {
342                 ret = mode_new(&m, &fbdev_mode_ops);
343                 if (ret)
344                         goto err_map;
345                 ret = uterm_mode_bind(m, disp);
346                 if (ret) {
347                         uterm_mode_unref(m);
348                         goto err_map;
349                 }
350                 disp->current_mode = m;
351                 uterm_mode_unref(m);
352         }
353
354         mfb = m->data;
355         mfb->width = dfb->xres;
356         mfb->height = dfb->yres;
357
358         disp->flags |= DISPLAY_ONLINE;
359         return 0;
360
361 err_map:
362         munmap(dfb->map, dfb->len);
363 err_close:
364         close(dfb->fd);
365         return ret;
366 }
367
368 static int display_activate(struct uterm_display *disp, struct uterm_mode *mode)
369 {
370         return display_activate_force(disp, mode, false);
371 }
372
373 static void display_deactivate_force(struct uterm_display *disp, bool force)
374 {
375         struct fbdev_display *dfb = disp->data;
376
377         log_info("deactivating device %s", dfb->node);
378
379         if (dfb->map) {
380                 memset(dfb->map, 0, dfb->len);
381                 munmap(dfb->map, dfb->len);
382                 close(dfb->fd);
383                 dfb->map = NULL;
384         }
385         if (!force) {
386                 uterm_mode_unbind(disp->current_mode);
387                 disp->current_mode = NULL;
388                 disp->flags &= ~DISPLAY_ONLINE;
389         }
390 }
391
392 static void display_deactivate(struct uterm_display *disp)
393 {
394         return display_deactivate_force(disp, false);
395 }
396
397 static int display_set_dpms(struct uterm_display *disp, int state)
398 {
399         int set, ret;
400         struct fbdev_display *dfb = disp->data;
401
402         switch (state) {
403         case UTERM_DPMS_ON:
404                 set = FB_BLANK_UNBLANK;
405                 break;
406         case UTERM_DPMS_STANDBY:
407                 set = FB_BLANK_NORMAL;
408                 break;
409         case UTERM_DPMS_SUSPEND:
410                 set = FB_BLANK_NORMAL;
411                 break;
412         case UTERM_DPMS_OFF:
413                 set = FB_BLANK_POWERDOWN;
414                 break;
415         default:
416                 return -EINVAL;
417         }
418
419         log_info("setting DPMS of device %p to %s", dfb->node,
420                  uterm_dpms_to_name(state));
421
422         ret = ioctl(dfb->fd, FBIOBLANK, set);
423         if (ret) {
424                 log_error("cannot set DPMS on %s (%d): %m", dfb->node,
425                           errno);
426                 return -EFAULT;
427         }
428
429         disp->dpms = state;
430         return 0;
431 }
432
433 static int display_use(struct uterm_display *disp, bool *opengl)
434 {
435         struct fbdev_display *dfb = disp->data;
436
437         if (opengl)
438                 *opengl = false;
439
440         if (!(disp->flags & DISPLAY_DBUF))
441                 return 0;
442
443         return dfb->bufid ^ 1;
444 }
445
446 static int display_get_buffers(struct uterm_display *disp,
447                                struct uterm_video_buffer *buffer,
448                                unsigned int formats)
449 {
450         struct fbdev_display *dfb = disp->data;
451         unsigned int f = 0, i;
452
453         if (dfb->xrgb32)
454                 f = UTERM_FORMAT_XRGB32;
455         else if (dfb->rgb16)
456                 f = UTERM_FORMAT_RGB16;
457
458         if (!(formats & f))
459                 return -EOPNOTSUPP;
460
461         for (i = 0; i < 2; ++i) {
462                 buffer[i].width = dfb->xres;
463                 buffer[i].height = dfb->yres;
464                 buffer[i].stride = dfb->stride;
465                 buffer[i].format = f;
466                 if (!(disp->flags & DISPLAY_DBUF) || !i)
467                         buffer[i].data = dfb->map;
468                 else
469                         buffer[i].data = &dfb->map[dfb->yres * dfb->stride];
470         }
471
472         return 0;
473 }
474
475 static int display_swap(struct uterm_display *disp, bool immediate)
476 {
477         struct fbdev_display *dfb = disp->data;
478         struct fb_var_screeninfo *vinfo;
479         int ret;
480
481         if (!(disp->flags & DISPLAY_DBUF)) {
482                 if (immediate)
483                         return 0;
484                 return display_schedule_vblank_timer(disp);
485         }
486
487         vinfo = &dfb->vinfo;
488         if (immediate)
489                 vinfo->activate = FB_ACTIVATE_NOW;
490         else
491                 vinfo->activate = FB_ACTIVATE_VBL;
492
493         if (!dfb->bufid)
494                 vinfo->yoffset = dfb->yres;
495         else
496                 vinfo->yoffset = 0;
497
498         ret = ioctl(dfb->fd, FBIOPUT_VSCREENINFO, vinfo);
499         if (ret) {
500                 log_warning("cannot swap buffers on %s (%d): %m",
501                             dfb->node, errno);
502                 return -EFAULT;
503         }
504
505         dfb->bufid ^= 1;
506         return display_schedule_vblank_timer(disp);
507 }
508
509 static const struct display_ops fbdev_display_ops = {
510         .init = display_init,
511         .destroy = display_destroy,
512         .activate = display_activate,
513         .deactivate = display_deactivate,
514         .set_dpms = display_set_dpms,
515         .use = display_use,
516         .get_buffers = display_get_buffers,
517         .swap = display_swap,
518         .blit = uterm_fbdev_display_blit,
519         .fake_blendv = uterm_fbdev_display_fake_blendv,
520         .fill = uterm_fbdev_display_fill,
521 };
522
523 static void intro_idle_event(struct ev_eloop *eloop, void *unused, void *data)
524 {
525         struct uterm_video *video = data;
526         struct fbdev_video *vfb = video->data;
527         struct uterm_display *disp;
528         struct fbdev_display *dfb;
529         int ret;
530
531         vfb->pending_intro = false;
532         ev_eloop_unregister_idle_cb(eloop, intro_idle_event, data, EV_NORMAL);
533
534         ret = display_new(&disp, &fbdev_display_ops);
535         if (ret) {
536                 log_error("cannot create fbdev display: %d", ret);
537                 return;
538         }
539
540         dfb = disp->data;
541         dfb->node = vfb->node;
542         ret = uterm_display_bind(disp, video);
543         if (ret) {
544                 log_error("cannot bind fbdev display: %d", ret);
545                 uterm_display_unref(disp);
546                 return;
547         }
548
549         uterm_display_unref(disp);
550 }
551
552 static int video_init(struct uterm_video *video, const char *node)
553 {
554         int ret;
555         struct fbdev_video *vfb;
556
557         log_info("new device on %s", node);
558
559         vfb = malloc(sizeof(*vfb));
560         if (!vfb)
561                 return -ENOMEM;
562         memset(vfb, 0, sizeof(*vfb));
563         video->data = vfb;
564
565         vfb->node = strdup(node);
566         if (!vfb->node) {
567                 ret = -ENOMEM;
568                 goto err_free;
569         }
570
571         ret = ev_eloop_register_idle_cb(video->eloop, intro_idle_event, video,
572                                         EV_NORMAL);
573         if (ret) {
574                 log_error("cannot register idle event: %d", ret);
575                 goto err_node;
576         }
577         vfb->pending_intro = true;
578
579         return 0;
580
581 err_node:
582         free(vfb->node);
583 err_free:
584         free(vfb);
585         return ret;
586 }
587
588 static void video_destroy(struct uterm_video *video)
589 {
590         struct fbdev_video *vfb = video->data;
591
592         log_info("free device on %s", vfb->node);
593
594         if (vfb->pending_intro)
595                 ev_eloop_unregister_idle_cb(video->eloop, intro_idle_event,
596                                             video, EV_NORMAL);
597
598         free(vfb->node);
599         free(vfb);
600 }
601
602 static void video_sleep(struct uterm_video *video)
603 {
604         struct uterm_display *iter;
605         struct shl_dlist *i;
606
607         shl_dlist_for_each(i, &video->displays) {
608                 iter = shl_dlist_entry(i, struct uterm_display, list);
609
610                 if (!display_is_online(iter))
611                         continue;
612
613                 display_deactivate_force(iter, true);
614         }
615 }
616
617 static int video_wake_up(struct uterm_video *video)
618 {
619         struct uterm_display *iter;
620         struct shl_dlist *i;
621         int ret;
622
623         video->flags |= VIDEO_AWAKE;
624         shl_dlist_for_each(i, &video->displays) {
625                 iter = shl_dlist_entry(i, struct uterm_display, list);
626
627                 if (!display_is_online(iter))
628                         continue;
629
630                 ret = display_activate_force(iter, NULL, true);
631                 if (ret)
632                         return ret;
633
634                 if (iter->dpms != UTERM_DPMS_UNKNOWN)
635                         display_set_dpms(iter, iter->dpms);
636         }
637
638         return 0;
639 }
640
641 static const struct video_ops fbdev_video_ops = {
642         .init = video_init,
643         .destroy = video_destroy,
644         .segfault = NULL, /* TODO */
645         .poll = NULL,
646         .sleep = video_sleep,
647         .wake_up = video_wake_up,
648 };
649
650 static const struct uterm_video_module fbdev_module = {
651         .ops = &fbdev_video_ops,
652 };
653
654 const struct uterm_video_module *UTERM_VIDEO_FBDEV = &fbdev_module;