OSDN Git Service

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