OSDN Git Service

Change all occurances of LOGE to ALOGE, LOGW to ALOGW and LOGI to ALOGI due to change...
[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                 ALOGE("failed to set crtc");
85                 return ret;
86         }
87
88         if (drm->mode_quirk_vmwgfx)
89                 ret = drmModeDirtyFB(drm->fd, fb_id, &drm->clip, 1);
90
91         return ret;
92 }
93
94 /*
95  * Callback for a page flip event.
96  */
97 static void page_flip_handler(int fd, unsigned int sequence,
98                 unsigned int tv_sec, unsigned int tv_usec,
99                 void *user_data)
100 {
101         struct gralloc_drm_t *drm = (struct gralloc_drm_t *) user_data;
102
103         /* ack the last scheduled flip */
104         drm->current_front = drm->next_front;
105         drm->next_front = NULL;
106 }
107
108 /*
109  * Schedule a page flip.
110  */
111 static int drm_kms_page_flip(struct gralloc_drm_t *drm,
112                 struct gralloc_drm_bo_t *bo)
113 {
114         int ret;
115
116         /* there is another flip pending */
117         while (drm->next_front) {
118                 drm->waiting_flip = 1;
119                 drmHandleEvent(drm->fd, &drm->evctx);
120                 drm->waiting_flip = 0;
121                 if (drm->next_front) {
122                         /* record an error and break */
123                         ALOGE("drmHandleEvent returned without flipping");
124                         drm->current_front = drm->next_front;
125                         drm->next_front = NULL;
126                 }
127         }
128
129         if (!bo)
130                 return 0;
131
132         ret = drmModePageFlip(drm->fd, drm->crtc_id, bo->fb_id,
133                         DRM_MODE_PAGE_FLIP_EVENT, (void *) drm);
134         if (ret)
135                 ALOGE("failed to perform page flip");
136         else
137                 drm->next_front = bo;
138
139         return ret;
140 }
141
142 /*
143  * Wait for the next post.
144  */
145 static void drm_kms_wait_for_post(struct gralloc_drm_t *drm, int flip)
146 {
147         unsigned int current, target;
148         drmVBlank vbl;
149         int ret;
150
151         if (drm->mode_quirk_vmwgfx)
152                 return;
153
154         flip = !!flip;
155
156         memset(&vbl, 0, sizeof(vbl));
157         vbl.request.type = DRM_VBLANK_RELATIVE;
158         if (drm->vblank_secondary)
159                 vbl.request.type |= DRM_VBLANK_SECONDARY;
160         vbl.request.sequence = 0;
161
162         /* get the current vblank */
163         ret = drmWaitVBlank(drm->fd, &vbl);
164         if (ret) {
165                 ALOGW("failed to get vblank");
166                 return;
167         }
168
169         current = vbl.reply.sequence;
170         if (drm->first_post)
171                 target = current;
172         else
173                 target = drm->last_swap + drm->swap_interval - flip;
174
175         /* wait for vblank */
176         if (current < target || !flip) {
177                 memset(&vbl, 0, sizeof(vbl));
178                 vbl.request.type = DRM_VBLANK_ABSOLUTE;
179                 if (drm->vblank_secondary)
180                         vbl.request.type |= DRM_VBLANK_SECONDARY;
181                 if (!flip) {
182                         vbl.request.type |= DRM_VBLANK_NEXTONMISS;
183                         if (target < current)
184                                 target = current;
185                 }
186
187                 vbl.request.sequence = target;
188
189                 ret = drmWaitVBlank(drm->fd, &vbl);
190                 if (ret) {
191                         ALOGW("failed to wait vblank");
192                         return;
193                 }
194         }
195
196         drm->last_swap = vbl.reply.sequence + flip;
197 }
198
199 /*
200  * Post a bo.  This is not thread-safe.
201  */
202 int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
203 {
204         struct gralloc_drm_t *drm = bo->drm;
205         int ret;
206
207         if (!bo->fb_id && drm->swap_mode != DRM_SWAP_COPY) {
208                 ALOGE("unable to post bo %p without fb", bo);
209                 return -EINVAL;
210         }
211
212         /* TODO spawn a thread to avoid waiting and race */
213
214         if (drm->first_post) {
215                 if (drm->swap_mode == DRM_SWAP_COPY) {
216                         struct gralloc_drm_bo_t *dst;
217
218                         dst = (drm->next_front) ?
219                                 drm->next_front :
220                                 drm->current_front;
221                         drm->drv->copy(drm->drv, dst, bo, 0, 0,
222                                         bo->handle->width,
223                                         bo->handle->height);
224                         bo = dst;
225                 }
226
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                 if (drm->mode_quirk_vmwgfx)
261                         ret = drmModeDirtyFB(drm->fd, drm->current_front->fb_id, &drm->clip, 1);
262                 ret = 0;
263                 break;
264         case DRM_SWAP_SETCRTC:
265                 drm_kms_wait_for_post(drm, 0);
266                 ret = drm_kms_set_crtc(drm, bo->fb_id);
267                 drm->current_front = bo;
268                 break;
269         default:
270                 /* no-op */
271                 ret = 0;
272                 break;
273         }
274
275         return ret;
276 }
277
278 static struct gralloc_drm_t *drm_singleton;
279
280 static void on_signal(int sig)
281 {
282         struct gralloc_drm_t *drm = drm_singleton;
283
284         /* wait the pending flip */
285         if (drm && drm->swap_mode == DRM_SWAP_FLIP && drm->next_front) {
286                 /* there is race, but this function is hacky enough to ignore that */
287                 if (drm_singleton->waiting_flip)
288                         usleep(100 * 1000); /* 100ms */
289                 else
290                         drm_kms_page_flip(drm_singleton, NULL);
291         }
292
293         exit(-1);
294 }
295
296 static void drm_kms_init_features(struct gralloc_drm_t *drm)
297 {
298         const char *swap_mode;
299
300         /* call to the driver here, after KMS has been initialized */
301         drm->drv->init_kms_features(drm->drv, drm);
302
303         if (drm->swap_mode == DRM_SWAP_FLIP) {
304                 struct sigaction act;
305
306                 memset(&drm->evctx, 0, sizeof(drm->evctx));
307                 drm->evctx.version = DRM_EVENT_CONTEXT_VERSION;
308                 drm->evctx.page_flip_handler = page_flip_handler;
309
310                 /*
311                  * XXX GPU tends to freeze if the program is terminiated with a
312                  * flip pending.  What is the right way to handle the
313                  * situation?
314                  */
315                 sigemptyset(&act.sa_mask);
316                 act.sa_handler = on_signal;
317                 act.sa_flags = 0;
318                 sigaction(SIGINT, &act, NULL);
319                 sigaction(SIGTERM, &act, NULL);
320
321                 drm_singleton = drm;
322         }
323         else if (drm->swap_mode == DRM_SWAP_COPY) {
324                 struct gralloc_drm_bo_t *front;
325                 int stride;
326
327                 /* create the real front buffer */
328                 front = gralloc_drm_bo_create(drm,
329                                               drm->mode.hdisplay,
330                                               drm->mode.vdisplay,
331                                               drm->fb_format,
332                                               GRALLOC_USAGE_HW_FB);
333                 if (front && gralloc_drm_bo_add_fb(front)) {
334                         gralloc_drm_bo_destroy(front);
335                         front = NULL;
336                 }
337
338                 /* abuse next_front */
339                 if (front)
340                         drm->next_front = front;
341                 else
342                         drm->swap_mode = DRM_SWAP_SETCRTC;
343         }
344
345         switch (drm->swap_mode) {
346         case DRM_SWAP_FLIP:
347                 swap_mode = "flip";
348                 break;
349         case DRM_SWAP_COPY:
350                 swap_mode = "copy";
351                 break;
352         case DRM_SWAP_SETCRTC:
353                 swap_mode = "set-crtc";
354                 break;
355         default:
356                 swap_mode = "no-op";
357                 break;
358         }
359
360         ALOGD("will use %s for fb posting", swap_mode);
361 }
362
363 static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp)
364 {
365         char value[PROPERTY_VALUE_MAX];
366         drmModeModeInfoPtr mode;
367         int dist, i;
368         int xres = 0, yres = 0;
369
370         if (property_get("debug.drm.mode", value, NULL)) {
371                 char *p = value, *end;
372
373                 /* parse <xres>x<yres>[@<bpp>] */
374                 if (sscanf(value, "%dx%d@%d", &xres, &yres, bpp) != 3) {
375                         *bpp = 0;
376                         if (sscanf(value, "%dx%d", &xres, &yres) != 2)
377                                 xres = yres = 0;
378                 }
379
380                 if ((xres && yres) || *bpp) {
381                         ALOGI("will find the closest match for %dx%d@%d",
382                                         xres, yres, *bpp);
383                 }
384         }
385         else {
386                 *bpp = 0;
387         }
388
389         mode = NULL;
390         dist = INT_MAX;
391         for (i = 0; i < connector->count_modes; i++) {
392                 drmModeModeInfoPtr m = &connector->modes[i];
393                 int tmp;
394
395                 if (xres && yres) {
396                         tmp = (m->hdisplay - xres) * (m->hdisplay - xres) +
397                                 (m->vdisplay - yres) * (m->vdisplay - yres);
398                 }
399                 else {
400                         /* use the first preferred mode */
401                         tmp = (m->type & DRM_MODE_TYPE_PREFERRED) ? 0 : dist;
402                 }
403
404                 if (tmp < dist) {
405                         mode = m;
406                         dist = tmp;
407                         if (!dist)
408                                 break;
409                 }
410         }
411
412         /* fallback to the first mode */
413         if (!mode)
414                 mode = &connector->modes[0];
415
416         *bpp /= 8;
417
418         return mode;
419 }
420
421 /*
422  * Initialize KMS with a connector.
423  */
424 static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
425                 drmModeConnectorPtr connector)
426 {
427         drmModeEncoderPtr encoder;
428         drmModeModeInfoPtr mode;
429         int bpp, i;
430
431         if (!connector->count_modes)
432                 return -EINVAL;
433
434         encoder = drmModeGetEncoder(drm->fd, connector->encoders[0]);
435         if (!encoder)
436                 return -EINVAL;
437
438         for (i = 0; i < drm->resources->count_crtcs; i++) {
439                 if (encoder->possible_crtcs & (1 << i))
440                         break;
441         }
442         drmModeFreeEncoder(encoder);
443         if (i == drm->resources->count_crtcs)
444                 return -EINVAL;
445
446         drm->crtc_id = drm->resources->crtcs[i];
447         drm->connector_id = connector->connector_id;
448
449         /* print connector info */
450         if (connector->count_modes > 1) {
451                 ALOGI("there are %d modes on connector 0x%x",
452                                 connector->count_modes,
453                                 connector->connector_id);
454                 for (i = 0; i < connector->count_modes; i++)
455                         ALOGI("  %s", connector->modes[i].name);
456         }
457         else {
458                 ALOGI("there is one mode on connector 0x%d: %s",
459                                 connector->connector_id,
460                                 connector->modes[0].name);
461         }
462
463         mode = find_mode(connector, &bpp);
464
465         ALOGI("the best mode is %s", mode->name);
466
467         drm->mode = *mode;
468         switch (bpp) {
469         case 2:
470                 drm->fb_format = HAL_PIXEL_FORMAT_RGB_565;
471                 break;
472         case 4:
473         default:
474                 drm->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
475                 break;
476         }
477
478         if (connector->mmWidth && connector->mmHeight) {
479                 drm->xdpi = (drm->mode.hdisplay * 25.4 / connector->mmWidth);
480                 drm->ydpi = (drm->mode.vdisplay * 25.4 / connector->mmHeight);
481         }
482         else {
483                 drm->xdpi = 75;
484                 drm->ydpi = 75;
485         }
486
487 #ifdef DRM_MODE_FEATURE_DIRTYFB
488         drm->clip.x1 = 0;
489         drm->clip.y1 = 0;
490         drm->clip.x2 = drm->mode.hdisplay;
491         drm->clip.y2 = drm->mode.vdisplay;
492 #endif
493
494         return 0;
495 }
496
497 /*
498  * Initialize KMS.
499  */
500 int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
501 {
502         int i, ret;
503
504         if (drm->resources)
505                 return 0;
506
507         drm->resources = drmModeGetResources(drm->fd);
508         if (!drm->resources) {
509                 ALOGE("failed to get modeset resources");
510                 return -EINVAL;
511         }
512
513         /* find the crtc/connector/mode to use */
514         for (i = 0; i < drm->resources->count_connectors; i++) {
515                 drmModeConnectorPtr connector;
516
517                 connector = drmModeGetConnector(drm->fd,
518                                 drm->resources->connectors[i]);
519                 if (connector) {
520                         if (connector->connection == DRM_MODE_CONNECTED) {
521                                 if (!drm_kms_init_with_connector(drm,
522                                                         connector))
523                                         break;
524                         }
525
526                         drmModeFreeConnector(connector);
527                 }
528         }
529         if (i == drm->resources->count_connectors) {
530                 ALOGE("failed to find a valid crtc/connector/mode combination");
531                 drmModeFreeResources(drm->resources);
532                 drm->resources = NULL;
533
534                 return -EINVAL;
535         }
536
537         drm_kms_init_features(drm);
538         drm->first_post = 1;
539
540         return 0;
541 }
542
543 void gralloc_drm_fini_kms(struct gralloc_drm_t *drm)
544 {
545         switch (drm->swap_mode) {
546         case DRM_SWAP_FLIP:
547                 drm_kms_page_flip(drm, NULL);
548                 break;
549         case DRM_SWAP_COPY:
550                 {
551                         struct gralloc_drm_bo_t **bo = (drm->current_front) ?
552                                 &drm->current_front : &drm->next_front;
553
554                         if (*bo)
555                                 gralloc_drm_bo_destroy(*bo);
556                         *bo = NULL;
557                 }
558                 break;
559         default:
560                 break;
561         }
562
563         /* restore crtc? */
564
565         if (drm->resources) {
566                 drmModeFreeResources(drm->resources);
567                 drm->resources = NULL;
568         }
569
570         drm_singleton = NULL;
571 }
572
573 int gralloc_drm_is_kms_initialized(struct gralloc_drm_t *drm)
574 {
575         return (drm->resources != NULL);
576 }
577
578 /*
579  * Initialize a framebuffer device with KMS info.
580  */
581 void gralloc_drm_get_kms_info(struct gralloc_drm_t *drm,
582                 struct framebuffer_device_t *fb)
583 {
584         *((uint32_t *) &fb->flags) = 0x0;
585         *((uint32_t *) &fb->width) = drm->mode.hdisplay;
586         *((uint32_t *) &fb->height) = drm->mode.vdisplay;
587         *((int *)      &fb->stride) = drm->mode.hdisplay;
588         *((float *)    &fb->fps) = drm->mode.vrefresh;
589
590         *((int *)      &fb->format) = drm->fb_format;
591         *((float *)    &fb->xdpi) = drm->xdpi;
592         *((float *)    &fb->ydpi) = drm->ydpi;
593         *((int *)      &fb->minSwapInterval) = drm->swap_interval;
594         *((int *)      &fb->maxSwapInterval) = drm->swap_interval;
595 }
596
597 /*
598  * Return true if fb posting is pipelined.
599  */
600 int gralloc_drm_is_kms_pipelined(struct gralloc_drm_t *drm)
601 {
602         return (drm->swap_mode != DRM_SWAP_SETCRTC);
603 }