OSDN Git Service

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