aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--hwcomposer.cpp421
2 files changed, 348 insertions, 75 deletions
diff --git a/Android.mk b/Android.mk
index 3b14cdd..2eb3212 100644
--- a/Android.mk
+++ b/Android.mk
@@ -20,6 +20,7 @@ LOCAL_SHARED_LIBRARIES := \
libdrm \
libhardware \
liblog \
+ libsync \
libutils \
LOCAL_C_INCLUDES := \
@@ -27,6 +28,7 @@ LOCAL_C_INCLUDES := \
external/libdrm \
external/libdrm/include/drm \
system/core/include/utils \
+ system/core/libsync/include \
LOCAL_CFLAGS :=
LOCAL_SRC_FILES := hwcomposer.cpp
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
index 0629f84..175e573 100644
--- a/hwcomposer.cpp
+++ b/hwcomposer.cpp
@@ -19,6 +19,8 @@
#include <fcntl.h>
#include <errno.h>
#include <sys/param.h>
+#include <sys/resource.h>
+#include <pthread.h>
#include <cutils/log.h>
@@ -29,6 +31,8 @@
#include <hardware/hardware.h>
#include <hardware/hwcomposer.h>
+#include <sync/sync.h>
+
#include <gralloc_drm.h>
#include <gralloc_drm_priv.h>
#include <gralloc_drm_handle.h>
@@ -45,7 +49,29 @@ static const uint32_t panel_types[] = {
DRM_MODE_CONNECTOR_DSI,
};
+struct hwc_drm_bo {
+ int fd; /* TODO: This is bad voodoo, remove it */
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
+ uint32_t pitches[4];
+ uint32_t offsets[4];
+ uint32_t gem_handles[4];
+ uint32_t fb_id;
+ int acquire_fence_fd;
+};
+
+struct hwc_worker {
+ pthread_t thread;
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+ bool exit;
+};
+
struct hwc_drm_display {
+ struct hwc_context_t *ctx;
+ int display;
+
uint32_t connector_id;
drmModeModeInfoPtr configs;
@@ -53,6 +79,11 @@ struct hwc_drm_display {
int active_config;
uint32_t active_crtc;
+
+ struct hwc_worker set_worker;
+
+ struct hwc_drm_bo front;
+ struct hwc_drm_bo back;
};
struct hwc_context_t {
@@ -136,45 +167,6 @@ static int hwc_prepare(hwc_composer_device_1_t */* dev */, size_t num_displays,
return ret;
}
-static int hwc_bo_add_fb(hwc_context_t */* ctx */, struct gralloc_drm_bo_t *bo)
-{
- uint32_t pitches[4] = { 0, 0, 0, 0 };
- uint32_t handles[4] = { 0, 0, 0, 0 };
- uint32_t offsets[4] = { 0, 0, 0, 0 };
- int drm_format;
-
- pitches[0] = bo->handle->stride;
- handles[0] = bo->fb_handle;
-
- switch(bo->handle->format) {
- case HAL_PIXEL_FORMAT_RGB_888:
- drm_format = DRM_FORMAT_BGR888;
- break;
- case HAL_PIXEL_FORMAT_BGRA_8888:
- drm_format = DRM_FORMAT_ARGB8888;
- break;
- case HAL_PIXEL_FORMAT_RGBX_8888:
- drm_format = DRM_FORMAT_XBGR8888;
- break;
- case HAL_PIXEL_FORMAT_RGBA_8888:
- drm_format = DRM_FORMAT_ABGR8888;
- break;
- case HAL_PIXEL_FORMAT_RGB_565:
- drm_format = DRM_FORMAT_BGR565;
- break;
- case HAL_PIXEL_FORMAT_YV12:
- drm_format = DRM_FORMAT_YVU420;
- break;
- default:
- 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);
-}
-
static bool hwc_mode_is_equal(drmModeModeInfoPtr a, drmModeModeInfoPtr b)
{
return a->clock == b->clock &&
@@ -194,19 +186,220 @@ static bool hwc_mode_is_equal(drmModeModeInfoPtr a, drmModeModeInfoPtr b)
!strcmp(a->name, b->name);
}
-static int hwc_set_display(hwc_context_t *ctx, int display,
- hwc_display_contents_1_t* display_contents)
+static int hwc_modeset_required(struct hwc_drm_display *hd,
+ bool *modeset_required)
{
- struct hwc_drm_display *hd = NULL;
drmModeCrtcPtr crtc;
drmModeModeInfoPtr m;
- hwc_layer_1_t *layer = NULL;
- int ret, i;
- /* TODO: NUKE THIS */
+ crtc = drmModeGetCrtc(hd->ctx->fd, hd->active_crtc);
+ if (!crtc) {
+ ALOGE("Failed to get crtc for display %d", hd->display);
+ return -ENODEV;
+ }
+
+ m = &hd->configs[hd->active_config];
+
+ /* Do a modeset if we haven't done one, or the mode has changed */
+ if (!crtc->mode_valid || !hwc_mode_is_equal(m, &crtc->mode))
+ *modeset_required = true;
+ else
+ *modeset_required = false;
+
+ drmModeFreeCrtc(crtc);
+
+ return 0;
+}
+
+static void hwc_flip_handler(int /* fd */, unsigned int /* sequence */,
+ unsigned int /* tv_sec */, unsigned int /* tv_usec */,
+ void */* user_data */)
+{
+}
+
+static int hwc_flip(struct hwc_drm_display *hd)
+{
+ fd_set fds;
+ drmEventContext event_context;
+ int ret;
+ bool modeset_required;
+
+ ret = hwc_modeset_required(hd, &modeset_required);
+ if (ret) {
+ ALOGE("Failed to determine if modeset is required %d", ret);
+ return ret;
+ }
+ if (modeset_required) {
+ ret = drmModeSetCrtc(hd->ctx->fd, hd->active_crtc,
+ hd->back.fb_id, 0, 0, &hd->connector_id, 1,
+ &hd->configs[hd->active_config]);
+ if (ret) {
+ ALOGE("Modeset failed for crtc %d",
+ hd->active_crtc);
+ return ret;
+ }
+ return 0;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(hd->ctx->fd, &fds);
+
+ event_context.version = DRM_EVENT_CONTEXT_VERSION;
+ event_context.page_flip_handler = hwc_flip_handler;
+
+ ret = drmModePageFlip(hd->ctx->fd, hd->active_crtc, hd->back.fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, hd);
+ if (ret) {
+ ALOGE("Failed to flip buffer for crtc %d",
+ hd->active_crtc);
+ return ret;
+ }
+
+ do {
+ ret = select(hd->ctx->fd + 1, &fds, NULL, NULL, NULL);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret != 1) {
+ ALOGE("Failed waiting for flip to complete\n");
+ return -EINVAL;
+ }
+ drmHandleEvent(hd->ctx->fd, &event_context);
+
+ return 0;
+}
+
+static int hwc_wait_and_set(struct hwc_drm_display *hd)
+{
+ int ret;
+
+ ret = drmModeAddFB2(hd->back.fd, hd->back.width, hd->back.height,
+ hd->back.format, hd->back.gem_handles, hd->back.pitches,
+ hd->back.offsets, &hd->back.fb_id, 0);
+ if (ret) {
+ ALOGE("could not create drm fb %d", ret);
+ return ret;
+ }
+
+ if (hd->back.acquire_fence_fd >= 0) {
+ ret = sync_wait(hd->back.acquire_fence_fd, -1);
+ if (ret) {
+ ALOGE("Failed to wait for acquire %d", ret);
+ return ret;
+ }
+ }
+
+ ret = hwc_flip(hd);
+ if (ret) {
+ ALOGE("Failed to perform flip\n");
+ return ret;
+ }
+
+ if (hd->front.fb_id) {
+ ret = drmModeRmFB(hd->front.fd, hd->front.fb_id);
+ if (ret) {
+ ALOGE("Failed to rm fb from front %d", ret);
+ return ret;
+ }
+ }
+ hd->front = hd->back;
+
+ memset(&hd->back, 0, sizeof(hd->back));
+ hd->back.acquire_fence_fd = -1;
+
+ return ret;
+}
+
+static void *hwc_set_worker(void *arg)
+{
+ struct hwc_drm_display *hd = (struct hwc_drm_display *)arg;
+ int ret;
+
+ setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+ ret = pthread_mutex_lock(&hd->set_worker.lock);
+ if (ret) {
+ ALOGE("Failed to lock set lock %d", ret);
+ return NULL;
+ }
+
+ do {
+ ret = pthread_cond_wait(&hd->set_worker.cond,
+ &hd->set_worker.lock);
+ if (ret) {
+ ALOGE("Failed to wait on set condition %d", ret);
+ break;
+ } else if (hd->set_worker.exit) {
+ break;
+ }
+
+ ret = hwc_wait_and_set(hd);
+ if (ret)
+ ALOGE("Failed to wait and set %d", ret);
+ } while (true);
+
+ ret = pthread_mutex_unlock(&hd->set_worker.lock);
+ if (ret) {
+ ALOGE("Failed to unlock set lock %d", ret);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static uint32_t hwc_convert_hal_format_to_drm_format(uint32_t hal_format)
+{
+ switch(hal_format) {
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return DRM_FORMAT_BGR888;
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return DRM_FORMAT_ARGB8888;
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ return DRM_FORMAT_XBGR8888;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ return DRM_FORMAT_ABGR8888;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ return DRM_FORMAT_BGR565;
+ case HAL_PIXEL_FORMAT_YV12:
+ return DRM_FORMAT_YVU420;
+ default:
+ ALOGE("Cannot convert hal format to drm format %u", hal_format);
+ return -EINVAL;
+ }
+}
+
+/* TODO: NUKE THIS */
+static int hwc_convert_gralloc_handle_to_drm_bo(hwc_context_t *ctx,
+ buffer_handle_t handle, struct hwc_drm_bo *bo)
+{
gralloc_drm_t *drm = ctx->gralloc_module->drm;
gralloc_drm_handle_t *gr_handle;
- struct gralloc_drm_bo_t *bo;
+ struct gralloc_drm_bo_t *gralloc_bo;
+
+ gr_handle = (gralloc_drm_handle_t *)handle;
+
+ gralloc_bo = gr_handle->data;
+ if (!gralloc_bo) {
+ ALOGE("Could not get drm bo from handle");
+ return -EINVAL;
+ }
+
+ bo->fd = drm->fd;
+ bo->width = gr_handle->width;
+ bo->height = gr_handle->height;
+ bo->format = hwc_convert_hal_format_to_drm_format(gr_handle->format);
+ bo->pitches[0] = gr_handle->stride;
+ bo->gem_handles[0] = gralloc_bo->fb_handle;
+
+ return 0;
+}
+
+static int hwc_set_display(hwc_context_t *ctx, int display,
+ hwc_display_contents_1_t* display_contents)
+{
+ struct hwc_drm_display *hd = NULL;
+ hwc_layer_1_t *layer = NULL;
+ int ret, i;
+ uint32_t fb_id;
ret = hwc_get_drm_display(ctx, display, &hd);
if (ret)
@@ -237,49 +430,46 @@ static int hwc_set_display(hwc_context_t *ctx, int display,
}
}
- gr_handle = (gralloc_drm_handle_t *)layer->handle;
-
- bo = gr_handle->data;
- if (!bo) {
- ALOGE("Could not get drm bo from handle");
- return -EINVAL;
+ ret = pthread_mutex_lock(&hd->set_worker.lock);
+ if (ret) {
+ ALOGE("Failed to lock set lock in set() %d", ret);
+ return ret;
}
- if (!bo->fb_id) {
- ret = hwc_bo_add_fb(ctx, bo);
- if (ret) {
- ALOGE("could not create drm fb %d", ret);
- return ret;
- }
+ if (hd->back.gem_handles[0]) {
+ ALOGE("Failing set, back buffer already exists");
+ ret = -EINVAL;
+ goto out;
}
- crtc = drmModeGetCrtc(ctx->fd, hd->active_crtc);
- if (!crtc) {
- ALOGE("Failed to get crtc for display %d", display);
- return -ENODEV;
+ ret = hwc_convert_gralloc_handle_to_drm_bo(ctx, layer->handle,
+ &hd->back);
+ if (ret) {
+ ALOGE("Failed to convert gralloc handle to drm bo %d", ret);
+ goto out;
}
- m = &hd->configs[hd->active_config];
+ hd->back.acquire_fence_fd = layer->acquireFenceFd;
+ layer->releaseFenceFd = -1;
- /* Do a modeset if we haven't done one, or the mode has changed */
- if (!crtc->mode_valid || !hwc_mode_is_equal(m, &crtc->mode)) {
- ret = drmModeSetCrtc(ctx->fd, crtc->crtc_id, bo->fb_id, 0, 0,
- &hd->connector_id, 1, m);
- if (ret) {
- ALOGE("Modeset failed for crtc %d", crtc->crtc_id);
- goto out;
- }
+ ret = pthread_cond_signal(&hd->set_worker.cond);
+ if (ret) {
+ ALOGE("Failed to signal set worker %d", ret);
goto out;
}
- ret = drmModePageFlip(ctx->fd, crtc->crtc_id, bo->fb_id, 0, 0);
+ ret = pthread_mutex_unlock(&hd->set_worker.lock);
if (ret) {
- ALOGE("Failed to flip buffer for crtc %d", crtc->crtc_id);
- goto out;
+ ALOGE("Failed to unlock set lock in set() %d", ret);
+ return ret;
}
+ return ret;
+
out:
- drmModeFreeCrtc(crtc);
+ if (pthread_mutex_unlock(&hd->set_worker.lock))
+ ALOGE("Failed to unlock set lock in set error handler");
+
return ret;
}
@@ -710,15 +900,88 @@ out:
return ret;
}
+static int hwc_destroy_worker(struct hwc_worker *worker)
+{
+ int ret;
+
+ ret = pthread_mutex_lock(&worker->lock);
+ if (ret) {
+ ALOGE("Failed to lock in destroy() %d", ret);
+ return ret;
+ }
+
+ worker->exit = true;
+
+ ret |= pthread_cond_signal(&worker->cond);
+ if (ret)
+ ALOGE("Failed to signal cond in destroy() %d", ret);
+
+ ret |= pthread_mutex_unlock(&worker->lock);
+ if (ret)
+ ALOGE("Failed to unlock in destroy() %d", ret);
+
+ ret |= pthread_join(worker->thread, NULL);
+ if (ret && ret != ESRCH)
+ ALOGE("Failed to join thread in destroy() %d", ret);
+
+ return ret;
+}
+
+static void hwc_destroy_display(struct hwc_drm_display *hd)
+{
+ int ret;
+
+ if (hwc_destroy_worker(&hd->set_worker))
+ ALOGE("Destroy set worker failed");
+}
+
static int hwc_device_close(struct hw_device_t *dev)
{
struct hwc_context_t *ctx = (struct hwc_context_t *)dev;
+ int i;
+ for (i = 0; i < MAX_NUM_DISPLAYS; i++)
+ hwc_destroy_display(&ctx->displays[i]);
+
+ drmClose(ctx->fd);
free(ctx);
return 0;
}
+static int hwc_initialize_worker(struct hwc_drm_display *hd,
+ struct hwc_worker *worker, void *(*routine)(void*))
+{
+ int ret;
+
+ ret = pthread_cond_init(&worker->cond, NULL);
+ if (ret) {
+ ALOGE("Failed to create worker condition %d", ret);
+ return ret;
+ }
+
+ ret = pthread_mutex_init(&worker->lock, NULL);
+ if (ret) {
+ ALOGE("Failed to initialize worker lock %d", ret);
+ goto err_cond;
+ }
+
+ worker->exit = false;
+
+ ret = pthread_create(&worker->thread, NULL, routine, hd);
+ if (ret) {
+ ALOGE("Could not create worker thread %d", ret);
+ goto err_lock;
+ }
+ return 0;
+
+err_lock:
+ pthread_mutex_destroy(&worker->lock);
+err_cond:
+ pthread_cond_destroy(&worker->cond);
+ return ret;
+}
+
static int hwc_initialize_display(struct hwc_context_t *ctx, int display,
uint32_t connector_id)
{
@@ -729,9 +992,17 @@ static int hwc_initialize_display(struct hwc_context_t *ctx, int display,
if (ret)
return ret;
+ hd->ctx = ctx;
+ hd->display = display;
hd->active_config = -1;
hd->connector_id = connector_id;
+ ret = hwc_initialize_worker(hd, &hd->set_worker, hwc_set_worker);
+ if (ret) {
+ ALOGE("Failed to create set worker %d\n", ret);
+ return ret;
+ }
+
return 0;
}