OSDN Git Service

Fix insignificant warnings
[android-x86/external-drm_gralloc.git] / gralloc_drm_kms.c
index c1035cd..3242957 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 <math.h>
 #include "gralloc_drm.h"
 #include "gralloc_drm_priv.h"
+#include <hardware_legacy/uevent.h>
+
+#include <drm_fourcc.h>
+
+struct uevent {
+       const char *action;
+       const char *path;
+       const char *subsystem;
+       const char *device_name;
+       int major;
+       int minor;
+       int hotplug;
+       int switchstate;
+};
+
+static void parse_event(const char *msg, struct uevent *uevent)
+{
+       uevent->action = "";
+       uevent->path = "";
+       uevent->subsystem = "";
+       uevent->major = -1;
+       uevent->minor = -1;
+       uevent->device_name = "";
+       uevent->hotplug = 0;
+       uevent->switchstate = -1;
+
+       while (*msg) {
+               if (!strncmp(msg, "ACTION=", 7)) {
+                       msg += 7;
+                       uevent->action = msg;
+               } else if (!strncmp(msg, "DEVPATH=", 8)) {
+                       msg += 8;
+                       uevent->path = msg;
+               } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
+                       msg += 10;
+                       uevent->subsystem = msg;
+               } else if (!strncmp(msg, "MAJOR=", 6)) {
+                       msg += 6;
+                       uevent->major = atoi(msg);
+               } else if (!strncmp(msg, "MINOR=", 6)) {
+                       msg += 6;
+                       uevent->minor = atoi(msg);
+               } else if (!strncmp(msg, "DEVNAME=", 8)) {
+                       msg += 8;
+                       uevent->device_name = msg;
+               } else if (!strncmp(msg, "HOTPLUG=1", 9)) {
+                       msg += 9;
+                       uevent->hotplug = 1;
+               } else if (!strncmp(msg, "SWITCH_STATE=", 13)) {
+                       msg += 13;
+                       uevent->switchstate = atoi(msg);
+               }
+
+               /* advance to after the next \0 */
+               while (*msg++)
+                       ;
+       }
+}
 
 /*
  * Return true if a bo needs fb.
@@ -40,22 +102,94 @@ 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_BGRA_8888:
+                       return DRM_FORMAT_XRGB8888;
+               case HAL_PIXEL_FORMAT_RGBX_8888:
+                       return DRM_FORMAT_XBGR8888;
+               case HAL_PIXEL_FORMAT_RGBA_8888:
+//                     return DRM_FORMAT_ABGR8888;
+                       return DRM_FORMAT_XBGR8888;
+               case HAL_PIXEL_FORMAT_RGB_565:
+                       return DRM_FORMAT_RGB565;
+               case HAL_PIXEL_FORMAT_YV12:
+                       return DRM_FORMAT_YUV420;
+               case HAL_PIXEL_FORMAT_DRM_NV12:
+                       return DRM_FORMAT_NV12;
+               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)
+{
+       struct gralloc_drm_t *drm = bo->drm;
+
+       pitches[0] = bo->handle->stride;
+       handles[0] = bo->fb_handle;
+
+       /* driver takes care of HW specific padding, alignment etc. */
+       if (drm->drv->resolve_format)
+               drm->drv->resolve_format(drm->drv, bo,
+                       pitches, offsets, handles);
+
+       return drm_format_from_hal(bo->handle->format);
+}
+
+/*
+ * Returns planes that are supported for a particular format
+ */
+unsigned int planes_for_format(struct gralloc_drm_t *drm,
+       int hal_format)
+{
+       unsigned int i, j, mask = 0;
+       unsigned int drm_format = drm_format_from_hal(hal_format);
+       struct gralloc_drm_plane_t *plane = drm->planes;
+
+       /* no planes available */
+       if (!plane)
+               return 0;
+
+       /* iterate through planes, mark those that match format */
+       for (i=0; i<drm->plane_resources->count_planes; i++, plane++)
+               for (j=0; j<plane->drm_plane->count_formats; j++)
+                       if (plane->drm_plane->formats[j] == drm_format)
+                               mask |= (1U << plane->drm_plane->plane_id);
+
+       return mask;
+}
+
 /*
  * 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] = { 0, 0, 0, 0 };
+       uint32_t offsets[4] = { 0, 0, 0, 0 };
+       uint32_t handles[4] = { 0, 0, 0, 0 };
 
        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);
+
+       if (drm_format == 0) {
+               ALOGE("error resolving drm format");
+               return -EINVAL;
+       }
 
-       return drmModeAddFB(bo->drm->fd,
-                       bo->handle->width, bo->handle->height, bpp, bpp,
-                       bo->handle->stride, bo->fb_handle,
-                       (uint32_t *) &bo->fb_id);
+       return drmModeAddFB2(bo->drm->fd,
+               bo->handle->width, bo->handle->height,
+               drm_format, handles, pitches, offsets,
+               (uint32_t *) &bo->fb_id, 0);
 }
 
 /*
@@ -72,21 +206,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;
 }
@@ -106,6 +241,234 @@ static void page_flip_handler(int fd, unsigned int sequence,
 }
 
 /*
+ * Set a plane.
+ */
+static int gralloc_drm_bo_setplane(struct gralloc_drm_t *drm,
+       struct gralloc_drm_plane_t *plane)
+{
+       struct gralloc_drm_bo_t *bo = NULL;
+       int err;
+
+       if (plane->handle)
+               bo = gralloc_drm_bo_from_handle(plane->handle);
+
+       // create a framebuffer if does not exist
+       if (bo && bo->fb_id == 0) {
+               err = gralloc_drm_bo_add_fb(bo);
+               if (err) {
+                       ALOGE("%s: could not create drm fb, (%s)",
+                               __func__, strerror(-err));
+                       return err;
+               }
+       }
+
+       err = drmModeSetPlane(drm->fd,
+               plane->drm_plane->plane_id,
+               drm->primary->crtc_id,
+               bo ? bo->fb_id : 0,
+               0, // flags
+               plane->dst_x,
+               plane->dst_y,
+               plane->dst_w,
+               plane->dst_h,
+               plane->src_x << 16,
+               plane->src_y << 16,
+               plane->src_w << 16,
+               plane->src_h << 16);
+
+       if (err) {
+               /* clear plane_mask so that this buffer won't be tried again */
+               struct gralloc_drm_handle_t *drm_handle =
+                       (struct gralloc_drm_handle_t *) plane->handle;
+               drm_handle->plane_mask = 0;
+
+               ALOGE("drmModeSetPlane : error (%s) (plane %d crtc %d fb %d)",
+                       strerror(-err),
+                       plane->drm_plane->plane_id,
+                       drm->primary->crtc_id,
+                       bo ? bo->fb_id : 0);
+       }
+
+       if (plane->prev)
+               gralloc_drm_bo_decref(plane->prev);
+
+       if (bo)
+               bo->refcount++;
+
+       plane->prev = bo;
+
+       return err;
+}
+
+/*
+ * Returns if a particular plane is supported with the implementation
+ */
+static unsigned is_plane_supported(const struct gralloc_drm_t *drm,
+       const struct gralloc_drm_plane_t *plane)
+{
+       /* Planes are only supported on primary pipe for now */
+       return plane->drm_plane->possible_crtcs & (1 << drm->primary->pipe);
+}
+
+/*
+ * Sets all the active planes to be displayed.
+ */
+static void gralloc_drm_set_planes(struct gralloc_drm_t *drm)
+{
+       struct gralloc_drm_plane_t *plane = drm->planes;
+       unsigned int i;
+       for (i = 0; i < drm->plane_resources->count_planes;
+               i++, plane++) {
+               /* plane is not in use at all */
+               if (!plane->active && !plane->handle)
+                       continue;
+
+               /* plane is active, safety check if it is supported */
+               if (!is_plane_supported(drm, plane))
+                       ALOGE("%s: plane %d is not supported",
+                                __func__, plane->drm_plane->plane_id);
+
+               /*
+                * Disable overlay if it is not active
+                * or if there is error during setplane
+                */
+               if (!plane->active)
+                       plane->handle = 0;
+
+               if (gralloc_drm_bo_setplane(drm, plane))
+                       plane->active = 0;
+       }
+}
+
+/*
+ * Interface for HWC, used to reserve a plane for a layer.
+ */
+int gralloc_drm_reserve_plane(struct gralloc_drm_t *drm,
+       buffer_handle_t handle,
+       uint32_t id,
+       uint32_t dst_x,
+       uint32_t dst_y,
+       uint32_t dst_w,
+       uint32_t dst_h,
+       uint32_t src_x,
+       uint32_t src_y,
+       uint32_t src_w,
+       uint32_t src_h)
+{
+       int j;
+       struct gralloc_drm_handle_t *drm_handle =
+               gralloc_drm_handle(handle);
+       int plane_count = drm->plane_resources->count_planes;
+       struct gralloc_drm_plane_t *plane = drm->planes;
+
+       /* no supported planes for this handle */
+       if (!drm_handle->plane_mask) {
+               ALOGE("%s: buffer %p cannot be shown on a plane\n",
+                       __func__, drm_handle);
+               return -EINVAL;
+       }
+
+       for (j = 0; j < plane_count; j++, plane++) {
+
+               /*
+                * handle may be suitable to be shown on a plane, in
+                * addition we need to check that this particular plane
+                * is supported by the current implementation
+                */
+               if (!is_plane_supported(drm, plane))
+                       continue;
+
+               /* if plane is available and can support this buffer */
+               if (!plane->active &&
+                       drm_handle->plane_mask &
+                       (1U << plane->drm_plane->plane_id)) {
+
+                       plane->dst_x = dst_x;
+                       plane->dst_y = dst_y;
+                       plane->dst_w = dst_w;
+                       plane->dst_h = dst_h;
+                       plane->src_x = src_x;
+                       plane->src_y = src_y;
+                       plane->src_w = src_w;
+                       plane->src_h = src_h;
+                       plane->handle = handle;
+                       plane->id = id;
+                       plane->active = 1;
+
+                       return 0;
+               }
+       }
+
+       /* no free planes available */
+       return -EBUSY;
+}
+
+/*
+ * Interface for HWC, used to disable all the overlays. Plane id
+ * is also set to 0 as it should be mappable to a particular layer only
+ * if it has been reserved with 'reserve_plane'.
+ */
+void gralloc_drm_disable_planes(struct gralloc_drm_t *drm)
+{
+       struct gralloc_drm_plane_t *plane = drm->planes;
+       unsigned int i;
+
+       for (i = 0; i < drm->plane_resources->count_planes; i++, plane++) {
+               plane->active = 0;
+               plane->id = 0;
+       }
+}
+
+/*
+ * Interface for HWC, used to change handle of a reserved plane.
+ */
+int gralloc_drm_set_plane_handle(struct gralloc_drm_t *drm,
+       uint32_t id, buffer_handle_t handle)
+{
+       struct gralloc_drm_plane_t *plane = drm->planes;
+       unsigned i;
+
+       for (i = 0; i < drm->plane_resources->count_planes; i++, plane++)
+               if (plane->active && plane->id == id) {
+                       plane->handle = handle;
+                       return 0;
+               }
+
+       return -EINVAL;
+}
+
+static int drm_kms_blit_to_mirror_connectors(struct gralloc_drm_t *drm, struct gralloc_drm_bo_t *bo)
+{
+       int ret = 0;
+       for (int i = 1; i < drm->output_capacity; i++) {
+               struct gralloc_drm_output *output = &drm->outputs[i];
+
+               if (output->active && output->output_mode == DRM_OUTPUT_CLONED && output->bo) {
+
+                       int dst_x1 = 0, dst_y1 = 0;
+
+                       if (output->bo->handle->width > bo->handle->width)
+                               dst_x1 = (output->bo->handle->width - bo->handle->width) / 2;
+                       if (output->bo->handle->height > bo->handle->height)
+                               dst_y1 = (output->bo->handle->height - bo->handle->height) / 2;
+
+                       drm->drv->blit(drm->drv, output->bo, bo,
+                                       dst_x1, dst_y1,
+                                       dst_x1 + bo->handle->width,
+                                       dst_y1 + bo->handle->height,
+                                       0, 0, bo->handle->width, bo->handle->height);
+
+                       ret = drmModePageFlip(drm->fd, output->crtc_id, output->bo->fb_id, 0, NULL);
+                       if (ret && errno != EBUSY)
+                               ALOGE("failed to perform page flip for output (%s) (crtc %d fb %d))",
+                                               strerror(errno), output->crtc_id, output->bo->fb_id);
+               }
+       }
+
+       return ret;
+}
+
+/*
  * Schedule a page flip.
  */
 static int drm_kms_page_flip(struct gralloc_drm_t *drm,
@@ -120,7 +483,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 +492,22 @@ 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->outputs_mutex);
+       drm_kms_blit_to_mirror_connectors(drm, bo);
+       pthread_mutex_unlock(&drm->outputs_mutex);
+
+       /* set planes to be displayed */
+       gralloc_drm_set_planes(drm);
+
+       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 */
+               if (errno != EBUSY)
+                       drm->first_post = 1;
+       }
        else
                drm->next_front = bo;
 
@@ -148,6 +523,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 +537,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 +563,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 +580,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;
        }
 
@@ -215,14 +593,16 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
                        dst = (drm->next_front) ?
                                drm->next_front :
                                drm->current_front;
-                       drm->drv->copy(drm->drv, dst, bo, 0, 0,
+                       drm->drv->blit(drm->drv, dst, bo, 0, 0,
+                                       bo->handle->width,
+                                       bo->handle->height,
+                                       0, 0,
                                        bo->handle->width,
                                        bo->handle->height);
                        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 +610,14 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
                                drm->next_front = NULL;
                }
 
+               pthread_mutex_lock(&drm->outputs_mutex);
+               for (int i = 1; i < drm->output_capacity; i++) {
+                       struct gralloc_drm_output *output = &drm->outputs[i];
+                       if (output->active && output->output_mode == DRM_OUTPUT_CLONED && output->bo)
+                               drm_kms_set_crtc(drm, output, output->bo->fb_id);
+               }
+               pthread_mutex_unlock(&drm->outputs_mutex);
+
                return ret;
        }
 
@@ -244,22 +632,36 @@ int gralloc_drm_bo_post(struct gralloc_drm_bo_t *bo)
                         * will be written by CPU
                         */
                        if (drm->mode_sync_flip ||
-                           (drm->current_front->handle->usage &
-                            GRALLOC_USAGE_SW_WRITE_MASK))
+                               (drm->current_front->handle->usage &
+                                GRALLOC_USAGE_SW_WRITE_MASK))
                                drm_kms_page_flip(drm, NULL);
                }
                break;
        case DRM_SWAP_COPY:
                drm_kms_wait_for_post(drm, 0);
-               drm->drv->copy(drm->drv, drm->current_front,
+               drm->drv->blit(drm->drv, drm->current_front,
                                bo, 0, 0,
                                bo->handle->width,
+                               bo->handle->height,
+                               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->outputs_mutex);
+               for (int i = 1; i < drm->output_capacity; i++) {
+                       struct gralloc_drm_output *output = &drm->outputs[i];
+                       if (output->active && output->output_mode == DRM_OUTPUT_CLONED && output->bo)
+                               drm_kms_set_crtc(drm, output, output->bo->fb_id);
+               }
+               pthread_mutex_unlock(&drm->outputs_mutex);
+
                drm->current_front = bo;
                break;
        default:
@@ -275,18 +677,18 @@ static struct gralloc_drm_t *drm_singleton;
 
 static void on_signal(int sig)
 {
-   struct sigaction act;
+       struct gralloc_drm_t *drm = drm_singleton;
 
-   /* 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);
-   }
+       /* 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);
+       exit(-1);
 }
 
 static void drm_kms_init_features(struct gralloc_drm_t *drm)
@@ -322,12 +724,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->fb_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 +755,236 @@ 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);
+}
+
+#define MARGIN_PERCENT 1.8   /* % of active vertical image*/
+#define CELL_GRAN 8.0   /* assumed character cell granularity*/
+#define MIN_PORCH 1 /* minimum front porch   */
+#define V_SYNC_RQD 3 /* width of vsync in lines   */
+#define H_SYNC_PERCENT 8.0   /* width of hsync as % of total line */
+#define MIN_VSYNC_PLUS_BP 550.0 /* min time of vsync + back porch (microsec) */
+#define M 600.0 /* blanking formula gradient */
+#define C 40.0  /* blanking formula offset   */
+#define K 128.0 /* blanking formula scaling factor   */
+#define J 20.0  /* blanking formula scaling factor   */
+/* C' and M' are part of the Blanking Duty Cycle computation */
+#define C_PRIME   (((C - J) * K / 256.0) + J)
+#define M_PRIME   (K / 256.0 * M)
+
+static drmModeModeInfoPtr generate_mode(int h_pixels, int v_lines, float freq)
+{
+       float h_pixels_rnd;
+       float v_lines_rnd;
+       float v_field_rate_rqd;
+       float top_margin;
+       float bottom_margin;
+       float interlace;
+       float h_period_est;
+       float vsync_plus_bp;
+       float v_back_porch;
+       float total_v_lines;
+       float v_field_rate_est;
+       float h_period;
+       float v_field_rate;
+       float v_frame_rate;
+       float left_margin;
+       float right_margin;
+       float total_active_pixels;
+       float ideal_duty_cycle;
+       float h_blank;
+       float total_pixels;
+       float pixel_freq;
+       float h_freq;
+
+       float h_sync;
+       float h_front_porch;
+       float v_odd_front_porch_lines;
+       int interlaced = 0;
+       int margins = 0;
+
+       drmModeModeInfoPtr m = malloc(sizeof(drmModeModeInfo));
+
+       h_pixels_rnd = rint((float) h_pixels / CELL_GRAN) * CELL_GRAN;
+       v_lines_rnd = interlaced ? rint((float) v_lines) / 2.0 : rint((float) v_lines);
+       v_field_rate_rqd = interlaced ? (freq * 2.0) : (freq);
+       top_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
+       bottom_margin = margins ? rint(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0);
+       interlace = interlaced ? 0.5 : 0.0;
+       h_period_est = (((1.0 / v_field_rate_rqd) - (MIN_VSYNC_PLUS_BP / 1000000.0)) / (v_lines_rnd + (2 * top_margin) + MIN_PORCH + interlace) * 1000000.0);
+       vsync_plus_bp = rint(MIN_VSYNC_PLUS_BP / h_period_est);
+       v_back_porch = vsync_plus_bp - V_SYNC_RQD;
+       total_v_lines = v_lines_rnd + top_margin + bottom_margin + vsync_plus_bp + interlace + MIN_PORCH;
+       v_field_rate_est = 1.0 / h_period_est / total_v_lines * 1000000.0;
+       h_period = h_period_est / (v_field_rate_rqd / v_field_rate_est);
+       v_field_rate = 1.0 / h_period / total_v_lines * 1000000.0;
+       v_frame_rate = interlaced ? v_field_rate / 2.0 : v_field_rate;
+       left_margin = margins ? rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : 0.0;
+       right_margin = margins ? rint(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN) * CELL_GRAN : 0.0;
+       total_active_pixels = h_pixels_rnd + left_margin + right_margin;
+       ideal_duty_cycle = C_PRIME - (M_PRIME * h_period / 1000.0);
+       h_blank = rint(total_active_pixels * ideal_duty_cycle / (100.0 - ideal_duty_cycle) / (2.0 * CELL_GRAN)) * (2.0 * CELL_GRAN);
+       total_pixels = total_active_pixels + h_blank;
+       pixel_freq = total_pixels / h_period;
+       h_freq = 1000.0 / h_period;
+       h_sync = rint(H_SYNC_PERCENT / 100.0 * total_pixels / CELL_GRAN) * CELL_GRAN;
+       h_front_porch = (h_blank / 2.0) - h_sync;
+       v_odd_front_porch_lines = MIN_PORCH + interlace;
+
+       m->clock = ceil(pixel_freq) * 1000;
+       m->hdisplay = (int) (h_pixels_rnd);
+       m->hsync_start = (int) (h_pixels_rnd + h_front_porch);
+       m->hsync_end = (int) (h_pixels_rnd + h_front_porch + h_sync);
+       m->htotal = (int) (total_pixels);
+       m->hskew = 0;
+       m->vdisplay = (int) (v_lines_rnd);
+       m->vsync_start = (int) (v_lines_rnd + v_odd_front_porch_lines);
+       m->vsync_end = (int) (int) (v_lines_rnd + v_odd_front_porch_lines + V_SYNC_RQD);
+       m->vtotal = (int) (total_v_lines);
+       m->vscan = 0;
+       m->vrefresh = freq;
+       m->flags = 10;
+       m->type = 64;
+
+       return (m);
+}
+
+static int mode_distance_best_fit(
+               int xres_base,
+               int yres_base,
+               int xres,
+               int yres,
+               double prefered_aspect)
+{
+       const double eps = 0.3;
+
+       if (xres_base > xres || yres_base > yres) {
+               // if the res cannot cover base res, return max int
+               return INT_MAX;
+       } else if (fabs((double) xres / yres - prefered_aspect) > eps) {
+               // if the aspect is too different with prefered_aspect, return max int
+               return INT_MAX;
+       } else {
+               return (xres - xres_base) * (xres - xres_base) +
+                               (yres - yres_base) * (yres - yres_base) +
+                               ((xres / yres) - prefered_aspect) * ((xres / yres) - prefered_aspect);
+       }
+}
+
+static int mode_distance_closest(int xres_base, int yres_base, int xres, int yres) {
+       return (xres - xres_base) * (xres - xres_base) +
+                       (yres - yres_base) * (yres - yres_base);
+}
+
+static drmModeModeInfoPtr find_mode(drmModeConnectorPtr connector, int *bpp, drmModeModeInfoPtr primary_mode)
+{
+       char value[PROPERTY_VALUE_MAX];
+       drmModeModeInfoPtr mode;
+       int dist, i;
+       int xres = 0, yres = 0, rate = 0;
+       int forcemode = 0;
+       int bestfit = 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 if (property_get("debug.drm.mode.force", value, NULL)) {
+               char *p = value, *end;
+               *bpp = 0;
+
+               /* parse <xres>x<yres>[@<refreshrate>] */
+               if (sscanf(value, "%dx%d@%d", &xres, &yres, &rate) != 3) {
+                       rate = 60;
+                       if (sscanf(value, "%dx%d", &xres, &yres) != 2)
+                               xres = yres = 0;
+               }
+
+               if (xres && yres && rate) {
+                       ALOGI("will use %dx%d@%dHz", xres, yres, rate);
+                       forcemode = 1;
+               }
+       } else if (primary_mode != NULL) {
+               xres = primary_mode->hdisplay;
+               yres = primary_mode->vdisplay;
+               *bpp = 0;
+               bestfit = 1;
+               ALOGI("will find the best fit for %dx%d", xres, yres);
+       } else {
+               *bpp = 0;
+       }
+
+       dist = INT_MAX;
+
+       if (forcemode)
+               mode = generate_mode(xres, yres, rate);
+       else {
+               mode = NULL;
+               for (i = 0; i < connector->count_modes; i++) {
+                       drmModeModeInfoPtr m = &connector->modes[i];
+                       int tmp;
+
+                       if (xres && yres) {
+                               if (bestfit) {
+                                       tmp = mode_distance_best_fit(
+                                                       xres,
+                                                       yres,
+                                                       m->hdisplay,
+                                                       m->vdisplay,
+                                                       (double) connector->modes[0].hdisplay / connector->modes[0].vdisplay);
+                               } else {
+                                       tmp = mode_distance_closest(xres, yres, m->hdisplay, m->vdisplay);
+                               }
+                       }
+                       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];
+
+       ALOGI("Established mode:");
+       ALOGI("clock: %d, hdisplay: %d, hsync_start: %d, hsync_end: %d, htotal: %d, hskew: %d", mode->clock, mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal, mode->hskew);
+       ALOGI("vdisplay: %d, vsync_start: %d, vsync_end: %d, vtotal: %d, vscan: %d, vrefresh: %d", mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal, mode->vscan, mode->vrefresh);
+       ALOGI("flags: %d, type: %d, name %s", mode->flags, mode->type, mode->name);
+
+       *bpp /= 8;
+
+       return mode;
 }
 
+static int used_crtcs = 0;
+
 /*
  * 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,89 +993,348 @@ 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) &&
+                       (used_crtcs & (1 << i)) != (1 << i))
                        break;
        }
+
+       used_crtcs |= (1 << i);
+
        drmModeFreeEncoder(encoder);
        if (i == drm->resources->count_crtcs)
                return -EINVAL;
 
-       drm->crtc_id = drm->resources->crtcs[i];
-       drm->connector_id = connector->connector_id;
+       output->bo = NULL;
+       output->crtc_id = drm->resources->crtcs[i];
+       output->connector_id = connector->connector_id;
+       output->pipe = i;
+
+       /* print connector info */
+       if (connector->count_modes > 1) {
+               ALOGI("there are %d modes on connector 0x%x, type %d",
+                               connector->count_modes,
+                               connector->connector_id,
+                               connector->connector_type);
+               for (i = 0; i < connector->count_modes; i++)
+                       ALOGI("  %s", connector->modes[i].name);
+       }
+       else {
+               ALOGI("there is one mode on connector 0x%d: %s",
+                               connector->connector_id,
+                               connector->modes[0].name);
+       }
 
-       /* 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;
-               }
+       if (drm->primary && output != drm->primary && property_get_bool("persist.remixos.disp_best_fit", 1)) {
+               mode = find_mode(connector, &bpp, &drm->primary->mode);
+       } else {
+               mode = find_mode(connector, &bpp, NULL);
        }
-       /* no preference; use the first */
-       if (!mode)
-               mode = &connector->modes[0];
 
-       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_RGBA_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;
        }
 
 #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;
 }
 
+static struct gralloc_drm_output*
+drm_kms_init_with_new_connector(struct gralloc_drm_t *drm, drmModeConnectorPtr connector)
+{
+       struct gralloc_drm_output *output = NULL;
+
+       for (int i=0;i<drm->output_capacity;i++) {
+               if (!drm->outputs[i].active) {
+                       output = &drm->outputs[i];
+                       break;
+               }
+       }
+
+       if (output && !drm_kms_init_with_connector(drm, output, connector)) {
+               drm->output_count++;
+               output->active = 1;
+               if (!drm->primary) {
+                       drm->primary = output;
+               }
+       }
+
+       return output;
+}
+
+
+/*
+ * 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 = 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;
+}
+
+
+/*
+ * Initializes external output with a connector and allocates
+ * a private framebuffer for it. This is called on startup if
+ * external cable is connected and also on hotplug events.
+ */
+static int init_external_output(struct gralloc_drm_t *drm,
+       drmModeConnectorPtr connector)
+{
+       struct gralloc_drm_output *output;
+       output = drm_kms_init_with_new_connector(drm, connector);
+       if (output == NULL || !output->active) {
+               ALOGW("connector 0x%x output is null or not active", connector->connector_id);
+               return -EINVAL;
+       }
+
+       ALOGD("%s, allocate private buffer for hdmi [%dx%d]",
+               __func__, output->mode.hdisplay, output->mode.vdisplay);
+
+       output->bo = gralloc_drm_bo_create(drm,
+               output->mode.hdisplay, output->mode.vdisplay,
+               output->fb_format,
+               GRALLOC_USAGE_HW_RENDER);
+
+       int err = gralloc_drm_bo_add_fb(output->bo);
+       if (err) {
+               ALOGE("%s: could not create drm fb, (%s)",
+                       __func__, strerror(-err));
+               return err;
+       }
+
+       output->output_mode = DRM_OUTPUT_CLONED;
+       output->active = 1;
+
+       /* This is a hack to workaround mirror mode render error */
+       drm_kms_set_crtc(drm, output, output->bo->fb_id);
+
+       return 0;
+}
+
+
+static int init_connectors(struct gralloc_drm_t *drm)
+{
+       if (!drm->resources)
+               return -1;
+
+       for (int i = 0; i<drm->resources->count_connectors; i++) {
+               drmModeConnectorPtr connector = drmModeGetConnector(drm->fd,
+                               drm->resources->connectors[i]);
+               if (connector) {
+                       if (connector->connection == DRM_MODE_CONNECTED &&
+                               connector->connector_id != drm->primary->connector_id) {
+                               init_external_output(drm, connector);
+                       } else if (connector->connection == DRM_MODE_DISCONNECTED) {
+                               for (int j = 1; j < drm->output_capacity; j++) {
+                                       if (drm->outputs[j].connector_id == connector->connector_id && drm->outputs[j].active) {
+                                               drm->outputs[j].active = 0;
+                                               used_crtcs &= ~(1 << drm->outputs[j].pipe);
+                                               gralloc_drm_bo_decref(drm->outputs[j].bo);
+                                               drm->outputs[j].bo = NULL;
+                                               break;
+                                       }
+                               }
+                       }
+                       drmModeFreeConnector(connector);
+               }
+       }
+
+       return 0;
+}
+
+
+/*
+ * Thread that listens to uevents and checks if hdmi state changes
+ */
+static void *extcon_observer(void *data)
+{
+       char uevent_desc[2048];
+       drmModeConnectorPtr hdmi;
+       struct gralloc_drm_t *drm =
+               (struct gralloc_drm_t *) data;
+       struct uevent event;
+
+       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) continue;
+
+               parse_event(uevent_desc, &event);
+               ALOGD_IF(0, "event { '%s', '%s', '%s', %d, %d, %d, %d }\n",
+                               event.action, event.path, event.subsystem,
+                               event.major, event.minor,
+                               event.switchstate, event.hotplug);
+
+               if (!strcmp(event.path, "devices/virtual/switch/hdmi")) {
+                       if (event.switchstate != -1) {
+                               pthread_mutex_lock(&drm->outputs_mutex);
+
+                               init_connectors(drm);
+
+                               /* will trigger modeset */
+                               drm->first_post = 1;
+
+                               pthread_mutex_unlock(&drm->outputs_mutex);
+                       }
+               } else if (!strcmp(event.subsystem, "drm") &&
+                               !strcmp(event.device_name, "dri/card0") && event.hotplug) {
+                       pthread_mutex_lock(&drm->outputs_mutex);
+
+                       init_connectors(drm);
+
+                       /* will trigger modeset */
+                       drm->first_post = 1;
+
+                       pthread_mutex_unlock(&drm->outputs_mutex);
+               }
+       }
+
+       pthread_exit(NULL);
+       return 0;
+}
+
+
 /*
  * Initialize KMS.
  */
 int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
 {
-       int i, ret;
-
        if (drm->resources)
                return 0;
 
        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 {
+               unsigned int i, j;
+
+               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++) {
+                       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);
+               }
+       }
+
+       drm->output_capacity = drm->resources->count_connectors;
+       drm->output_count = 0;
+
+       drm->outputs = calloc(sizeof(*drm->outputs), drm->output_capacity);
+
        /* find the crtc/connector/mode to use */
-       for (i = 0; i < drm->resources->count_connectors; i++) {
+       uint32_t internal_connectors[] = {
+               DRM_MODE_CONNECTOR_LVDS,
+               DRM_MODE_CONNECTOR_DSI,
+       };
+       for (size_t i = 0; i < sizeof(internal_connectors) / sizeof(uint32_t); i++) {
                drmModeConnectorPtr connector;
-
-               connector = drmModeGetConnector(drm->fd,
-                               drm->resources->connectors[i]);
+               connector = fetch_connector(drm, internal_connectors[i]);
                if (connector) {
-                       if (connector->connection == DRM_MODE_CONNECTED) {
-                               if (!drm_kms_init_with_connector(drm,
-                                                       connector))
+                       drm_kms_init_with_new_connector(drm, connector);
+                       drmModeFreeConnector(connector);
+                       break;
+               }
+       }
+
+       /* if still no connector, find first connected connector and try it */
+       if (!drm->primary) {
+               int i;
+               ALOGI("Try to find the first connected connector");
+
+               for (i = 0; i < drm->resources->count_connectors; i++) {
+                       drmModeConnectorPtr connector;
+
+                       connector = drmModeGetConnector(drm->fd,
+                                       drm->resources->connectors[i]);
+                       if (connector) {
+                               bool found = (connector->connection == DRM_MODE_CONNECTED)
+                                               && !!drm_kms_init_with_new_connector(drm, connector);
+                               drmModeFreeConnector(connector);
+                               if (found)
                                        break;
                        }
+               }
+               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;
+       /* Mirror mode need driver support for blitting. which is not implemented for
+               drivers other than intel. skip external detection for them to avoid crash
+       */
+       if (drm->drv->blit) {
+               init_connectors(drm);
+
+               /* launch external display observer thread */
+               pthread_mutex_init(&drm->outputs_mutex, NULL);
+               pthread_create(&drm->hotplug_thread, NULL, extcon_observer, drm);
        }
 
        drm_kms_init_features(drm);
@@ -464,6 +1343,61 @@ int gralloc_drm_init_kms(struct gralloc_drm_t *drm)
        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;
+       }
+
+       /* destroy private buffer of external output */
+       for (int i = 1; i < drm->output_capacity; i++)
+               if (drm->outputs[i].bo)
+                       gralloc_drm_bo_decref(drm->outputs[i].bo);
+
+       free(drm->outputs);
+
+       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.
  */
@@ -471,14 +1405,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->fb_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;
 }