OSDN Git Service

add support for debug.drm.mode
[android-x86/external-drm_gralloc.git] / gralloc_drm_kms.c
1 /*
2  * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com>
3  * Copyright (C) 2010-2011 LunarG Inc.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23
24 #define LOG_TAG "GRALLOC-KMS"
25
26 #include <cutils/properties.h>
27 #include <cutils/log.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include "gralloc_drm.h"
34 #include "gralloc_drm_priv.h"
35
36 /*
37  * Return true if a bo needs fb.
38  */
39 int gralloc_drm_bo_need_fb(const struct gralloc_drm_bo_t *bo)
40 {
41         return ((bo->handle->usage & GRALLOC_USAGE_HW_FB) &&
42                 bo->drm->swap_mode != DRM_SWAP_COPY);
43 }
44
45 /*
46  * Add a fb object for a bo.
47  */
48 int gralloc_drm_bo_add_fb(struct gralloc_drm_bo_t *bo)
49 {
50         uint8_t bpp;
51
52         if (bo->fb_id)
53                 return 0;
54
55         bpp = gralloc_drm_get_bpp(bo->handle->format) * 8;
56
57         return drmModeAddFB(bo->drm->fd,
58                         bo->handle->width, bo->handle->height, bpp, bpp,
59                         bo->handle->stride, bo->fb_handle,
60                         (uint32_t *) &bo->fb_id);
61 }
62
63 /*
64  * Remove a fb object for a bo.
65  */
66 void gralloc_drm_bo_rm_fb(struct gralloc_drm_bo_t *bo)
67 {
68         if (bo->fb_id) {
69                 drmModeRmFB(bo->drm->fd, bo->fb_id);
70                 bo->fb_id = 0;
71         }
72 }
73
74 /*
75  * Program CRTC.
76  */
77 static int drm_kms_set_crtc(struct gralloc_drm_t *drm, int fb_id)
78 {
79         int ret;
80
81         ret = drmModeSetCrtc(drm->fd, drm->crtc_id, fb_id,
82                         0, 0, &drm->connector_id, 1, &drm->mode);
83         if (ret) {
84                 LOGE("failed to set crtc");
85                 return ret;
86         }
87
88 #ifdef DRM_MODE_FEATURE_DIRTYFB
89         if (drm->mode_dirty_fb)
90                 ret = drmModeDirtyFB(drm->fd, fb_id, &drm->clip, 1);
91 #endif
92
93         return ret;
94 }
95
96 /*
97  * Callback for a page flip event.
98  */
99 static void page_flip_handler(int fd, unsigned int sequence,
100                 unsigned int tv_sec, unsigned int tv_usec,
101                 void *user_data)
102 {
103         struct gralloc_drm_t *drm = (struct gralloc_drm_t *) user_data;
104
105         /* ack the last scheduled flip */
106         drm->current_front = drm->next_front;
107         drm->next_front = NULL;
108 }
109
110 /*
111  * Schedule a page flip.
112  */
113 static int drm_kms_page_flip(struct gralloc_drm_t *drm,
114                 struct gralloc_drm_bo_t *bo)
115 {
116         int ret;
117
118         /* there is another flip pending */
119         while (drm->next_front) {
120                 drm->waiting_flip = 1;
121                 drmHandleEvent(drm->fd, &drm->evctx);
122                 drm->waiting_flip = 0;
123                 if (drm->next_front) {
124                         /* record an error and break */
125                         LOGE("drmHandleEvent returned without flipping");
126                         drm->current_front = drm->next_front;
127                         drm->next_front = NULL;
128                 }
129         }
130
131         if (!bo)
132                 return 0;
133
134         ret = drmModePageFlip(drm->fd, drm->crtc_id, bo->fb_id,
135                         DRM_MODE_PAGE_FLIP_EVENT, (void *) drm);
136         if (ret)
137                 LOGE("failed to perform page flip");
138         else
139                 drm->next_front = bo;
140
141         return ret;
142 }
143
144 /*
145  * Wait for the next post.
146  */
147 static void drm_kms_wait_for_post(struct gralloc_drm_t *drm, int flip)
148 {
149         unsigned int current, target;
150         drmVBlank vbl;
151         int ret;
152
153         flip = !!flip;
154
155         memset(&vbl, 0, sizeof(vbl));
156         vbl.request.type = DRM_VBLANK_RELATIVE;
157         if (drm->vblank_secondary)
158                 vbl.request.type |= DRM_VBLANK_SECONDARY;
159         vbl.request.sequence = 0;
160
161         /* get the current vblank */
162         ret = drmWaitVBlank(drm->fd, &vbl);
163         if (ret) {
164                 LOGW("failed to get vblank");
165                 return;
166         }
167
168         current = vbl.reply.sequence;
169         if (drm->first_post)
170                 target = current;
171         else
172                 target = drm->last_swap + drm->swap_interval - flip;
173
174         /* wait for vblank */
175         if (current < target || !flip) {
176                 memset(&vbl, 0, sizeof(vbl));
177                 vbl.request.type = DRM_VBLANK_ABSOLUTE;
178                 if (drm->vblank_secondary)
179                         vbl.request.type |= DRM_VBLANK_SECONDARY;
180                 if (!flip) {
181                         vbl.request.type |= DRM_VBLANK_NEXTONMISS;
182                         if (target < current)
183                                 target = current;
184                 }
185
186                 vbl.request.sequence = target;
187
188                 ret = drmWaitVBlank(drm->fd, &vbl);
189                 if (ret) {
190                         LOGW("failed to wait vblank");
191                         return;
192                 }
193         }
194
195         drm->last_swap = vbl.reply.sequence + flip;
196 }
197
198 /*
199  * Post a bo.  This is not thread-safe.
200  */
201 int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
202 {
203         struct gralloc_drm_t *drm = bo->drm;
204         int ret;
205
206         if (!bo->fb_id && drm->swap_mode != DRM_SWAP_COPY) {
207                 LOGE("unable to post bo %p without fb", bo);
208                 return -EINVAL;
209         }
210
211         /* TODO spawn a thread to avoid waiting and race */
212
213         if (drm->first_post) {
214                 if (drm->swap_mode == DRM_SWAP_COPY) {
215                         struct gralloc_drm_bo_t *dst;
216
217                         dst = (drm->next_front) ?
218                                 drm->next_front :
219                                 drm->current_front;
220                         drm->drv->copy(drm->drv, dst, bo, 0, 0,
221                                         bo->handle->width,
222                                         bo->handle->height);
223                         bo = dst;
224                 }
225
226                 drm_kms_wait_for_post(drm, 0);
227                 ret = drm_kms_set_crtc(drm, bo->fb_id);
228                 if (!ret) {
229                         drm->first_post = 0;
230                         drm->current_front = bo;
231                         if (drm->next_front == bo)
232                                 drm->next_front = NULL;
233                 }
234
235                 return ret;
236         }
237
238         switch (drm->swap_mode) {
239         case DRM_SWAP_FLIP:
240                 if (drm->swap_interval > 1)
241                         drm_kms_wait_for_post(drm, 1);
242                 ret = drm_kms_page_flip(drm, bo);
243                 if (drm->next_front) {
244                         /*
245                          * wait if the driver says so or the current front
246                          * will be written by CPU
247                          */
248                         if (drm->mode_sync_flip ||
249                             (drm->current_front->handle->usage &
250                              GRALLOC_USAGE_SW_WRITE_MASK))
251                                 drm_kms_page_flip(drm, NULL);
252                 }
253                 break;
254         case DRM_SWAP_COPY:
255                 drm_kms_wait_for_post(drm, 0);
256                 drm->drv->copy(drm->drv, drm->current_front,
257                                 bo, 0, 0,
258                                 bo->handle->width,
259                                 bo->handle->height);
260                 ret = 0;
261                 break;
262         case DRM_SWAP_SETCRTC:
263                 drm_kms_wait_for_post(drm, 0);
264                 ret = drm_kms_set_crtc(drm, bo->fb_id);
265                 drm->current_front = bo;
266                 break;
267         default:
268                 /* no-op */
269                 ret = 0;
270                 break;
271         }
272
273         return ret;
274 }
275
276 static struct gralloc_drm_t *drm_singleton;
277
278 static void on_signal(int sig)
279 {
280    struct sigaction act;
281
282    /* wait the pending flip */
283    if (drm_singleton && drm_singleton->next_front) {
284       /* there is race, but this function is hacky enough to ignore that */
285       if (drm_singleton->waiting_flip)
286          usleep(100 * 1000); /* 100ms */
287       else
288          drm_kms_page_flip(drm_singleton, NULL);
289    }
290
291    exit(-1);
292 }
293
294 static void drm_kms_init_features(struct gralloc_drm_t *drm)
295 {
296         const char *swap_mode;
297
298         /* call to the driver here, after KMS has been initialized */
299         drm->drv->init_kms_features(drm->drv, drm);
300
301         if (drm->swap_mode == DRM_SWAP_FLIP) {
302                 struct sigaction act;
303
304                 memset(&drm->evctx, 0, sizeof(drm->evctx));
305                 drm->evctx.version = DRM_EVENT_CONTEXT_VERSION;
306                 drm->evctx.page_flip_handler = page_flip_handler;
307
308                 /*
309                  * XXX GPU tends to freeze if the program is terminiated with a
310                  * flip pending.  What is the right way to handle the
311                  * situation?
312                  */
313                 sigemptyset(&act.sa_mask);
314                 act.sa_handler = on_signal;
315                 act.sa_flags = 0;
316                 sigaction(SIGINT, &act, NULL);
317                 sigaction(SIGTERM, &act, NULL);
318
319                 drm_singleton = drm;
320         }
321         else if (drm->swap_mode == DRM_SWAP_COPY) {
322                 struct gralloc_drm_bo_t *front;
323                 int stride;
324
325                 /* create the real front buffer */
326                 front = gralloc_drm_bo_create(drm,
327                                               drm->mode.hdisplay,
328                                               drm->mode.vdisplay,
329                                               drm->fb_format,
330                                               GRALLOC_USAGE_HW_FB);
331                 if (front && gralloc_drm_bo_add_fb(front)) {
332                         gralloc_drm_bo_destroy(front);
333                         front = NULL;
334                 }
335
336                 /* abuse next_front */
337                 if (front)
338                         drm->next_front = front;
339                 else
340                         drm->swap_mode = DRM_SWAP_SETCRTC;
341         }
342
343         switch (drm->swap_mode) {
344         case DRM_SWAP_FLIP:
345                 swap_mode = "flip";
346                 break;
347         case DRM_SWAP_COPY:
348                 swap_mode = "copy";
349                 break;
350         case DRM_SWAP_SETCRTC:
351                 swap_mode = "set-crtc";
352                 break;
353         default:
354                 swap_mode = "no-op";
355                 break;
356         }
357
358         LOGD("will use %s for fb posting", swap_mode);
359 }
360
361 static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp)
362 {
363         char value[PROPERTY_VALUE_MAX];
364         drmModeModeInfoPtr mode;
365         int dist, i;
366         int xres = 0, yres = 0;
367
368         if (property_get("debug.drm.mode", value, NULL)) {
369                 char *p = value, *end;
370
371                 /* parse <xres>x<yres>[@<bpp>] */
372                 if (sscanf(value, "%dx%d@%d", &xres, &yres, bpp) != 3) {
373                         *bpp = 0;
374                         if (sscanf(value, "%dx%d", &xres, &yres) != 2)
375                                 xres = yres = 0;
376                 }
377         }
378         else {
379                 *bpp = 0;
380         }
381
382         mode = NULL;
383         dist = INT_MAX;
384         for (i = 0; i < connector->count_modes; i++) {
385                 drmModeModeInfoPtr m = &connector->modes[i];
386                 int tmp;
387
388                 if (xres && yres) {
389                         tmp = (m->hdisplay - xres) * (m->hdisplay - xres) +
390                                 (m->vdisplay - yres) * (m->vdisplay - yres);
391                 }
392                 else {
393                         /* use the first preferred mode */
394                         tmp = (m->type & DRM_MODE_TYPE_PREFERRED) ? 0 : dist;
395                 }
396
397                 if (tmp < dist) {
398                         mode = m;
399                         dist = tmp;
400                         if (!dist)
401                                 break;
402                 }
403         }
404
405         /* fallback to the first mode */
406         if (!mode)
407                 mode = &connector->modes[0];
408
409         *bpp /= 8;
410
411         return mode;
412 }
413
414 /*
415  * Initialize KMS with a connector.
416  */
417 static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
418                 drmModeConnectorPtr connector)
419 {
420         drmModeEncoderPtr encoder;
421         drmModeModeInfoPtr mode;
422         int bpp, i;
423
424         if (!connector->count_modes)
425                 return -EINVAL;
426
427         encoder = drmModeGetEncoder(drm->fd, connector->encoders[0]);
428         if (!encoder)
429                 return -EINVAL;
430
431         for (i = 0; i < drm->resources->count_crtcs; i++) {
432                 if (encoder->possible_crtcs & (1 << i))
433                         break;
434         }
435         drmModeFreeEncoder(encoder);
436         if (i == drm->resources->count_crtcs)
437                 return -EINVAL;
438
439         drm->crtc_id = drm->resources->crtcs[i];
440         drm->connector_id = connector->connector_id;
441
442         mode = find_mode(connector, &bpp);
443         drm->mode = *mode;
444         switch (bpp) {
445         case 2:
446                 drm->fb_format = HAL_PIXEL_FORMAT_RGB_565;
447                 break;
448         case 4:
449         default:
450                 drm->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
451                 break;
452         }
453
454         if (connector->mmWidth && connector->mmHeight) {
455                 drm->xdpi = (drm->mode.hdisplay * 25.4 / connector->mmWidth);
456                 drm->ydpi = (drm->mode.vdisplay * 25.4 / connector->mmHeight);
457         }
458         else {
459                 drm->xdpi = 75;
460                 drm->ydpi = 75;
461         }
462
463 #ifdef DRM_MODE_FEATURE_DIRTYFB
464         drm->clip.x1 = 0;
465         drm->clip.y1 = 0;
466         drm->clip.x2 = drm->mode.hdisplay;
467         drm->clip.y2 = drm->mode.vdisplay;
468 #endif
469
470         return 0;
471 }
472
473 /*
474  * Initialize KMS.
475  */
476 int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
477 {
478         int i, ret;
479
480         if (drm->resources)
481                 return 0;
482
483         drm->resources = drmModeGetResources(drm->fd);
484         if (!drm->resources) {
485                 LOGE("failed to get modeset resources");
486                 return -EINVAL;
487         }
488
489         /* find the crtc/connector/mode to use */
490         for (i = 0; i < drm->resources->count_connectors; i++) {
491                 drmModeConnectorPtr connector;
492
493                 connector = drmModeGetConnector(drm->fd,
494                                 drm->resources->connectors[i]);
495                 if (connector) {
496                         if (connector->connection == DRM_MODE_CONNECTED) {
497                                 if (!drm_kms_init_with_connector(drm,
498                                                         connector))
499                                         break;
500                         }
501
502                         drmModeFreeConnector(connector);
503                 }
504         }
505         if (i == drm->resources->count_connectors) {
506                 LOGE("failed to find a valid crtc/connector/mode combination");
507                 drmModeFreeResources(drm->resources);
508                 drm->resources = NULL;
509
510                 return -EINVAL;
511         }
512
513         drm_kms_init_features(drm);
514         drm->first_post = 1;
515
516         return 0;
517 }
518
519 /*
520  * Initialize a framebuffer device with KMS info.
521  */
522 void gralloc_drm_get_kms_info(struct gralloc_drm_t *drm,
523                 struct framebuffer_device_t *fb)
524 {
525         *((uint32_t *) &fb->flags) = 0x0;
526         *((uint32_t *) &fb->width) = drm->mode.hdisplay;
527         *((uint32_t *) &fb->height) = drm->mode.vdisplay;
528         *((int *)      &fb->stride) = drm->mode.hdisplay;
529         *((float *)    &fb->fps) = drm->mode.vrefresh;
530
531         *((int *)      &fb->format) = drm->fb_format;
532         *((float *)    &fb->xdpi) = drm->xdpi;
533         *((float *)    &fb->ydpi) = drm->ydpi;
534         *((int *)      &fb->minSwapInterval) = drm->swap_interval;
535         *((int *)      &fb->maxSwapInterval) = drm->swap_interval;
536 }
537
538 /*
539  * Return true if fb posting is pipelined.
540  */
541 int gralloc_drm_is_kms_pipelined(struct gralloc_drm_t *drm)
542 {
543         return (drm->swap_mode != DRM_SWAP_SETCRTC);
544 }