OSDN Git Service

gralloc: hdmi cloned mode support
[android-x86/external-drm_gralloc.git] / gralloc_drm_kms.c
index c64d4ad..57870f7 100644 (file)
 
 #define LOG_TAG "GRALLOC-KMS"
 
+#include <cutils/properties.h>
 #include <cutils/log.h>
 #include <errno.h>
 #include <unistd.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <stdio.h>
+#include <poll.h>
 #include "gralloc_drm.h"
 #include "gralloc_drm_priv.h"
+#include <hardware_legacy/uevent.h>
+
+#include <drm_fourcc.h>
 
 /*
  * Return true if a bo needs fb.
@@ -40,22 +46,82 @@ int gralloc_drm_bo_need_fb(const struct gralloc_drm_bo_t *bo)
                bo->drm->swap_mode != DRM_SWAP_COPY);
 }
 
+static unsigned int drm_format_from_hal(int hal_format)
+{
+       switch(hal_format) {
+               case HAL_PIXEL_FORMAT_RGB_888:
+               case HAL_PIXEL_FORMAT_RGBX_8888:
+               case HAL_PIXEL_FORMAT_BGRA_8888:
+                       return DRM_FORMAT_XRGB8888;
+               case HAL_PIXEL_FORMAT_RGBA_8888:
+                       return DRM_FORMAT_RGBA8888;
+               case HAL_PIXEL_FORMAT_RGB_565:
+                       return DRM_FORMAT_RGB565;
+               case HAL_PIXEL_FORMAT_YV12:
+                       return DRM_FORMAT_YUV420;
+               default:
+                       return 0;
+       }
+}
+
+/*
+ * Modify pitches, offsets and handles according to
+ * the format and return corresponding drm format value
+ */
+static int resolve_drm_format(struct gralloc_drm_bo_t *bo,
+       uint32_t *pitches, uint32_t *offsets, uint32_t *handles)
+{
+       memset(pitches, 0, 4 * sizeof(uint32_t));
+       memset(offsets, 0, 4 * sizeof(uint32_t));
+       memset(handles, 0, 4 * sizeof(uint32_t));
+
+       pitches[0] = bo->handle->stride;
+       handles[0] = bo->fb_handle;
+
+       int format = drm_format_from_hal(bo->handle->format);
+
+       // handle 'special formats'
+       switch(bo->handle->format) {
+               case HAL_PIXEL_FORMAT_YV12:
+
+                       // U and V stride are half of Y plane
+                       pitches[2] = pitches[0]/2;
+                       pitches[1] = pitches[0]/2;
+
+                       // like I420 but U and V are in reverse order
+                       offsets[2] = offsets[0] +
+                               pitches[0] * bo->handle->height;
+                       offsets[1] = offsets[2] +
+                               pitches[2] * bo->handle->height/2;
+
+                       handles[1] = handles[2] = handles[0];
+       }
+       return format;
+}
+
 /*
  * Add a fb object for a bo.
  */
 int gralloc_drm_bo_add_fb(struct gralloc_drm_bo_t *bo)
 {
-       uint8_t bpp;
+       uint32_t pitches[4];
+       uint32_t offsets[4];
+       uint32_t handles[4];
 
        if (bo->fb_id)
                return 0;
 
-       bpp = gralloc_drm_get_bpp(bo->handle->format) * 8;
+       int drm_format = resolve_drm_format(bo, pitches, offsets, handles);
 
-       return drmModeAddFB(bo->drm->fd,
-                       bo->handle->width, bo->handle->height, bpp, bpp,
-                       bo->handle->stride, bo->fb_handle,
-                       (uint32_t *) &bo->fb_id);
+       if (drm_format == 0) {
+               ALOGE("error resolving drm format");
+               return -EINVAL;
+       }
+
+       return drmModeAddFB2(bo->drm->fd,
+               bo->handle->width, bo->handle->height,
+               drm_format, handles, pitches, offsets,
+               (uint32_t *) &bo->fb_id, 0);
 }
 
 /*
@@ -72,21 +138,22 @@ void gralloc_drm_bo_rm_fb(struct gralloc_drm_bo_t *bo)
 /*
  * Program CRTC.
  */
-static int drm_kms_set_crtc(struct gralloc_drm_t *drm, int fb_id)
+static int drm_kms_set_crtc(struct gralloc_drm_t *drm,
+       struct gralloc_drm_output *output, int fb_id)
 {
        int ret;
 
-       ret = drmModeSetCrtc(drm->fd, drm->crtc_id, fb_id,
-                       0, 0, &drm->connector_id, 1, &drm->mode);
+       ret = drmModeSetCrtc(drm->fd, output->crtc_id, fb_id,
+                       0, 0, &output->connector_id, 1, &output->mode);
        if (ret) {
-               LOGE("failed to set crtc");
+               ALOGE("failed to set crtc (%s) (crtc_id %d, fb_id %d, conn %d, mode %dx%d)",
+                       strerror(errno), output->crtc_id, fb_id, output->connector_id,
+                       output->mode.hdisplay, output->mode.vdisplay);
                return ret;
        }
 
-#ifdef DRM_MODE_FEATURE_DIRTYFB
-       if (drm->mode_dirty_fb)
+       if (drm->mode_quirk_vmwgfx)
                ret = drmModeDirtyFB(drm->fd, fb_id, &drm->clip, 1);
-#endif
 
        return ret;
 }
@@ -120,7 +187,7 @@ static int drm_kms_page_flip(struct gralloc_drm_t *drm,
                drm->waiting_flip = 0;
                if (drm->next_front) {
                        /* record an error and break */
-                       LOGE("drmHandleEvent returned without flipping");
+                       ALOGE("drmHandleEvent returned without flipping");
                        drm->current_front = drm->next_front;
                        drm->next_front = NULL;
                }
@@ -129,10 +196,23 @@ static int drm_kms_page_flip(struct gralloc_drm_t *drm,
        if (!bo)
                return 0;
 
-       ret = drmModePageFlip(drm->fd, drm->crtc_id, bo->fb_id,
+       pthread_mutex_lock(&drm->hdmi_mutex);
+       if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED) {
+               ret = drmModePageFlip(drm->fd, drm->hdmi.crtc_id, bo->fb_id, 0, NULL);
+               if (ret && errno != EBUSY)
+                       ALOGE("failed to perform page flip for hdmi (%s) (crtc %d fb %d))",
+                               strerror(errno), drm->hdmi.crtc_id, bo->fb_id);
+       }
+       pthread_mutex_unlock(&drm->hdmi_mutex);
+
+       ret = drmModePageFlip(drm->fd, drm->primary.crtc_id, bo->fb_id,
                        DRM_MODE_PAGE_FLIP_EVENT, (void *) drm);
-       if (ret)
-               LOGE("failed to perform page flip");
+       if (ret) {
+               ALOGE("failed to perform page flip for primary (%s) (crtc %d fb %d))",
+                       strerror(errno), drm->primary.crtc_id, bo->fb_id);
+               /* try to set mode for next frame */
+               drm->first_post = 1;
+       }
        else
                drm->next_front = bo;
 
@@ -148,6 +228,9 @@ static void drm_kms_wait_for_post(struct gralloc_drm_t *drm, int flip)
        drmVBlank vbl;
        int ret;
 
+       if (drm->mode_quirk_vmwgfx)
+               return;
+
        flip = !!flip;
 
        memset(&vbl, 0, sizeof(vbl));
@@ -159,7 +242,7 @@ static void drm_kms_wait_for_post(struct gralloc_drm_t *drm, int flip)
        /* get the current vblank */
        ret = drmWaitVBlank(drm->fd, &vbl);
        if (ret) {
-               LOGW("failed to get vblank");
+               ALOGW("failed to get vblank");
                return;
        }
 
@@ -185,7 +268,7 @@ static void drm_kms_wait_for_post(struct gralloc_drm_t *drm, int flip)
 
                ret = drmWaitVBlank(drm->fd, &vbl);
                if (ret) {
-                       LOGW("failed to wait vblank");
+                       ALOGW("failed to wait vblank");
                        return;
                }
        }
@@ -202,7 +285,7 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
        int ret;
 
        if (!bo->fb_id && drm->swap_mode != DRM_SWAP_COPY) {
-               LOGE("unable to post bo %p without fb", bo);
+               ALOGE("unable to post bo %p without fb", bo);
                return -EINVAL;
        }
 
@@ -221,8 +304,7 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
                        bo = dst;
                }
 
-               drm_kms_wait_for_post(drm, 0);
-               ret = drm_kms_set_crtc(drm, bo->fb_id);
+               ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
                if (!ret) {
                        drm->first_post = 0;
                        drm->current_front = bo;
@@ -230,6 +312,11 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
                                drm->next_front = NULL;
                }
 
+               pthread_mutex_lock(&drm->hdmi_mutex);
+               if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
+                       drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
+               pthread_mutex_unlock(&drm->hdmi_mutex);
+
                return ret;
        }
 
@@ -255,11 +342,19 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
                                bo, 0, 0,
                                bo->handle->width,
                                bo->handle->height);
+               if (drm->mode_quirk_vmwgfx)
+                       ret = drmModeDirtyFB(drm->fd, drm->current_front->fb_id, &drm->clip, 1);
                ret = 0;
                break;
        case DRM_SWAP_SETCRTC:
                drm_kms_wait_for_post(drm, 0);
-               ret = drm_kms_set_crtc(drm, bo->fb_id);
+               ret = drm_kms_set_crtc(drm, &drm->primary, bo->fb_id);
+
+               pthread_mutex_lock(&drm->hdmi_mutex);
+               if (drm->hdmi.active && drm->hdmi_mode == HDMI_CLONED)
+                       drm_kms_set_crtc(drm, &drm->hdmi, bo->fb_id);
+               pthread_mutex_unlock(&drm->hdmi_mutex);
+
                drm->current_front = bo;
                break;
        default:
@@ -275,18 +370,18 @@ static struct gralloc_drm_t *drm_singleton;
 
 static void on_signal(int sig)
 {
-   struct sigaction act;
-
-   /* wait the pending flip */
-   if (drm_singleton && drm_singleton->next_front) {
-      /* there is race, but this function is hacky enough to ignore that */
-      if (drm_singleton->waiting_flip)
-         usleep(100 * 1000); /* 100ms */
-      else
-         drm_kms_page_flip(drm_singleton, NULL);
-   }
-
-   exit(-1);
+       struct gralloc_drm_t *drm = drm_singleton;
+
+       /* wait the pending flip */
+       if (drm && drm->swap_mode == DRM_SWAP_FLIP && drm->next_front) {
+               /* there is race, but this function is hacky enough to ignore that */
+               if (drm_singleton->waiting_flip)
+                       usleep(100 * 1000); /* 100ms */
+               else
+                       drm_kms_page_flip(drm_singleton, NULL);
+       }
+
+       exit(-1);
 }
 
 static void drm_kms_init_features(struct gralloc_drm_t *drm)
@@ -322,12 +417,12 @@ static void drm_kms_init_features(struct gralloc_drm_t *drm)
 
                /* create the real front buffer */
                front = gralloc_drm_bo_create(drm,
-                                             drm->mode.hdisplay,
-                                             drm->mode.vdisplay,
-                                             drm->format,
+                                             drm->primary.mode.hdisplay,
+                                             drm->primary.mode.vdisplay,
+                                             drm->primary.fb_format,
                                              GRALLOC_USAGE_HW_FB);
                if (front && gralloc_drm_bo_add_fb(front)) {
-                       gralloc_drm_bo_destroy(front);
+                       gralloc_drm_bo_decref(front);
                        front = NULL;
                }
 
@@ -353,18 +448,76 @@ static void drm_kms_init_features(struct gralloc_drm_t *drm)
                break;
        }
 
-       LOGD("will use %s for fb posting", swap_mode);
+       ALOGD("will use %s for fb posting", swap_mode);
+}
+
+static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp)
+{
+       char value[PROPERTY_VALUE_MAX];
+       drmModeModeInfoPtr mode;
+       int dist, i;
+       int xres = 0, yres = 0;
+
+       if (property_get("debug.drm.mode", value, NULL)) {
+               char *p = value, *end;
+
+               /* parse <xres>x<yres>[@<bpp>] */
+               if (sscanf(value, "%dx%d@%d", &xres, &yres, bpp) != 3) {
+                       *bpp = 0;
+                       if (sscanf(value, "%dx%d", &xres, &yres) != 2)
+                               xres = yres = 0;
+               }
+
+               if ((xres && yres) || *bpp) {
+                       ALOGI("will find the closest match for %dx%d@%d",
+                                       xres, yres, *bpp);
+               }
+       }
+       else {
+               *bpp = 0;
+       }
+
+       mode = NULL;
+       dist = INT_MAX;
+       for (i = 0; i < connector->count_modes; i++) {
+               drmModeModeInfoPtr m = &connector->modes[i];
+               int tmp;
+
+               if (xres && yres) {
+                       tmp = (m->hdisplay - xres) * (m->hdisplay - xres) +
+                               (m->vdisplay - yres) * (m->vdisplay - yres);
+               }
+               else {
+                       /* use the first preferred mode */
+                       tmp = (m->type & DRM_MODE_TYPE_PREFERRED) ? 0 : dist;
+               }
+
+               if (tmp < dist) {
+                       mode = m;
+                       dist = tmp;
+                       if (!dist)
+                               break;
+               }
+       }
+
+       /* fallback to the first mode */
+       if (!mode)
+               mode = &connector->modes[0];
+
+       *bpp /= 8;
+
+       return mode;
 }
 
 /*
  * Initialize KMS with a connector.
  */
 static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
-               drmModeConnectorPtr connector)
+               struct gralloc_drm_output *output, drmModeConnectorPtr connector)
 {
        drmModeEncoderPtr encoder;
        drmModeModeInfoPtr mode;
-       int i;
+       int bpp, i;
 
        if (!connector->count_modes)
                return -EINVAL;
@@ -373,63 +526,174 @@ static int drm_kms_init_with_connector(struct gralloc_drm_t *drm,
        if (!encoder)
                return -EINVAL;
 
+       /* find first possible crtc which is not used yet */
        for (i = 0; i < drm->resources->count_crtcs; i++) {
-               if (encoder->possible_crtcs & (1 << i))
+               if (encoder->possible_crtcs & (1 << i) &&
+                       i != drm->primary.crtc_id)
                        break;
        }
        drmModeFreeEncoder(encoder);
        if (i == drm->resources->count_crtcs)
                return -EINVAL;
 
-       drm->crtc_id = drm->resources->crtcs[i];
-       drm->connector_id = connector->connector_id;
+       output->crtc_id = drm->resources->crtcs[i];
+       output->connector_id = connector->connector_id;
 
-       /* find the first preferred mode */
-       mode = NULL;
-       for (i = 0; i < connector->count_modes; i++) {
-               drmModeModeInfoPtr m = &connector->modes[i];
-               if (m->type & DRM_MODE_TYPE_PREFERRED) {
-                       mode = m;
-                       break;
-               }
+       /* print connector info */
+       if (connector->count_modes > 1) {
+               ALOGI("there are %d modes on connector 0x%x",
+                               connector->count_modes,
+                               connector->connector_id);
+               for (i = 0; i < connector->count_modes; i++)
+                       ALOGI("  %s", connector->modes[i].name);
        }
-       /* no preference; use the first */
-       if (!mode)
-               mode = &connector->modes[0];
+       else {
+               ALOGI("there is one mode on connector 0x%d: %s",
+                               connector->connector_id,
+                               connector->modes[0].name);
+       }
+
+       mode = find_mode(connector, &bpp);
 
-       drm->mode = *mode;
+       ALOGI("the best mode is %s", mode->name);
+
+       output->mode = *mode;
+       switch (bpp) {
+       case 2:
+               output->fb_format = HAL_PIXEL_FORMAT_RGB_565;
+               break;
+       case 4:
+       default:
+               output->fb_format = HAL_PIXEL_FORMAT_BGRA_8888;
+               break;
+       }
 
        if (connector->mmWidth && connector->mmHeight) {
-               drm->xdpi = (drm->mode.hdisplay * 25.4 / connector->mmWidth);
-               drm->ydpi = (drm->mode.vdisplay * 25.4 / connector->mmHeight);
+               output->xdpi = (output->mode.hdisplay * 25.4 / connector->mmWidth);
+               output->ydpi = (output->mode.vdisplay * 25.4 / connector->mmHeight);
        }
        else {
-               drm->xdpi = 75;
-               drm->ydpi = 75;
+               output->xdpi = 75;
+               output->ydpi = 75;
        }
 
-       /* select between 32/16 bits */
-#if 1
-       drm->format = HAL_PIXEL_FORMAT_BGRA_8888;
-#else
-       drm->format = HAL_PIXEL_FORMAT_RGB_565;
-#endif
-
 #ifdef DRM_MODE_FEATURE_DIRTYFB
        drm->clip.x1 = 0;
        drm->clip.y1 = 0;
-       drm->clip.x2 = drm->mode.hdisplay;
-       drm->clip.y2 = drm->mode.vdisplay;
+       drm->clip.x2 = output->mode.hdisplay;
+       drm->clip.y2 = output->mode.vdisplay;
 #endif
 
        return 0;
 }
 
+
+/*
+ * Fetch a connector of particular type
+ */
+static drmModeConnectorPtr fetch_connector(struct gralloc_drm_t *drm,
+       uint32_t type)
+{
+       int i;
+
+       if (!drm->resources)
+               return NULL;
+
+       for (i = 0; i < drm->resources->count_connectors; i++) {
+               drmModeConnectorPtr connector =
+                       connector = drmModeGetConnector(drm->fd,
+                               drm->resources->connectors[i]);
+               if (connector) {
+                       if (connector->connector_type == type &&
+                               connector->connection == DRM_MODE_CONNECTED)
+                               return connector;
+                       drmModeFreeConnector(connector);
+               }
+       }
+       return NULL;
+}
+
+
+/*
+ * Thread that listens to uevents and checks if hdmi state changes
+ */
+static void *hdmi_observer(void *data)
+{
+       static char uevent_desc[4096];
+       drmModeConnectorPtr hdmi;
+       struct gralloc_drm_t *drm =
+               (struct gralloc_drm_t *) data;
+
+       uevent_init();
+
+       memset(uevent_desc, 0, sizeof(uevent_desc));
+
+       while(1) {
+
+               /* this polls */
+               int len = uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2);
+
+               if(len && strstr(uevent_desc, "devices/virtual/switch/hdmi")) {
+
+                       /* check what changed */
+                       const char *prop = uevent_desc + strlen(uevent_desc) + 1;
+
+                       while (*prop) {
+
+                               const char *state = strstr(prop, "SWITCH_STATE=");
+                               if (state) {
+                                       unsigned int value = 0;
+                                       state += strlen("SWITCH_STATE=");
+                                       value = atoi(state);
+
+                                       pthread_mutex_lock(&drm->hdmi_mutex);
+
+                                       if (value) {
+                                               hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
+                                               if (hdmi) {
+                                                       drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
+                                                       drmModeFreeConnector(hdmi);
+
+                                                       /* will trigger modeset */
+                                                       drm->first_post = 1;
+
+                                                       /* HACK, assume same mode for now */
+                                                       memcpy(&drm->hdmi.mode, &drm->primary.mode,
+                                                               sizeof(drmModeModeInfo));
+
+                                                       drm->hdmi_mode = HDMI_CLONED;
+                                                       drm->hdmi.active = 1;
+                                                       pthread_mutex_unlock(&drm->hdmi_mutex);
+                                               }
+                                               break;
+                                       } else {
+                                               drm->hdmi.active = 0;
+                                               pthread_mutex_unlock(&drm->hdmi_mutex);
+                                               break;
+                                       }
+
+                                       pthread_mutex_unlock(&drm->hdmi_mutex);
+                               }
+
+                               /* next property/value pair */
+                               prop += strlen(prop) + 1;
+                               if (prop - uevent_desc >= len)
+                                       break;
+                       }
+               }
+       }
+
+       pthread_exit(NULL);
+       return 0;
+}
+
+
 /*
  * Initialize KMS.
  */
 int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
 {
+       drmModeConnectorPtr lvds, hdmi;
        int i, ret;
 
        if (drm->resources)
@@ -437,40 +701,143 @@ int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
 
        drm->resources = drmModeGetResources(drm->fd);
        if (!drm->resources) {
-               LOGE("failed to get modeset resources");
+               ALOGE("failed to get modeset resources");
                return -EINVAL;
        }
 
+       drm->plane_resources = drmModeGetPlaneResources(drm->fd);
+       if (!drm->plane_resources) {
+               ALOGD("no planes found from drm resources");
+       } else {
+               ALOGD("supported drm planes and formats");
+               /* fill a helper structure for hwcomposer */
+               drm->planes = calloc(drm->plane_resources->count_planes,
+                       sizeof(struct gralloc_drm_plane_t));
+
+               for (i = 0; i < drm->plane_resources->count_planes; i++) {
+
+                       unsigned int j;
+
+                       drm->planes[i].drm_plane = drmModeGetPlane(drm->fd,
+                               drm->plane_resources->planes[i]);
+
+                       ALOGD("plane id %d", drm->planes[i].drm_plane->plane_id);
+                       for (j = 0; j < drm->planes[i].drm_plane->count_formats; j++)
+                               ALOGD("    format %c%c%c%c",
+                                       (drm->planes[i].drm_plane->formats[j]),
+                                       (drm->planes[i].drm_plane->formats[j])>>8,
+                                       (drm->planes[i].drm_plane->formats[j])>>16,
+                                       (drm->planes[i].drm_plane->formats[j])>>24);
+               }
+       }
+
        /* find the crtc/connector/mode to use */
-       for (i = 0; i < drm->resources->count_connectors; i++) {
-               drmModeConnectorPtr connector;
+       lvds = fetch_connector(drm, DRM_MODE_CONNECTOR_LVDS);
+       if (lvds) {
+               drm_kms_init_with_connector(drm, &drm->primary, lvds);
+               drmModeFreeConnector(lvds);
+               drm->primary.active = 1;
+       }
 
-               connector = drmModeGetConnector(drm->fd,
-                               drm->resources->connectors[i]);
-               if (connector) {
-                       if (connector->connection == DRM_MODE_CONNECTED) {
-                               if (!drm_kms_init_with_connector(drm,
-                                                       connector))
-                                       break;
+       /* if still no connector, find first connected connector and try it */
+       if (!drm->primary.active) {
+
+               for (i = 0; i < drm->resources->count_connectors; i++) {
+                       drmModeConnectorPtr connector;
+
+                       connector = drmModeGetConnector(drm->fd,
+                                       drm->resources->connectors[i]);
+                       if (connector) {
+                               if (connector->connection == DRM_MODE_CONNECTED) {
+                                       if (!drm_kms_init_with_connector(drm,
+                                                       &drm->primary, connector))
+                                               break;
+                               }
+
+                               drmModeFreeConnector(connector);
                        }
+               }
+               if (i == drm->resources->count_connectors) {
+                       ALOGE("failed to find a valid crtc/connector/mode combination");
+                       drmModeFreeResources(drm->resources);
+                       drm->resources = NULL;
 
-                       drmModeFreeConnector(connector);
+                       return -EINVAL;
                }
        }
-       if (i == drm->resources->count_connectors) {
-               LOGE("failed to find a valid crtc/connector/mode combination");
-               drmModeFreeResources(drm->resources);
-               drm->resources = NULL;
 
-               return -EINVAL;
+       /* check if hdmi is connected already */
+       hdmi = fetch_connector(drm, DRM_MODE_CONNECTOR_HDMIA);
+       if (hdmi) {
+               drm_kms_init_with_connector(drm, &drm->hdmi, hdmi);
+               drmModeFreeConnector(hdmi);
+
+               /* HACK, assume same mode for now */
+               memcpy(&drm->hdmi.mode, &drm->primary.mode,
+                       sizeof(drmModeModeInfo));
+
+               drm->hdmi_mode = HDMI_CLONED;
+               drm->hdmi.active = 1;
        }
 
+       /* launch hdmi observer thread */
+       pthread_mutex_init(&drm->hdmi_mutex, NULL);
+       pthread_create(&drm->hdmi_hotplug_thread, NULL, hdmi_observer, drm);
+
        drm_kms_init_features(drm);
        drm->first_post = 1;
 
        return 0;
 }
 
+void gralloc_drm_fini_kms(struct gralloc_drm_t *drm)
+{
+       switch (drm->swap_mode) {
+       case DRM_SWAP_FLIP:
+               drm_kms_page_flip(drm, NULL);
+               break;
+       case DRM_SWAP_COPY:
+               {
+                       struct gralloc_drm_bo_t **bo = (drm->current_front) ?
+                               &drm->current_front : &drm->next_front;
+
+                       if (*bo)
+                               gralloc_drm_bo_decref(*bo);
+                       *bo = NULL;
+               }
+               break;
+       default:
+               break;
+       }
+
+       /* restore crtc? */
+
+       if (drm->resources) {
+               drmModeFreeResources(drm->resources);
+               drm->resources = NULL;
+       }
+
+       if (drm->planes) {
+               unsigned int i;
+               for (i = 0; i < drm->plane_resources->count_planes; i++)
+                       drmModeFreePlane(drm->planes[i].drm_plane);
+               free(drm->planes);
+               drm->planes = NULL;
+       }
+
+       if (drm->plane_resources) {
+               drmModeFreePlaneResources(drm->plane_resources);
+               drm->plane_resources = NULL;
+       }
+
+       drm_singleton = NULL;
+}
+
+int gralloc_drm_is_kms_initialized(struct gralloc_drm_t *drm)
+{
+       return (drm->resources != NULL);
+}
+
 /*
  * Initialize a framebuffer device with KMS info.
  */
@@ -478,14 +845,14 @@ void gralloc_drm_get_kms_info(struct gralloc_drm_t *drm,
                struct framebuffer_device_t *fb)
 {
        *((uint32_t *) &fb->flags) = 0x0;
-       *((uint32_t *) &fb->width) = drm->mode.hdisplay;
-       *((uint32_t *) &fb->height) = drm->mode.vdisplay;
-       *((int *)      &fb->stride) = drm->mode.hdisplay;
-       *((float *)    &fb->fps) = drm->mode.vrefresh;
-
-       *((int *)      &fb->format) = drm->format;
-       *((float *)    &fb->xdpi) = drm->xdpi;
-       *((float *)    &fb->ydpi) = drm->ydpi;
+       *((uint32_t *) &fb->width) = drm->primary.mode.hdisplay;
+       *((uint32_t *) &fb->height) = drm->primary.mode.vdisplay;
+       *((int *)      &fb->stride) = drm->primary.mode.hdisplay;
+       *((float *)    &fb->fps) = drm->primary.mode.vrefresh;
+
+       *((int *)      &fb->format) = drm->primary.fb_format;
+       *((float *)    &fb->xdpi) = drm->primary.xdpi;
+       *((float *)    &fb->ydpi) = drm->primary.ydpi;
        *((int *)      &fb->minSwapInterval) = drm->swap_interval;
        *((int *)      &fb->maxSwapInterval) = drm->swap_interval;
 }