OSDN Git Service

gralloc: add NV12 format enum and fb allocation support for it
[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 <poll.h>
34 #include "gralloc_drm.h"
35 #include "gralloc_drm_priv.h"
36 #include <hardware_legacy/uevent.h>
37
38 #include <drm_fourcc.h>
39
40 /*
41  * Return true if a bo needs fb.
42  */
43 int gralloc_drm_bo_need_fb(const struct gralloc_drm_bo_t *bo)
44 {
45         return ((bo->handle->usage & GRALLOC_USAGE_HW_FB) &&
46                 bo->drm->swap_mode != DRM_SWAP_COPY);
47 }
48
49 static unsigned int drm_format_from_hal(int hal_format)
50 {
51         switch(hal_format) {
52                 case HAL_PIXEL_FORMAT_RGB_888:
53                 case HAL_PIXEL_FORMAT_RGBX_8888:
54                 case HAL_PIXEL_FORMAT_BGRA_8888:
55                         return DRM_FORMAT_XRGB8888;
56                 case HAL_PIXEL_FORMAT_RGBA_8888:
57                         return DRM_FORMAT_RGBA8888;
58                 case HAL_PIXEL_FORMAT_RGB_565:
59                         return DRM_FORMAT_RGB565;
60                 case HAL_PIXEL_FORMAT_YV12:
61                         return DRM_FORMAT_YUV420;
62                 case HAL_PIXEL_FORMAT_DRM_NV12:
63                         return DRM_FORMAT_NV12;
64                 default:
65                         return 0;
66         }
67 }
68
69 /*
70  * Modify pitches, offsets and handles according to
71  * the format and return corresponding drm format value
72  */
73 static int resolve_drm_format(struct gralloc_drm_bo_t *bo,
74         uint32_t *pitches, uint32_t *offsets, uint32_t *handles)
75 {
76         memset(pitches, 0, 4 * sizeof(uint32_t));
77         memset(offsets, 0, 4 * sizeof(uint32_t));
78         memset(handles, 0, 4 * sizeof(uint32_t));
79
80         pitches[0] = bo->handle->stride;
81         handles[0] = bo->fb_handle;
82
83         int format = drm_format_from_hal(bo->handle->format);
84
85         // handle 'special formats'
86         switch(bo->handle->format) {
87                 case HAL_PIXEL_FORMAT_YV12:
88
89                         // U and V stride are half of Y plane
90                         pitches[2] = pitches[0]/2;
91                         pitches[1] = pitches[0]/2;
92
93                         // like I420 but U and V are in reverse order
94                         offsets[2] = offsets[0] +
95                                 pitches[0] * bo->handle->height;
96                         offsets[1] = offsets[2] +
97                                 pitches[2] * bo->handle->height/2;
98
99                         handles[1] = handles[2] = handles[0];
100                         break;
101
102                 case HAL_PIXEL_FORMAT_DRM_NV12:
103
104                         // U and V are interleaved in 2nd plane
105                         pitches[1] = pitches[0];
106                         offsets[1] = offsets[0] +
107                                 pitches[0] * bo->handle->height;
108                         handles[1] = handles[0];
109                         break;
110         }
111         return format;
112 }
113
114 /*
115  * Add a fb object for a bo.
116  */
117 int gralloc_drm_bo_add_fb(struct gralloc_drm_bo_t *bo)
118 {
119         uint32_t pitches[4];
120         uint32_t offsets[4];
121         uint32_t handles[4];
122
123         if (bo->fb_id)
124                 return 0;
125
126         int drm_format = resolve_drm_format(bo, pitches, offsets, handles);
127
128         if (drm_format == 0) {
129                 ALOGE("error resolving drm format");
130                 return -EINVAL;
131         }
132
133         return drmModeAddFB2(bo->drm->fd,
134                 bo->handle->width, bo->handle->height,
135                 drm_format, handles, pitches, offsets,
136                 (uint32_t *) &bo->fb_id, 0);
137 }
138
139 /*
140  * Remove a fb object for a bo.
141  */
142 void gralloc_drm_bo_rm_fb(struct gralloc_drm_bo_t *bo)
143 {
144         if (bo->fb_id) {
145                 drmModeRmFB(bo->drm->fd, bo->fb_id);
146                 bo->fb_id = 0;
147         }
148 }
149
150 /*
151  * Program CRTC.
152  */
153 static int drm_kms_set_crtc(struct gralloc_drm_t *drm,
154         struct gralloc_drm_output *output, int fb_id)
155 {
156         int ret;
157
158         ret = drmModeSetCrtc(drm->fd, output->crtc_id, fb_id,
159                         0, 0, &output->connector_id, 1, &output->mode);
160         if (ret) {
161                 ALOGE("failed to set crtc (%s) (crtc_id %d, fb_id %d, conn %d, mode %dx%d)",
162                         strerror(errno), output->crtc_id, fb_id, output->connector_id,
163                         output->mode.hdisplay, output->mode.vdisplay);
164                 return ret;
165         }
166
167         if (drm->mode_quirk_vmwgfx)
168                 ret = drmModeDirtyFB(drm->fd, fb_id, &drm->clip, 1);
169
170         return ret;
171 }
172
173 /*
174  * Callback for a page flip event.
175  */
176 static void page_flip_handler(int fd, unsigned int sequence,
177                 unsigned int tv_sec, unsigned int tv_usec,
178                 void *user_data)
179 {
180         struct gralloc_drm_t *drm = (struct gralloc_drm_t *) user_data;
181
182         /* ack the last scheduled flip */
183         drm->current_front = drm->next_front;
184         drm->next_front = NULL;
185 }
186
187 /*
188  * Schedule a page flip.
189  */
190 static int drm_kms_page_flip(struct gralloc_drm_t *drm,
191                 struct gralloc_drm_bo_t *bo)
192 {
193         int ret;
194
195         /* there is another flip pending */
196         while (drm->next_front) {
197                 drm->waiting_flip = 1;
198                 drmHandleEvent(drm->fd, &drm->evctx);
199                 drm->waiting_flip = 0;
200                 if (drm->next_front) {
201                         /* record an error and break */
202                         ALOGE("drmHandleEvent returned without flipping");
203                         drm->current_front = drm->next_front;
204                         drm->next_front = NULL;
205                 }
206         }
207
208         if (!bo)
209                 return 0;
210
211         pthread_mutex_lock(&drm->hdmi_mutex);
212         if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED) {
213                 ret = drmModePageFlip(drm->fd, drm->hdmi.crtc_id, bo->fb_id, 0, NULL);
214                 if (ret && errno != EBUSY)
215                         ALOGE("failed to perform page flip for hdmi (%s) (crtc %d fb %d))",
216                                 strerror(errno), drm->hdmi.crtc_id, bo->fb_id);
217         }
218         pthread_mutex_unlock(&drm->hdmi_mutex);
219
220         ret = drmModePageFlip(drm->fd, drm->primary.crtc_id, bo->fb_id,
221                         DRM_MODE_PAGE_FLIP_EVENT, (void *) drm);
222         if (ret) {
223                 ALOGE("failed to perform page flip for primary (%s) (crtc %d fb %d))",
224                         strerror(errno), drm->primary.crtc_id, bo->fb_id);
225                 /* try to set mode for next frame */
226                 if (errno != EBUSY)
227                         drm->first_post = 1;
228         }
229         else
230                 drm->next_front = bo;
231
232         return ret;
233 }
234
235 /*
236  * Wait for the next post.
237  */
238 static void drm_kms_wait_for_post(struct gralloc_drm_t *drm, int flip)
239 {
240         unsigned int current, target;
241         drmVBlank vbl;
242         int ret;
243
244         if (drm->mode_quirk_vmwgfx)
245                 return;
246
247         flip = !!flip;
248
249         memset(&vbl, 0, sizeof(vbl));
250         vbl.request.type = DRM_VBLANK_RELATIVE;
251         if (drm->vblank_secondary)
252                 vbl.request.type |= DRM_VBLANK_SECONDARY;
253         vbl.request.sequence = 0;
254
255         /* get the current vblank */
256         ret = drmWaitVBlank(drm->fd, &vbl);
257         if (ret) {
258                 ALOGW("failed to get vblank");
259                 return;
260         }
261
262         current = vbl.reply.sequence;
263         if (drm->first_post)
264                 target = current;
265         else
266                 target = drm->last_swap + drm->swap_interval - flip;
267
268         /* wait for vblank */
269         if (current < target || !flip) {
270                 memset(&vbl, 0, sizeof(vbl));
271                 vbl.request.type = DRM_VBLANK_ABSOLUTE;
272                 if (drm->vblank_secondary)
273                         vbl.request.type |= DRM_VBLANK_SECONDARY;
274                 if (!flip) {
275                         vbl.request.type |= DRM_VBLANK_NEXTONMISS;
276                         if (target < current)
277                                 target = current;
278                 }
279
280                 vbl.request.sequence = target;
281
282                 ret = drmWaitVBlank(drm->fd, &vbl);
283                 if (ret) {
284                         ALOGW("failed to wait vblank");
285                         return;
286                 }
287         }
288
289         drm->last_swap = vbl.reply.sequence + flip;
290 }
291
292 /*
293  * Post a bo.  This is not thread-safe.
294  */
295 int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
296 {
297         struct gralloc_drm_t *drm = bo->drm;
298         int ret;
299
300         if (!bo->fb_id && drm->swap_mode != DRM_SWAP_COPY) {
301                 ALOGE("unable to post bo %p without fb", bo);
302                 return -EINVAL;
303         }
304
305         /* TODO spawn a thread to avoid waiting and race */
306
307         if (drm->first_post) {
308                 if (drm->swap_mode == DRM_SWAP_COPY) {
309                         struct gralloc_drm_bo_t *dst;
310
311                         dst = (drm->next_front) ?
312                                 drm->next_front :
313                                 drm->current_front;
314                         drm->drv->copy(drm->drv, dst, bo, 0, 0,
315                                         bo->handle->width,
316                                         bo->handle->height);
317                         bo = dst;
318                 }
319
320                 ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
321                 if (!ret) {
322                         drm->first_post = 0;
323                         drm->current_front = bo;
324                         if (drm->next_front == bo)
325                                 drm->next_front = NULL;
326                 }
327
328                 pthread_mutex_lock(&drm->hdmi_mutex);
329                 if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
330                         drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
331                 pthread_mutex_unlock(&drm->hdmi_mutex);
332
333                 return ret;
334         }
335
336         switch (drm->swap_mode) {
337         case DRM_SWAP_FLIP:
338                 if (drm->swap_interval > 1)
339                         drm_kms_wait_for_post(drm, 1);
340                 ret = drm_kms_page_flip(drm, bo);
341                 if (drm->next_front) {
342                         /*
343                          * wait if the driver says so or the current front
344                          * will be written by CPU
345                          */
346                         if (drm->mode_sync_flip ||
347                             (drm->current_front->handle->usage &
348                              GRALLOC_USAGE_SW_WRITE_MASK))
349                                 drm_kms_page_flip(drm, NULL);
350                 }
351                 break;
352         case DRM_SWAP_COPY:
353                 drm_kms_wait_for_post(drm, 0);
354                 drm->drv->copy(drm->drv, drm->current_front,
355                                 bo, 0, 0,
356                                 bo->handle->width,
357                                 bo->handle->height);
358                 if (drm->mode_quirk_vmwgfx)
359                         ret = drmModeDirtyFB(drm->fd, drm->current_front->fb_id, &drm->clip, 1);
360                 ret = 0;
361                 break;
362         case DRM_SWAP_SETCRTC:
363                 drm_kms_wait_for_post(drm, 0);
364                 ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
365
366                 pthread_mutex_lock(&drm->hdmi_mutex);
367                 if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
368                         drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
369                 pthread_mutex_unlock(&drm->hdmi_mutex);
370
371                 drm->current_front = bo;
372                 break;
373         default:
374                 /* no-op */
375                 ret = 0;
376                 break;
377         }
378
379         return ret;
380 }
381
382 static struct gralloc_drm_t *drm_singleton;
383
384 static void on_signal(int sig)
385 {
386         struct gralloc_drm_t *drm = drm_singleton;
387
388         /* wait the pending flip */
389         if (drm && drm->swap_mode == DRM_SWAP_FLIP && drm->next_front) {
390                 /* there is race, but this function is hacky enough to ignore that */
391                 if (drm_singleton->waiting_flip)
392                         usleep(100 * 1000); /* 100ms */
393                 else
394                         drm_kms_page_flip(drm_singleton, NULL);
395         }
396
397         exit(-1);
398 }
399
400 static void drm_kms_init_features(struct gralloc_drm_t *drm)
401 {
402         const char *swap_mode;
403
404         /* call to the driver here, after KMS has been initialized */
405         drm->drv->init_kms_features(drm->drv, drm);
406
407         if (drm->swap_mode == DRM_SWAP_FLIP) {
408                 struct sigaction act;
409
410                 memset(&drm->evctx, 0, sizeof(drm->evctx));
411                 drm->evctx.version = DRM_EVENT_CONTEXT_VERSION;
412                 drm->evctx.page_flip_handler = page_flip_handler;
413
414                 /*
415                  * XXX GPU tends to freeze if the program is terminiated with a
416                  * flip pending.  What is the right way to handle the
417                  * situation?
418                  */
419                 sigemptyset(&act.sa_mask);
420                 act.sa_handler = on_signal;
421                 act.sa_flags = 0;
422                 sigaction(SIGINT, &act, NULL);
423                 sigaction(SIGTERM, &act, NULL);
424
425                 drm_singleton = drm;
426         }
427         else if (drm->swap_mode == DRM_SWAP_COPY) {
428                 struct gralloc_drm_bo_t *front;
429                 int stride;
430
431                 /* create the real front buffer */
432                 front = gralloc_drm_bo_create(drm,
433                                               drm->primary.mode.hdisplay,
434                                               drm->primary.mode.vdisplay,
435                                               drm->primary.fb_format,
436                                               GRALLOC_USAGE_HW_FB);
437                 if (front && gralloc_drm_bo_add_fb(front)) {
438                         gralloc_drm_bo_decref(front);
439                         front = NULL;
440                 }
441
442                 /* abuse next_front */
443                 if (front)
444                         drm->next_front = front;
445                 else
446                         drm->swap_mode = DRM_SWAP_SETCRTC;
447         }
448
449         switch (drm->swap_mode) {
450         case DRM_SWAP_FLIP:
451                 swap_mode = "flip";
452                 break;
453         case DRM_SWAP_COPY:
454                 swap_mode = "copy";
455                 break;
456         case DRM_SWAP_SETCRTC:
457                 swap_mode = "set-crtc";
458                 break;
459         default:
460                 swap_mode = "no-op";
461                 break;
462         }
463
464         ALOGD("will use %s for fb posting", swap_mode);
465 }
466
467 static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp)
468 {
469         char value[PROPERTY_VALUE_MAX];
470         drmModeModeInfoPtr mode;
471         int dist, i;
472         int xres = 0, yres = 0;
473
474         if (property_get("debug.drm.mode", value, NULL)) {
475                 char *p = value, *end;
476
477                 /* parse <xres>x<yres>[@<bpp>] */
478                 if (sscanf(value, "%dx%d@%d", &xres, &yres, bpp) != 3) {
479                         *bpp = 0;
480                         if (sscanf(value, "%dx%d", &xres, &yres) != 2)
481                                 xres = yres = 0;
482                 }
483
484                 if ((xres && yres) || *bpp) {
485                         ALOGI("will find the closest match for %dx%d@%d",
486                                         xres, yres, *bpp);
487                 }
488         }
489         else {
490                 *bpp = 0;
491         }
492
493         mode = NULL;
494         dist = INT_MAX;
495         for (i = 0; i < connector->count_modes; i++) {
496                 drmModeModeInfoPtr m = &connector->modes[i];
497                 int tmp;
498
499                 if (xres && yres) {
500                         tmp = (m->hdisplay - xres) * (m->hdisplay - xres) +
501                                 (m->vdisplay - yres) * (m->vdisplay - yres);
502                 }
503                 else {
504                         /* use the first preferred mode */
505                         tmp = (m->type & DRM_MODE_TYPE_PREFERRED) ? 0 : dist;
506                 }
507
508                 if (tmp < dist) {
509                         mode = m;
510                         dist = tmp;
511                         if (!dist)
512                                 break;
513                 }
514         }
515
516         /* fallback to the first mode */
517         if (!mode)
518                 mode = &connector->modes[0];
519
520         *bpp /= 8;
521
522         return mode;
523 }
524
525 /*
526  * Initialize KMS with a connector.
527  */
528 static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
529                 struct gralloc_drm_output *output, drmModeConnectorPtr connector)
530 {
531         drmModeEncoderPtr encoder;
532         drmModeModeInfoPtr mode;
533         static int used_crtcs = 0;
534         int bpp, i;
535
536         if (!connector->count_modes)
537                 return -EINVAL;
538
539         encoder = drmModeGetEncoder(drm->fd, connector->encoders[0]);
540         if (!encoder)
541                 return -EINVAL;
542
543         /* find first possible crtc which is not used yet */
544         for (i = 0; i < drm->resources->count_crtcs; i++) {
545                 if (encoder->possible_crtcs & (1 << i) &&
546                         (used_crtcs & (1 << i)) != (1 << i))
547                         break;
548         }
549
550         used_crtcs |= (1 << i);
551
552         drmModeFreeEncoder(encoder);
553         if (i == drm->resources->count_crtcs)
554                 return -EINVAL;
555
556         output->crtc_id = drm->resources->crtcs[i];
557         output->connector_id = connector->connector_id;
558
559         /* print connector info */
560         if (connector->count_modes > 1) {
561                 ALOGI("there are %d modes on connector 0x%x",
562                                 connector->count_modes,
563                                 connector->connector_id);
564                 for (i = 0; i < connector->count_modes; i++)
565                         ALOGI("  %s", connector->modes[i].name);
566         }
567         else {
568                 ALOGI("there is one mode on connector 0x%d: %s",
569                                 connector->connector_id,
570                                 connector->modes[0].name);
571         }
572
573         mode = find_mode(connector, &bpp);
574
575         ALOGI("the best mode is %s", mode->name);
576
577         output->mode = *mode;
578         switch (bpp) {
579         case 2:
580                 output->fb_format = HAL_PIXEL_FORMAT_RGB_565;
581                 break;
582         case 4:
583         default:
584                 output->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
585                 break;
586         }
587
588         if (connector->mmWidth && connector->mmHeight) {
589                 output->xdpi = (output->mode.hdisplay * 25.4 / connector->mmWidth);
590                 output->ydpi = (output->mode.vdisplay * 25.4 / connector->mmHeight);
591         }
592         else {
593                 output->xdpi = 75;
594                 output->ydpi = 75;
595         }
596
597 #ifdef DRM_MODE_FEATURE_DIRTYFB
598         drm->clip.x1 = 0;
599         drm->clip.y1 = 0;
600         drm->clip.x2 = output->mode.hdisplay;
601         drm->clip.y2 = output->mode.vdisplay;
602 #endif
603
604         return 0;
605 }
606
607
608 /*
609  * Fetch a connector of particular type
610  */
611 static drmModeConnectorPtr fetch_connector(struct gralloc_drm_t *drm,
612         uint32_t type)
613 {
614         int i;
615
616         if (!drm->resources)
617                 return NULL;
618
619         for (i = 0; i < drm->resources->count_connectors; i++) {
620                 drmModeConnectorPtr connector =
621                         connector = drmModeGetConnector(drm->fd,
622                                 drm->resources->connectors[i]);
623                 if (connector) {
624                         if (connector->connector_type == type &&
625                                 connector->connection == DRM_MODE_CONNECTED)
626                                 return connector;
627                         drmModeFreeConnector(connector);
628                 }
629         }
630         return NULL;
631 }
632
633
634 /*
635  * Thread that listens to uevents and checks if hdmi state changes
636  */
637 static void *hdmi_observer(void *data)
638 {
639         static char uevent_desc[4096];
640         drmModeConnectorPtr hdmi;
641         struct gralloc_drm_t *drm =
642                 (struct gralloc_drm_t *) data;
643
644         uevent_init();
645
646         memset(uevent_desc, 0, sizeof(uevent_desc));
647
648         while(1) {
649
650                 /* this polls */
651                 int len = uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2);
652
653                 if(len && strstr(uevent_desc, "devices/virtual/switch/hdmi")) {
654
655                         /* check what changed */
656                         const char *prop = uevent_desc + strlen(uevent_desc) + 1;
657
658                         while (*prop) {
659
660                                 const char *state = strstr(prop, "SWITCH_STATE=");
661                                 if (state) {
662                                         unsigned int value = 0;
663                                         state += strlen("SWITCH_STATE=");
664                                         value = atoi(state);
665
666                                         pthread_mutex_lock(&drm->hdmi_mutex);
667
668                                         if (value) {
669                                                 hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
670                                                 if (hdmi) {
671                                                         drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
672                                                         drmModeFreeConnector(hdmi);
673
674                                                         /* will trigger modeset */
675                                                         drm->first_post = 1;
676
677                                                         /* HACK, assume same mode for now */
678                                                         memcpy(&drm->hdmi.mode, &drm->primary.mode,
679                                                                 sizeof(drmModeModeInfo));
680
681                                                         drm->hdmi_mode = HDMI_CLONED;
682                                                         drm->hdmi.active = 1;
683                                                         pthread_mutex_unlock(&drm->hdmi_mutex);
684                                                 }
685                                                 break;
686                                         } else {
687                                                 drm->hdmi.active = 0;
688                                                 pthread_mutex_unlock(&drm->hdmi_mutex);
689                                                 break;
690                                         }
691
692                                         pthread_mutex_unlock(&drm->hdmi_mutex);
693                                 }
694
695                                 /* next property/value pair */
696                                 prop += strlen(prop) + 1;
697                                 if (prop - uevent_desc >= len)
698                                         break;
699                         }
700                 }
701         }
702
703         pthread_exit(NULL);
704         return 0;
705 }
706
707
708 /*
709  * Initialize KMS.
710  */
711 int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
712 {
713         drmModeConnectorPtr lvds, hdmi;
714         int i, ret;
715
716         if (drm->resources)
717                 return 0;
718
719         drm->resources = drmModeGetResources(drm->fd);
720         if (!drm->resources) {
721                 ALOGE("failed to get modeset resources");
722                 return -EINVAL;
723         }
724
725         drm->plane_resources = drmModeGetPlaneResources(drm->fd);
726         if (!drm->plane_resources) {
727                 ALOGD("no planes found from drm resources");
728         } else {
729                 ALOGD("supported drm planes and formats");
730                 /* fill a helper structure for hwcomposer */
731                 drm->planes = calloc(drm->plane_resources->count_planes,
732                         sizeof(struct gralloc_drm_plane_t));
733
734                 for (i = 0; i < drm->plane_resources->count_planes; i++) {
735
736                         unsigned int j;
737
738                         drm->planes[i].drm_plane = drmModeGetPlane(drm->fd,
739                                 drm->plane_resources->planes[i]);
740
741                         ALOGD("plane id %d", drm->planes[i].drm_plane->plane_id);
742                         for (j = 0; j < drm->planes[i].drm_plane->count_formats; j++)
743                                 ALOGD("    format %c%c%c%c",
744                                         (drm->planes[i].drm_plane->formats[j]),
745                                         (drm->planes[i].drm_plane->formats[j])>>8,
746                                         (drm->planes[i].drm_plane->formats[j])>>16,
747                                         (drm->planes[i].drm_plane->formats[j])>>24);
748                 }
749         }
750
751         /* find the crtc/connector/mode to use */
752         lvds = fetch_connector(drm, DRM_MODE_CONNECTOR_LVDS);
753         if (lvds) {
754                 drm_kms_init_with_connector(drm, &drm->primary, lvds);
755                 drmModeFreeConnector(lvds);
756                 drm->primary.active = 1;
757         }
758
759         /* if still no connector, find first connected connector and try it */
760         if (!drm->primary.active) {
761
762                 for (i = 0; i < drm->resources->count_connectors; i++) {
763                         drmModeConnectorPtr connector;
764
765                         connector = drmModeGetConnector(drm->fd,
766                                         drm->resources->connectors[i]);
767                         if (connector) {
768                                 if (connector->connection == DRM_MODE_CONNECTED) {
769                                         if (!drm_kms_init_with_connector(drm,
770                                                         &drm->primary, connector))
771                                                 break;
772                                 }
773
774                                 drmModeFreeConnector(connector);
775                         }
776                 }
777                 if (i == drm->resources->count_connectors) {
778                         ALOGE("failed to find a valid crtc/connector/mode combination");
779                         drmModeFreeResources(drm->resources);
780                         drm->resources = NULL;
781
782                         return -EINVAL;
783                 }
784         }
785
786         /* check if hdmi is connected already */
787         hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
788         if (hdmi) {
789
790                 if (hdmi->connector_id == drm->primary.connector_id) {
791                         /* special case: our primary connector is hdmi */
792                         goto skip_hdmi_modes;
793                 }
794
795                 drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
796                 drmModeFreeConnector(hdmi);
797
798                 /* HACK, assume same mode for now */
799                 memcpy(&drm->hdmi.mode, &drm->primary.mode,
800                         sizeof(drmModeModeInfo));
801
802                 drm->hdmi_mode = HDMI_CLONED;
803                 drm->hdmi.active = 1;
804         }
805
806         /* launch hdmi observer thread */
807         pthread_mutex_init(&drm->hdmi_mutex, NULL);
808         pthread_create(&drm->hdmi_hotplug_thread, NULL, hdmi_observer, drm);
809
810 skip_hdmi_modes:
811
812         drm_kms_init_features(drm);
813         drm->first_post = 1;
814
815         return 0;
816 }
817
818 void gralloc_drm_fini_kms(struct gralloc_drm_t *drm)
819 {
820         switch (drm->swap_mode) {
821         case DRM_SWAP_FLIP:
822                 drm_kms_page_flip(drm, NULL);
823                 break;
824         case DRM_SWAP_COPY:
825                 {
826                         struct gralloc_drm_bo_t **bo = (drm->current_front) ?
827                                 &drm->current_front : &drm->next_front;
828
829                         if (*bo)
830                                 gralloc_drm_bo_decref(*bo);
831                         *bo = NULL;
832                 }
833                 break;
834         default:
835                 break;
836         }
837
838         /* restore crtc? */
839
840         if (drm->resources) {
841                 drmModeFreeResources(drm->resources);
842                 drm->resources = NULL;
843         }
844
845         if (drm->planes) {
846                 unsigned int i;
847                 for (i = 0; i < drm->plane_resources->count_planes; i++)
848                         drmModeFreePlane(drm->planes[i].drm_plane);
849                 free(drm->planes);
850                 drm->planes = NULL;
851         }
852
853         if (drm->plane_resources) {
854                 drmModeFreePlaneResources(drm->plane_resources);
855                 drm->plane_resources = NULL;
856         }
857
858         drm_singleton = NULL;
859 }
860
861 int gralloc_drm_is_kms_initialized(struct gralloc_drm_t *drm)
862 {
863         return (drm->resources != NULL);
864 }
865
866 /*
867  * Initialize a framebuffer device with KMS info.
868  */
869 void gralloc_drm_get_kms_info(struct gralloc_drm_t *drm,
870                 struct framebuffer_device_t *fb)
871 {
872         *((uint32_t *) &fb->flags) = 0x0;
873         *((uint32_t *) &fb->width) = drm->primary.mode.hdisplay;
874         *((uint32_t *) &fb->height) = drm->primary.mode.vdisplay;
875         *((int *)      &fb->stride) = drm->primary.mode.hdisplay;
876         *((float *)    &fb->fps) = drm->primary.mode.vrefresh;
877
878         *((int *)      &fb->format) = drm->primary.fb_format;
879         *((float *)    &fb->xdpi) = drm->primary.xdpi;
880         *((float *)    &fb->ydpi) = drm->primary.ydpi;
881         *((int *)      &fb->minSwapInterval) = drm->swap_interval;
882         *((int *)      &fb->maxSwapInterval) = drm->swap_interval;
883 }
884
885 /*
886  * Return true if fb posting is pipelined.
887  */
888 int gralloc_drm_is_kms_pipelined(struct gralloc_drm_t *drm)
889 {
890         return (drm->swap_mode != DRM_SWAP_SETCRTC);
891 }