OSDN Git Service

4c9871f065b87d61562991735bb23f4e796047ac
[android-x86/external-drm_framebuffer.git] / drm_framebuffer.c
1 /*
2  * Copyright (c) 2017 lambdadroid (https://github.com/lambdadroid)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22
23 #define LOG_TAG "drm-fb"
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <hardware/gralloc.h>
30 #include <log/log.h>
31
32 #include <drm/drm_fourcc.h>
33 #include <xf86drm.h>
34 #include <xf86drmMode.h>
35
36 #include "drm_framebuffer.h"
37 #include <android/gralloc_handle.h>
38
39 #define SWAP_INTERVAL 1
40
41 struct drm_framebuffer {
42         struct framebuffer_device_t device;
43
44         int fd;
45         uint32_t connector_id, crtc_id;
46         drmModeModeInfo mode;
47
48         uint32_t current_fb, next_fb;
49         drmEventContext evctx;
50         struct drm_framebuffer **fb_out;
51 };
52
53 static drmModeConnectorPtr fb0_find_connector(int fd, drmModeResPtr res)
54 {
55         drmModeConnectorPtr connector;
56         int i;
57
58         connector = NULL;
59         for (i = 0; i < res->count_connectors; ++i) {
60                 connector = drmModeGetConnector(fd, res->connectors[i]);
61                 if (connector->connection == DRM_MODE_CONNECTED) {
62                         break;
63                 }
64
65                 drmModeFreeConnector(connector);
66                 connector = NULL;
67         }
68
69         return connector;
70 }
71
72 static uint32_t fb0_find_crtc(int fd, drmModeResPtr res, drmModeConnectorPtr connector)
73 {
74         drmModeEncoderPtr encoder;
75         int i;
76
77         encoder = drmModeGetEncoder(fd, connector->encoders[0]);
78         for (i = 0; i < res->count_crtcs; ++i) {
79                 if (encoder->possible_crtcs & (1 << i)) {
80                         drmModeFreeEncoder(encoder);
81                         return res->crtcs[i];
82                 }
83         }
84
85         drmModeFreeEncoder(encoder);
86         return 0;
87 }
88
89 static drmModeModeInfoPtr fb0_find_preferred_mode(drmModeConnectorPtr connector)
90 {
91         int i;
92         drmModeModeInfoPtr mode = NULL;
93
94         for (i = 0; i < connector->count_modes; ++i) {
95                 mode = &connector->modes[i];
96                 if (mode->type & DRM_MODE_TYPE_PREFERRED) {
97                         break;
98                 }
99         }
100
101         return mode;
102 }
103
104 static void fb0_handle_page_flip(
105         __unused int fd, __unused unsigned int sequence,
106         __unused unsigned int tv_sec, __unused unsigned int tv_usec,
107         __unused void *data)
108 {
109         struct drm_framebuffer *fb = data;
110         fb->current_fb = fb->next_fb;
111         fb->next_fb = 0;
112 }
113
114 static int fb0_init(struct drm_framebuffer *fb)
115 {
116         drmModeResPtr res;
117         drmModeConnectorPtr connector;
118         drmModeModeInfoPtr mode;
119
120         res = drmModeGetResources(fb->fd);
121
122         connector = fb0_find_connector(fb->fd, res);
123         if (!connector) {
124                 ALOGE("No connector found");
125                 drmModeFreeResources(res);
126                 return -ENODEV;
127         }
128
129         fb->connector_id = connector->connector_id;
130
131         fb->crtc_id = fb0_find_crtc(fb->fd, res, connector);
132         drmModeFreeResources(res);
133         if (!fb->crtc_id) {
134                 ALOGE("No CRTC found");
135                 return -ENODEV;
136         }
137
138         ALOGI("Connector: %d, CRTC: %d", fb->connector_id, fb->crtc_id);
139
140         mode = fb0_find_preferred_mode(connector);
141         if (!mode) {
142                 ALOGE("No preferred mode found");
143                 drmModeFreeConnector(connector);
144                 return -ENODEV;
145         }
146
147         fb->mode = *mode;
148         fb->current_fb = 0;
149         fb->next_fb = 0;
150
151         *(uint32_t*) &fb->device.flags = 0;
152         *(uint32_t*) &fb->device.width = mode->hdisplay;
153         *(uint32_t*) &fb->device.height = mode->vdisplay;
154         *(int*) &fb->device.stride = mode->vdisplay;
155         /* Note: The format specified here seems to be entirely ignored... */
156         *(int*) &fb->device.format = HAL_PIXEL_FORMAT_RGBA_8888;
157         *(float*) &fb->device.xdpi = mode->hdisplay * 25.4 / connector->mmWidth;
158         *(float*) &fb->device.ydpi = mode->vdisplay * 25.4 / connector->mmHeight;
159         *(float*) &fb->device.fps = mode->vrefresh;
160         *(int*) &fb->device.minSwapInterval = SWAP_INTERVAL;
161         *(int*) &fb->device.maxSwapInterval = SWAP_INTERVAL;
162
163         memset(&fb->evctx, 0, sizeof(fb->evctx));
164         fb->evctx.version = DRM_EVENT_CONTEXT_VERSION;
165         fb->evctx.page_flip_handler = fb0_handle_page_flip;
166
167         drmModeFreeConnector(connector);
168         return 0;
169 }
170
171 static void fb0_await_page_flip(struct drm_framebuffer *fb)
172 {
173         if (fb->next_fb) {
174                 /* There is another flip pending */
175                 drmHandleEvent(fb->fd, &fb->evctx);
176                 if (fb->next_fb) {
177                         ALOGE("drmHandleEvent returned without flipping");
178                         fb->current_fb = fb->next_fb;
179                         fb->next_fb = 0;
180                 }
181         }
182 }
183
184 static int fb0_page_flip(struct drm_framebuffer *fb, int fb_id)
185 {
186         int ret;
187
188         /* Finish current page flip */
189         fb0_await_page_flip(fb);
190
191         ret = drmModePageFlip(fb->fd, fb->crtc_id, fb_id,
192                 DRM_MODE_PAGE_FLIP_EVENT, fb);
193         if (ret) {
194                 ALOGE("Failed to perform page flip: %d", ret);
195                 if (errno != -EBUSY) {
196                         fb->current_fb = 0;
197                 }
198                 return errno;
199         } else {
200                 fb->next_fb = fb_id;
201         }
202
203         return 0;
204 }
205
206 static int fb0_enable_crtc(struct drm_framebuffer *fb, uint32_t fb_id)
207 {
208         int ret = drmModeSetCrtc(fb->fd, fb->crtc_id, fb_id, 0, 0,
209                         &fb->connector_id, 1, &fb->mode);
210         if (ret) {
211                 ALOGE("Failed to enable CRTC: %d", ret);
212         } else {
213                 fb->current_fb = fb_id;
214         }
215
216         return ret;
217 }
218
219 static int fb0_disable_crtc(struct drm_framebuffer *fb)
220 {
221         int ret;
222
223         /* Finish current page flip */
224         fb0_await_page_flip(fb);
225
226         ret = drmModeSetCrtc(fb->fd, fb->crtc_id, 0, 0, 0, NULL, 0, NULL);
227         if (ret) {
228                 ALOGE("Failed to disable CRTC: %d", ret);
229         } else {
230                 fb->current_fb = 0;
231         }
232
233         return ret;
234 }
235
236 static int fb0_post(struct framebuffer_device_t *fbdev, buffer_handle_t buffer)
237 {
238         struct drm_framebuffer *fb = (struct drm_framebuffer *) fbdev;
239         struct gralloc_handle_t *handle = gralloc_handle(buffer);
240         uint32_t fb_id;
241         if (!handle) {
242                 return -EINVAL;
243         }
244
245         fb_id = (uint32_t) handle->data;
246         if (!fb_id) {
247                 return -EINVAL;
248         }
249
250         if (fb->current_fb == fb_id) {
251                 /* Already current */
252                 return 0;
253         }
254
255         if (fb->current_fb) {
256                 return fb0_page_flip(fb, fb_id);
257         } else {
258                 return fb0_enable_crtc(fb, fb_id);
259         }
260 }
261
262 static int fb0_enable_screen(struct framebuffer_device_t *fbdev, int enable)
263 {
264         struct drm_framebuffer *fb = (struct drm_framebuffer *) fbdev;
265         ALOGI("Updating screen state: %d", enable);
266
267         /* Only need to disable screen here, will be re-enabled with next post */
268         if (!enable && fb->current_fb) {
269                 return fb0_disable_crtc(fb);
270         } else {
271                 return 0;
272         }
273 }
274
275 static int fb0_composition_complete(__unused struct framebuffer_device_t *dev)
276 {
277         return 0;
278 }
279
280 static int fb0_set_swap_interval(
281         __unused struct framebuffer_device_t *window, int interval)
282 {
283         if (interval != SWAP_INTERVAL) {
284                 return -EINVAL;
285         }
286         return 0;
287 }
288
289 static int fb0_close(struct hw_device_t *dev)
290 {
291         struct drm_framebuffer *fb = (struct drm_framebuffer *) dev;
292         *fb->fb_out = NULL;
293
294         free(dev);
295         return 0;
296 }
297
298 int drm_framebuffer_open(int fd, struct drm_framebuffer **fb_out, struct hw_device_t **dev)
299 {
300         struct drm_framebuffer *fb;
301         int ret;
302
303         fb = calloc(1, sizeof(*fb));
304         if (!fb) {
305                 return -ENOMEM;
306         }
307
308         fb->fd = fd;
309         ret = fb0_init(fb);
310         if (ret) {
311                 free(fb);
312                 return ret;
313         }
314
315         fb->device.common.tag = HARDWARE_DEVICE_TAG;
316         fb->device.common.version = 0;
317         fb->device.common.close = fb0_close;
318
319         fb->device.setSwapInterval = fb0_set_swap_interval;
320         fb->device.post = fb0_post;
321         fb->device.compositionComplete = fb0_composition_complete;
322         fb->device.enableScreen = fb0_enable_screen;
323         fb->fb_out = fb_out;
324
325         *fb_out = fb;
326         *dev = &fb->device.common;
327         return 0;
328 }
329
330 static uint32_t convert_android_to_drm_fb_format(uint32_t format)
331 {
332         switch (format) {
333         case HAL_PIXEL_FORMAT_RGBA_8888:
334                 /* Avoid using alpha bits for the framebuffer.
335                  * They are not supported on older Intel GPUs for primary planes. */
336         case HAL_PIXEL_FORMAT_RGBX_8888:
337                 return DRM_FORMAT_XBGR8888;
338         case HAL_PIXEL_FORMAT_RGB_888:
339                 return DRM_FORMAT_BGR888;
340         case HAL_PIXEL_FORMAT_RGB_565:
341                 return DRM_FORMAT_BGR565;
342         case HAL_PIXEL_FORMAT_BGRA_8888:
343                 return DRM_FORMAT_ARGB8888;
344         default:
345                 ALOGE("Unsupported framebuffer format: %u", format);
346                 return 0;
347         }
348 }
349
350 static int fb0_add_fb(struct drm_framebuffer *fb, struct gralloc_handle_t *handle, uint32_t handle_id)
351 {
352         uint32_t pitches[4] = { handle->stride, 0, 0, 0 };
353         uint32_t offsets[4] = { 0, 0, 0, 0 };
354         uint32_t handles[4] = { handle_id, 0, 0, 0 };
355
356         return drmModeAddFB2(fb->fd, handle->width, handle->height,
357                 convert_android_to_drm_fb_format(handle->format),
358                 handles, pitches, offsets, (uint32_t*) &handle->data, 0);
359 }
360
361 void drm_framebuffer_import(struct drm_framebuffer *fb, buffer_handle_t buffer)
362 {
363         struct gralloc_handle_t *handle = gralloc_handle(buffer);
364         uint32_t handle_id;
365
366         /* Ignore buffers that are not intended for usage with the framebuffer */
367         if (!(handle->usage & GRALLOC_USAGE_HW_FB)) {
368                 return;
369         }
370
371         /* Lookup the handle for the prime fd.
372          * (The buffer should have already been imported by the gralloc HAL) */
373         if (drmPrimeFDToHandle(fb->fd, handle->prime_fd, &handle_id)) {
374                 ALOGE("Failed to get handle from prime fd: %d", errno);
375                 return;
376         }
377
378         /* Add a framebuffer to the handle */
379         fb0_add_fb(fb, handle, handle_id);
380 }