Fix Second Framebuffer's position calculation
[platform/core/system/initrd-recovery.git] / src / librui / graphics-fbdev.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /*
4  * Copyright (c) 2014 The Android Open Source Project
5  * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <stdbool.h>
27
28 #include <linux/fb.h>
29
30 #include <sys/mman.h>
31 #include <sys/cdefs.h>
32 #include <sys/ioctl.h>
33 #include <sys/types.h>
34
35 #include "fbdev.h"
36 #include "common.h"
37 #include "graphics.h"
38
39 static gr_surface fbdev_init(gr_backend *);
40 static gr_surface fbdev_flip(gr_backend *);
41 static bool fbdev_exit(gr_backend *);
42
43 fbdev_common fb_data = {
44         .fd                     = -1,
45         .displayed_buffer       =  0,
46         .gr_draw                = NULL
47 };
48
49 static gr_backend my_backend = {
50         .init   = fbdev_init,
51         .flip   = fbdev_flip,
52         .exit   = fbdev_exit,
53         .data   = (void *)&fb_data
54 };
55
56 gr_backend *open_fbdev(void) {
57         return &my_backend;
58 }
59
60 static void fbdev_blank(gr_backend *backend, bool blank) {
61         int ret;
62         fbdev_common *fb = (fbdev_common *)backend->data;
63
64         ret = ioctl(fb->fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN :
65                     FB_BLANK_UNBLANK);
66         if (ret < 0)
67                 perror("ioctl(): blank");
68 }
69
70 static void set_displayed_framebuffer(fbdev_common *fb, unsigned int n) {
71         if (n > 1)
72                 return;
73
74         if(n == 0)
75                 fb->vi.yoffset = 0;
76         else
77                 fb->vi.yoffset = fb->vi.yres;
78
79         if (ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi) < 0)
80                 perror("Active fb swap failed");
81
82         fb->displayed_buffer = n;
83 }
84
85 static gr_surface fbdev_init(gr_backend *backend) {
86         size_t bufsize;
87         size_t bufsize_for_mmap;
88         int nr_buffer;
89
90         fbdev_common *fb = (fbdev_common *)backend->data;
91
92         if (!fbdev_init_common(backend))
93                 return NULL;
94
95         nr_buffer = fb->vi.yres_virtual / fb->vi.yres;
96         printf("Frame buffer nr = %d\n", nr_buffer);
97
98         bufsize = fb->fi.line_length * fb->vi.yres;
99         //TODO : Based on Kernel code, following code is right.
100         //     : However, it is strange because of bufsize > bufsize_for_mmap
101         //     : It should be re-checked later with other kernel's framebuffer.
102         bufsize_for_mmap = fb->vi.xres * fb->vi.yres * fb->vi.bits_per_pixel / 8;
103
104         fb->gr_framebuffer[0].data = mmap(0, bufsize_for_mmap * nr_buffer,
105                         PROT_READ | PROT_WRITE, MAP_SHARED,
106                         fb->fd, 0);
107
108         if (fb->gr_framebuffer[0].data == MAP_FAILED) {
109                 perror("Failed to mmap video memory");
110                 fbdev_exit(backend);
111                 return NULL;
112         }
113
114         fb->gr_framebuffer[1].data = fb->gr_framebuffer[0].data + bufsize;
115         gr_fill_alpha(&fb->gr_framebuffer[0]);
116         memcpy(fb->gr_framebuffer[1].data, fb->gr_framebuffer[0].data,
117                         bufsize);
118         set_displayed_framebuffer(fb, 0);
119
120         printf("Framebuffer: %dx%d, %d BPP\n", fb->gr_draw->width,
121                         fb->gr_draw->height, fb->vi.bits_per_pixel);
122
123         fbdev_blank(backend, true);
124         fbdev_blank(backend, false);
125         return fb->gr_draw;
126 }
127
128 static gr_surface fbdev_flip(gr_backend *backend) {
129         fbdev_common *fb = (fbdev_common *)backend->data;
130
131         /* Change gr_draw to point to the buffer currently displayed,
132          * then flip the driver so we're displaying the other buffer
133          * instead. */
134         fb->gr_draw = fb->gr_framebuffer + fb->displayed_buffer;
135         set_displayed_framebuffer(fb, 1 - fb->displayed_buffer);
136         return fb->gr_draw;
137 }
138
139 static bool fbdev_exit(gr_backend *backend) {
140         fbdev_common *fb = (fbdev_common *)backend->data;
141         size_t bufsize = fb->vi.xres * fb->vi.yres * fb->vi.bits_per_pixel / 8;
142
143         /* Unmap double buffer */
144         if (fb->gr_framebuffer[0].data &&
145             munmap(fb->gr_framebuffer[0].data, bufsize * 2) < 0) {
146                 perror("Failed to unmmap video memory");
147                 return false;
148         }
149
150         return fbdev_exit_common(backend);
151 }