OSDN Git Service

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