From 2b1f1af17f8e8f199cb0dd4f7f1a225529b357c5 Mon Sep 17 00:00:00 2001 From: Chia-I Wu Date: Mon, 11 Oct 2010 16:06:47 +0800 Subject: android: Add DRM-based gralloc. --- src/gralloc/gralloc_gem.c | 119 +++++++++++++++ src/gralloc/gralloc_gem.h | 77 ++++++++++ src/gralloc/gralloc_gem_i915.c | 211 ++++++++++++++++++++++++++ src/gralloc/gralloc_gem_pipe.c | 333 +++++++++++++++++++++++++++++++++++++++++ src/gralloc/gralloc_kms.c | 243 ++++++++++++++++++++++++++++++ src/gralloc/gralloc_kms.h | 19 +++ src/gralloc/gralloc_mod.c | 328 ++++++++++++++++++++++++++++++++++++++++ src/gralloc/gralloc_mod.h | 65 ++++++++ 8 files changed, 1395 insertions(+) create mode 100644 src/gralloc/gralloc_gem.c create mode 100644 src/gralloc/gralloc_gem.h create mode 100644 src/gralloc/gralloc_gem_i915.c create mode 100644 src/gralloc/gralloc_gem_pipe.c create mode 100644 src/gralloc/gralloc_kms.c create mode 100644 src/gralloc/gralloc_kms.h create mode 100644 src/gralloc/gralloc_mod.c create mode 100644 src/gralloc/gralloc_mod.h diff --git a/src/gralloc/gralloc_gem.c b/src/gralloc/gralloc_gem.c new file mode 100644 index 0000000000..a3df97f89b --- /dev/null +++ b/src/gralloc/gralloc_gem.c @@ -0,0 +1,119 @@ +#define LOG_TAG "GRALLOC-GEM" + +#include +#include +#include +#include +#include +#include +#include + +#include "gralloc_mod.h" +#include "gralloc_gem.h" + +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define DRM_PATH "/dev/dri/card0" + +static int32_t drm_gem_pid = 0; + +static int +drm_gem_get_pid(void) +{ + if (unlikely(!drm_gem_pid)) + android_atomic_write((int32_t) getpid(), &drm_gem_pid); + return drm_gem_pid; +} + +static int +drm_gem_init_locked(struct drm_module_t *drm) +{ + int ret; + + if (drm->fd >= 0) + return 0; + + drm->fd = open(DRM_PATH, O_RDWR); + if (drm->fd < 0) { + LOGE("failed to open %s", DRM_PATH); + return -EINVAL; + } + + return 0; +} + +int +drm_gem_init(struct drm_module_t *drm) +{ + int ret; + + pthread_mutex_lock(&drm->mutex); + ret = drm_gem_init_locked(drm); + pthread_mutex_unlock(&drm->mutex); + + return ret; +} + +int +drm_gem_get_magic(struct drm_module_t *drm, int32_t *magic) +{ + int ret; + + ret = drm_gem_init(drm); + if (ret) + return ret; + + return drmGetMagic(drm->fd, (drm_magic_t *) magic); +} + +int +drm_gem_auth_magic(struct drm_module_t *drm, int32_t magic) +{ + int ret; + + ret = drm_gem_init(drm); + if (ret) + return ret; + + return drmAuthMagic(drm->fd, (drm_magic_t) magic); +} + +struct drm_bo_t * +drm_gem_create_bo(int width, int height, int format, int usage) +{ + struct drm_bo_t *bo; + + bo = calloc(1, sizeof(*bo)); + if (!bo) + return NULL; + + bo->base.version = sizeof(bo->base); + bo->base.numInts = DRM_HANDLE_NUM_INTS; + bo->base.numFds = DRM_HANDLE_NUM_FDS; + + bo->magic = DRM_HANDLE_MAGIC; + + bo->width = width; + bo->height = height; + bo->format = format; + bo->usage = usage; + + bo->pid = drm_gem_get_pid(); + + return bo; +} + +struct drm_bo_t * +drm_gem_validate(buffer_handle_t handle) +{ + struct drm_bo_t *bo = drm_gem_get(handle); + + if (bo && unlikely(bo->pid != drm_gem_pid)) { + bo->pid = drm_gem_get_pid(); + bo->fb_handle = 0; + bo->fb_id = 0; + bo->data = 0; + } + + return bo; +} diff --git a/src/gralloc/gralloc_gem.h b/src/gralloc/gralloc_gem.h new file mode 100644 index 0000000000..e6ef176efa --- /dev/null +++ b/src/gralloc/gralloc_gem.h @@ -0,0 +1,77 @@ +#ifndef _GRALLOC_GEM_H_ +#define _GRALLOC_GEM_H_ + +#include +#include "gralloc_mod.h" + +struct drm_bo_t { + native_handle_t base; + +#define DRM_HANDLE_MAGIC 0x12345678 +#define DRM_HANDLE_NUM_INTS 11 +#define DRM_HANDLE_NUM_FDS 0 + int magic; + + int width; + int height; + int format; + int usage; + + int name; + int stride; + + /* these fields are undefined until validated */ + int pid; + int fb_handle; + int fb_id; + int data; /* pointer as int? 32-bit only! */ +}; + +int +drm_gem_init(struct drm_module_t *drm); + +int +drm_gem_get_magic(struct drm_module_t *drm, int32_t *magic); + +int +drm_gem_auth_magic(struct drm_module_t *drm, int32_t magic); + +static inline struct drm_bo_t * +drm_gem_get(buffer_handle_t handle) +{ + struct drm_bo_t *bo = (struct drm_bo_t *) handle; + + if (bo->base.version != sizeof(bo->base) || + bo->base.numInts != DRM_HANDLE_NUM_INTS || + bo->base.numFds != DRM_HANDLE_NUM_FDS || + bo->magic != DRM_HANDLE_MAGIC) + bo = NULL; + + return bo; +} + +struct drm_bo_t * +drm_gem_validate(buffer_handle_t handle); + +struct drm_bo_t * +drm_gem_create_bo(int width, int height, int format, int usage); + + +int +drm_gem_drv_init(struct drm_module_t *drm); + +struct drm_bo_t * +drm_gem_drv_alloc(struct drm_module_t *drm, int width, int height, + int format, int usage, int *stride); + +void +drm_gem_drv_free(struct drm_module_t *drm, struct drm_bo_t *bo); + +int +drm_gem_drv_map(struct drm_module_t *drm, struct drm_bo_t *bo, + int x, int y, int w, int h, int enable_write, void **addr); + +void +drm_gem_drv_unmap(struct drm_module_t *drm, struct drm_bo_t *bo); + +#endif /* _GRALLOC_GEM_H_ */ diff --git a/src/gralloc/gralloc_gem_i915.c b/src/gralloc/gralloc_gem_i915.c new file mode 100644 index 0000000000..2822fe5c12 --- /dev/null +++ b/src/gralloc/gralloc_gem_i915.c @@ -0,0 +1,211 @@ +#define LOG_TAG "GRALLOC-I915" + +#include +#include +#include +#include +#include +#include + +#include "gralloc_mod.h" +#include "gralloc_gem.h" + +static void +drm_gem_drv_init_features_locked(struct drm_module_t *drm) +{ + struct drm_i915_getparam gp; + + drm_intel_bufmgr *bufmgr = (drm_intel_bufmgr *) drm->gem; + + drm->mode_dirty_fb = 0; + + memset(&gp, 0, sizeof(gp)); + gp.param = I915_PARAM_HAS_PAGEFLIPPING; + gp.value = &drm->mode_page_flip; + if (!drmCommandWriteRead(drm->fd, DRM_I915_GETPARAM, &gp, sizeof(gp))) + drm->mode_page_flip = *gp.value; + else + drm->mode_page_flip = 0; + + if (drm->resources) { + int pipe; + + pipe = drm_intel_get_pipe_from_crtc_id(bufmgr, drm->crtc_id); + drm->swap_interval = (pipe >= 0) ? 1 : 0; + drm->vblank_secondary = (pipe > 0); + } + else { + drm->swap_interval = 0; + } + + /* XXX there is a bug in the kernel module */ + drm->mode_page_flip = 0; + +} + +static int +drm_gem_drv_init_locked(struct drm_module_t *drm) +{ + if (drm->gem) + return 0; + + drm->gem = (void *) drm_intel_bufmgr_gem_init(drm->fd, 16 * 1024); + if (!drm->gem) { + LOGE("failed to create buffer manager"); + return -ENOMEM; + } + + drm_gem_drv_init_features_locked(drm); + + return 0; +} + +int +drm_gem_drv_init(struct drm_module_t *drm) +{ + int ret; + + pthread_mutex_lock(&drm->mutex); + ret = drm_gem_drv_init_locked(drm); + pthread_mutex_unlock(&drm->mutex); + + return ret; +} + +static uint32_t +drm_gem_get_tiling(struct drm_bo_t *bo) +{ + uint32_t tiling = I915_TILING_NONE; + + if (bo->usage & GRALLOC_USAGE_SW_READ_OFTEN) + return tiling; + + if (bo->usage & GRALLOC_USAGE_HW_FB || + bo->usage & GRALLOC_USAGE_HW_TEXTURE) { + if (bo->width >= 64) + tiling = I915_TILING_X; + } + + return tiling; +} + +struct drm_bo_t * +drm_gem_drv_alloc(struct drm_module_t *drm, int width, int height, + int format, int usage, int *stride) +{ + drm_intel_bufmgr *bufmgr = (drm_intel_bufmgr *) drm->gem; + struct drm_bo_t *bo; + drm_intel_bo *ibo; + int aligned_width, aligned_height, cpp; + unsigned long pitch, flags; + uint32_t tiling; + const char *name; + + bo = drm_gem_create_bo(width, height, format, usage); + if (!bo) + return NULL; + + cpp = drm_mod_get_bpp(format); + if (!cpp) { + LOGE("unrecognized format 0x%x", format); + free(bo); + return NULL; + } + + if (usage & GRALLOC_USAGE_HW_FB) { + name = "gralloc-fb"; + aligned_width = (width + 63) & ~63; + } + if (usage & GRALLOC_USAGE_HW_TEXTURE) { + name = "gralloc-texture"; + aligned_width = (width + 3) & ~3; + aligned_height = (height + 1) & ~1; + } + else { + name = "gralloc-buf"; + aligned_width = width; + aligned_height = height; + } + + tiling = drm_gem_get_tiling(bo); + + flags = 0x0; + if (bo->usage & (GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_FB)) + flags |= BO_ALLOC_FOR_RENDER; + + ibo = drm_intel_bo_alloc_tiled(bufmgr, name, + aligned_width, aligned_height, cpp, &tiling, &pitch, flags); + if (!ibo) { + LOGE("failed to allocate ibo %dx%dx%d", width, height, cpp); + free(bo); + return NULL; + } + + if (bo->usage & GRALLOC_USAGE_HW_FB) { + drm_intel_bo_disable_reuse(ibo); + bo->stride = pitch; + bo->fb_handle = ibo->handle; + } + + if (drm_intel_bo_flink(ibo, (uint32_t *) &bo->name)) { + LOGE("failed to flink ibo"); + drm_intel_bo_unreference(ibo); + free(bo); + return NULL; + } + + bo->data = (int) ibo; + + *stride = pitch; + + return bo; +} + +void +drm_gem_drv_free(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + drm_intel_bo_unreference((drm_intel_bo *) bo->data); + free(bo); +} + +int +drm_gem_drv_map(struct drm_module_t *drm, struct drm_bo_t *bo, + int x, int y, int w, int h, int enable_write, void **addr) +{ + drm_intel_bo *ibo = (drm_intel_bo *) bo->data; + int err; + + if (!ibo) { + drm_intel_bufmgr *bufmgr = (drm_intel_bufmgr *) drm->gem; + + ibo = drm_intel_bo_gem_create_from_name(bufmgr, "gralloc-r", bo->name); + if (!ibo) { + LOGE("failed to create ibo from name %u", bo->name); + return -EINVAL; + } + + bo->data = (int) ibo; + } + + if ((bo->usage & GRALLOC_USAGE_HW_FB) || + drm_gem_get_tiling(bo) != I915_TILING_NONE) + err = drm_intel_gem_bo_map_gtt(ibo); + else + err = drm_intel_bo_map(ibo, enable_write); + if (!err) + *addr = ibo->virtual; + + return err; +} + +void +drm_gem_drv_unmap(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + drm_intel_bo *ibo = (drm_intel_bo *) bo->data; + + if ((bo->usage & GRALLOC_USAGE_HW_FB) || + drm_gem_get_tiling(bo) != I915_TILING_NONE) + drm_intel_gem_bo_unmap_gtt(ibo); + else + drm_intel_bo_unmap(ibo); +} diff --git a/src/gralloc/gralloc_gem_pipe.c b/src/gralloc/gralloc_gem_pipe.c new file mode 100644 index 0000000000..99a8461627 --- /dev/null +++ b/src/gralloc/gralloc_gem_pipe.c @@ -0,0 +1,333 @@ +#define LOG_TAG "GRALLOC-PIPE" + +#include +#include + +#include "pipe/p_screen.h" +#include "pipe/p_context.h" +#include "state_tracker/drm_driver.h" +#include "util/u_inlines.h" +#include "util/u_memory.h" + +#include "gralloc_mod.h" +#include "gralloc_gem.h" + +struct drm_pipe_manager { + pthread_mutex_t mutex; + struct pipe_screen *screen; + struct pipe_context *context; +}; + +struct drm_pipe_buffer { + struct winsys_handle winsys; + uint32_t fb_handle; + struct pipe_resource *resource; + struct pipe_transfer *transfer; +}; + +static void +drm_gem_drv_init_features_locked(struct drm_module_t *drm) +{ + drm->mode_dirty_fb = 0; + drm->mode_page_flip = 0; + + if (strcmp(driver_descriptor.driver_name, "vmwgfx") == 0) + drm->mode_dirty_fb = 1; + + drm->swap_interval = 0; +} + +static int +drm_gem_drv_init_locked(struct drm_module_t *drm) +{ + struct drm_pipe_manager *pm; + + if (drm->gem) + return 0; + + pm = CALLOC(1, sizeof(*pm)); + if (!pm) + return -ENOMEM; + + pthread_mutex_init(&pm->mutex, NULL); + + pm->screen = driver_descriptor.create_screen(drm->fd); + if (!pm->screen) { + LOGE("failed to create pipe screen"); + FREE(pm); + return -EINVAL; + } + + drm->gem = (void *) pm; + + drm_gem_drv_init_features_locked(drm); + + return 0; +} + +int +drm_gem_drv_init(struct drm_module_t *drm) +{ + int ret; + + pthread_mutex_lock(&drm->mutex); + ret = drm_gem_drv_init_locked(drm); + pthread_mutex_unlock(&drm->mutex); + + return ret; +} + +static enum pipe_format +get_pipe_format(int format) +{ + enum pipe_format fmt; + + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + fmt = PIPE_FORMAT_R8G8B8A8_UNORM; + break; + case HAL_PIXEL_FORMAT_RGBX_8888: + fmt = PIPE_FORMAT_R8G8B8X8_UNORM; + break; + case HAL_PIXEL_FORMAT_RGB_888: + fmt = PIPE_FORMAT_R8G8B8_UNORM; + break; + case HAL_PIXEL_FORMAT_RGB_565: + fmt = PIPE_FORMAT_B5G6R5_UNORM; + break; + case HAL_PIXEL_FORMAT_BGRA_8888: + fmt = PIPE_FORMAT_B8G8R8A8_UNORM; + break; + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + default: + fmt = PIPE_FORMAT_NONE; + break; + } + + return fmt; +} + +static unsigned +get_pipe_bind(int usage) +{ + unsigned bind = PIPE_BIND_SHARED; + + if (usage & GRALLOC_USAGE_SW_READ_MASK) + bind |= PIPE_BIND_TRANSFER_READ; + if (usage & GRALLOC_USAGE_SW_WRITE_MASK) + bind |= PIPE_BIND_TRANSFER_WRITE; + + if (usage & GRALLOC_USAGE_HW_TEXTURE) + bind |= PIPE_BIND_SAMPLER_VIEW; + if (usage & GRALLOC_USAGE_HW_RENDER) + bind |= PIPE_BIND_RENDER_TARGET; + if (usage & GRALLOC_USAGE_HW_FB) { + bind |= PIPE_BIND_RENDER_TARGET; + bind |= PIPE_BIND_SCANOUT; + } + + return bind; +} + +static struct drm_pipe_buffer * +get_pipe_buffer(struct drm_pipe_manager *pm, int width, int height, + int format, int usage, struct winsys_handle *winsys) +{ + struct drm_pipe_buffer *buf; + struct pipe_resource templ; + + memset(&templ, 0, sizeof(templ)); + templ.format = get_pipe_format(format); + templ.bind = get_pipe_bind(usage); + templ.target = PIPE_TEXTURE_2D; + + if (templ.format == PIPE_FORMAT_NONE || + !pm->screen->is_format_supported(pm->screen, templ.format, + templ.target, 0, templ.bind, 0)) { + LOGE("unsupported format 0x%x", format); + return NULL; + } + + buf = CALLOC(1, sizeof(*buf)); + if (!buf) + return NULL; + + templ.width0 = width; + templ.height0 = height; + templ.depth0 = 1; + + if (winsys) { + buf->resource = pm->screen->resource_from_handle(pm->screen, + &templ, winsys); + if (!buf->resource) + goto fail; + + buf->winsys = *winsys; + } + else { + buf->resource = pm->screen->resource_create(pm->screen, &templ); + if (!buf->resource) + goto fail; + + buf->winsys.type = DRM_API_HANDLE_TYPE_SHARED; + if (!pm->screen->resource_get_handle(pm->screen, buf->resource, &buf->winsys)) + goto fail; + } + + /* need the gem handle for fb */ + if (usage & GRALLOC_USAGE_HW_FB) { + struct winsys_handle tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = DRM_API_HANDLE_TYPE_KMS; + if (!pm->screen->resource_get_handle(pm->screen, buf->resource, &tmp)) + goto fail; + + buf->fb_handle = tmp.handle; + } + + return buf; + +fail: + LOGE("failed to allocate pipe buffer"); + if (buf->resource) + pipe_resource_reference(&buf->resource, NULL); + FREE(buf); + + return NULL; +} + +struct drm_bo_t * +drm_gem_drv_alloc(struct drm_module_t *drm, int width, int height, + int format, int usage, int *stride) +{ + struct drm_pipe_manager *pm = (struct drm_pipe_manager *) drm->gem; + struct drm_pipe_buffer *buf; + struct drm_bo_t *bo; + struct pipe_resource templ; + + bo = drm_gem_create_bo(width, height, format, usage); + if (!bo) + return NULL; + + pthread_mutex_lock(&pm->mutex); + buf = get_pipe_buffer(pm, width, height, format, usage, NULL); + pthread_mutex_unlock(&pm->mutex); + + if (buf) { + bo->name = (int) buf->winsys.handle; + bo->stride = (int) buf->winsys.stride; + + bo->fb_handle = buf->fb_handle; + bo->data = (int) buf; + + *stride = bo->stride; + } + else { + free(bo); + bo = NULL; + } + + return bo; +} + +void +drm_gem_drv_free(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + struct drm_pipe_manager *pm = (struct drm_pipe_manager *) drm->gem; + struct drm_pipe_buffer *buf = (struct drm_pipe_buffer *) bo->data; + + pthread_mutex_lock(&pm->mutex); + + if (buf->transfer) + pipe_transfer_destroy(pm->context, buf->transfer); + pipe_resource_reference(&buf->resource, NULL); + + pthread_mutex_unlock(&pm->mutex); + + FREE(buf); + free(bo); +} + +int +drm_gem_drv_map(struct drm_module_t *drm, struct drm_bo_t *bo, + int x, int y, int w, int h, int enable_write, void **addr) +{ + struct drm_pipe_manager *pm = (struct drm_pipe_manager *) drm->gem; + struct drm_pipe_buffer *buf = (struct drm_pipe_buffer *) bo->data; + enum pipe_transfer_usage usage = 0; + int ret = 0; + + pthread_mutex_lock(&pm->mutex); + + /* need a context to get transfer */ + if (!pm->context) { + pm->context = pm->screen->context_create(pm->screen, NULL); + if (!pm->context) { + LOGE("failed to create pipe context"); + ret = -ENOMEM; + } + } + + /* the bo was allocated by another process */ + if (!buf && !ret) { + struct winsys_handle winsys; + + memset(&winsys, 0, sizeof(winsys)); + winsys.type = DRM_API_HANDLE_TYPE_SHARED; + winsys.handle = bo->name; + winsys.stride = bo->stride; + + buf = get_pipe_buffer(pm, bo->width, bo->height, + bo->format, bo->usage, &winsys); + if (!buf) { + LOGE("failed to create pipe buffer from name %u", bo->name); + ret = -ENOMEM; + } + + bo->data = (int) buf; + } + + if (buf) { + usage = PIPE_TRANSFER_READ; + if (enable_write) + usage |= PIPE_TRANSFER_WRITE; + + assert(!buf->transfer); + + /* + * ignore x, y, w and h so that returned addr points at the start of the + * buffer + */ + buf->transfer = pipe_get_transfer(pm->context, buf->resource, + 0, 0, usage, 0, 0, bo->width, bo->height); + if (buf->transfer) + *addr = pipe_transfer_map(pm->context, buf->transfer); + else + ret = -ENOMEM; + } + + pthread_mutex_unlock(&pm->mutex); + + return ret; +} + +void +drm_gem_drv_unmap(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + struct drm_pipe_manager *pm = (struct drm_pipe_manager *) drm->gem; + struct drm_pipe_buffer *buf = (struct drm_pipe_buffer *) bo->data; + + pthread_mutex_lock(&pm->mutex); + + assert(buf && buf->transfer); + + pipe_transfer_unmap(pm->context, buf->transfer); + pipe_transfer_destroy(pm->context, buf->transfer); + buf->transfer = NULL; + + pm->context->flush(pm->context, PIPE_FLUSH_RENDER_CACHE, NULL); + + pthread_mutex_unlock(&pm->mutex); +} diff --git a/src/gralloc/gralloc_kms.c b/src/gralloc/gralloc_kms.c new file mode 100644 index 0000000000..569e862f9b --- /dev/null +++ b/src/gralloc/gralloc_kms.c @@ -0,0 +1,243 @@ +#define LOG_TAG "GRALLOC-KMS" + +#include +#include +#include +#include "gralloc_kms.h" + +int +drm_kms_add_fb(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + uint8_t bpp; + + bpp = drm_mod_get_bpp(bo->format) * 8; + + return drmModeAddFB(drm->fd, bo->width, bo->height, bpp, bpp, + bo->stride, bo->fb_handle, (uint32_t *) &bo->fb_id); +} + +void +drm_kms_rm_fb(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + drmModeRmFB(drm->fd, bo->fb_id); + bo->fb_id = 0; +} + +static void +drm_kms_wait_vblank(struct drm_module_t *drm, int num) +{ + drmVBlank vbl; + int ret; + + memset(&vbl, 0, sizeof(vbl)); + vbl.request.type = DRM_VBLANK_RELATIVE; + if (drm->vblank_secondary) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = num; + + ret = drmWaitVBlank(drm->fd, &vbl); + if (ret) + LOGW("failed to wait vblank"); +} + +static int +drm_kms_set_crtc(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + int ret; + + if (drm->swap_interval) + drm_kms_wait_vblank(drm, drm->swap_interval); + + ret = drmModeSetCrtc(drm->fd, drm->crtc_id, bo->fb_id, + 0, 0, &drm->connector_id, 1, &drm->mode); + if (ret) { + LOGE("failed to set crtc"); + return ret; + } + +#ifdef DRM_MODE_FEATURE_DIRTYFB + if (drm->mode_dirty_fb) + ret = drmModeDirtyFB(drm->fd, bo->fb_id, &drm->clip, 1); +#endif + + return ret; +} + +static int +drm_kms_page_flip(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + int waits = 3, ret; + + if (drm->swap_interval > 1) + drm_kms_wait_vblank(drm, drm->swap_interval - 1); + + while (waits) { + ret = drmModePageFlip(drm->fd, drm->crtc_id, bo->fb_id, 0x0, NULL); + if (ret && errno == -EBUSY) { + if (drm->swap_interval) + drm_kms_wait_vblank(drm, 1); + else + usleep(5000); + waits--; + } + else { + break; + } + } + + if (ret) + LOGE("failed to perform page flip"); + + return ret; +} + +int +drm_kms_post(struct drm_module_t *drm, struct drm_bo_t *bo) +{ + int ret; + + if (!bo->fb_id) { + LOGE("unable to post bo %p without fb", bo); + return -EINVAL; + } + + /* TODO spawn a thread to avoid waiting */ + + if (drm->first_post) { + pthread_mutex_lock(&drm->mutex); + if (drm->first_post) { + ret = drm_kms_set_crtc(drm, bo); + if (!ret) + drm->first_post = 0; + pthread_mutex_unlock(&drm->mutex); + + return ret; + } + pthread_mutex_unlock(&drm->mutex); + } + + if (drm->mode_page_flip && drm->swap_interval) + ret = drm_kms_page_flip(drm, bo); + else + ret = drm_kms_set_crtc(drm, bo); + + return ret; +} + +static int +drm_kms_init_with_connector_locked(struct drm_module_t *drm, + drmModeConnectorPtr connector) +{ + drmModeEncoderPtr encoder; + drmModeModeInfoPtr mode; + int i; + + if (!connector->count_modes) + return -EINVAL; + + encoder = drmModeGetEncoder(drm->fd, connector->encoders[0]); + if (!encoder) + return -EINVAL; + + for (i = 0; i < drm->resources->count_crtcs; i++) { + if (encoder->possible_crtcs & (1 << i)) + break; + } + drmModeFreeEncoder(encoder); + if (i == drm->resources->count_crtcs) + return -EINVAL; + + drm->crtc_id = drm->resources->crtcs[i]; + drm->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; + } + } + /* no preference; use the first */ + if (!mode) + mode = &connector->modes[0]; + + drm->mode = *mode; + + if (connector->mmWidth && connector->mmHeight) { + drm->xdpi = (drm->mode.hdisplay * 25.4 / connector->mmWidth); + drm->ydpi = (drm->mode.vdisplay * 25.4 / connector->mmHeight); + } + else { + drm->xdpi = 75; + drm->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; +#endif + + drm->first_post = 1; + + return 0; +} + +static int +drm_kms_init_locked(struct drm_module_t *drm) +{ + int i, ret; + + if (drm->resources) + return 0; + + drm->resources = drmModeGetResources(drm->fd); + if (!drm->resources) { + LOGE("failed to get modeset resources"); + return -EINVAL; + } + + 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_locked(drm, connector)) + break; + } + + drmModeFreeConnector(connector); + } + } + if (i == drm->resources->count_connectors) { + drmModeFreeResources(drm->resources); + drm->resources = NULL; + + return -EINVAL; + } + + return 0; +} + +int +drm_kms_init(struct drm_module_t *drm) +{ + int ret; + + pthread_mutex_lock(&drm->mutex); + ret = drm_kms_init_locked(drm); + pthread_mutex_unlock(&drm->mutex); + + return ret; +} diff --git a/src/gralloc/gralloc_kms.h b/src/gralloc/gralloc_kms.h new file mode 100644 index 0000000000..44708e0034 --- /dev/null +++ b/src/gralloc/gralloc_kms.h @@ -0,0 +1,19 @@ +#ifndef _DRM_KMS_H_ +#define _DRM_KMS_H_ + +#include "gralloc_mod.h" +#include "gralloc_gem.h" + +int +drm_kms_init(struct drm_module_t *drm); + +int +drm_kms_add_fb(struct drm_module_t *drm, struct drm_bo_t *bo); + +void +drm_kms_rm_fb(struct drm_module_t *drm, struct drm_bo_t *bo); + +int +drm_kms_post(struct drm_module_t *drm, struct drm_bo_t *bo); + +#endif /* _DRM_KMS_H_ */ diff --git a/src/gralloc/gralloc_mod.c b/src/gralloc/gralloc_mod.c new file mode 100644 index 0000000000..1296d2af04 --- /dev/null +++ b/src/gralloc/gralloc_mod.c @@ -0,0 +1,328 @@ +#define LOG_TAG "GRALLOC-MOD" + +#include +#include +#include +#include + +#include "gralloc_mod.h" +#include "gralloc_gem.h" +#include "gralloc_kms.h" + +static int +drm_mod_perform(const struct gralloc_module_t *mod, int op, ...) +{ + struct drm_module_t *drm = (struct drm_module_t *) mod; + va_list args; + int ret; + + va_start(args, op); + switch (op) { + case GRALLOC_MODULE_PERFORM_GET_DRM_FD: + { + int *fd = va_arg(args, int *); + + ret = drm_gem_init(drm); + if (!ret) + *fd = drm->fd; + } + break; + case GRALLOC_MODULE_PERFORM_GET_DRM_MAGIC: + { + int32_t *magic = va_arg(args, int32_t *); + + ret = drm_gem_init(drm); + if (!ret) + ret = drm_gem_get_magic(drm, magic); + } + break; + case GRALLOC_MODULE_PERFORM_AUTH_DRM_MAGIC: + { + int32_t magic = va_arg(args, int32_t); + + ret = drm_gem_init(drm); + if (!ret) + ret = drm_gem_auth_magic(drm, magic); + } + break; + default: + ret = -EINVAL; + break; + } + va_end(args); + + return ret; +} + +static int +drm_mod_register_buffer(const gralloc_module_t *mod, buffer_handle_t handle) +{ + return (drm_gem_get(handle)) ? 0 : -EINVAL; +} + +static int +drm_mod_unregister_buffer(const gralloc_module_t *mod, buffer_handle_t handle) +{ + return (drm_gem_get(handle)) ? 0 : -EINVAL; +} + +static int +drm_mod_lock(const gralloc_module_t *mod, buffer_handle_t handle, + int usage, int x, int y, int w, int h, void **ptr) +{ + struct drm_module_t *drm = (struct drm_module_t *) mod; + struct drm_bo_t *bo; + int ret; + + ret = drm_gem_init(drm); + if (!ret) + ret = drm_gem_drv_init(drm); + if (ret) + return ret; + + bo = drm_gem_validate(handle); + if (!bo) + return -EINVAL; + + return drm_gem_drv_map(drm, bo, x, y, w, h, 1, ptr); +} + +static int +drm_mod_unlock(const gralloc_module_t *mod, buffer_handle_t handle) +{ + struct drm_module_t *drm = (struct drm_module_t *) mod; + struct drm_bo_t *bo; + + bo = drm_gem_validate(handle); + if (!bo) + return -EINVAL; + + drm_gem_drv_unmap(drm, bo); + + return 0; +} + +static int +drm_mod_close_gpu0(struct hw_device_t *dev) +{ + struct alloc_device_t *alloc = (struct alloc_device_t *) dev; + + free(alloc); + + return 0; +} + +static int +drm_mod_free_gpu0(alloc_device_t *dev, buffer_handle_t handle) +{ + struct drm_module_t *drm = (struct drm_module_t *) dev->common.module; + struct drm_bo_t *bo; + + bo = drm_gem_validate(handle); + if (!bo) + return -EINVAL; + + if (bo->usage & GRALLOC_USAGE_HW_FB) + drm_kms_rm_fb(drm, bo); + + drm_gem_drv_free(drm, bo); + + return 0; +} + +static int +drm_mod_alloc_gpu0(alloc_device_t *dev, int w, int h, int format, int usage, + buffer_handle_t *handle, int *stride) +{ + struct drm_module_t *drm = (struct drm_module_t *) dev->common.module; + struct drm_bo_t *bo; + int size, bpp, ret; + + ret = drm_gem_drv_init(drm); + if (ret) + return ret; + + bpp = drm_mod_get_bpp(format); + if (!bpp) + return -EINVAL; + + bo = drm_gem_drv_alloc(drm, w, h, format, usage, stride); + if (!bo) + return -EINVAL; + if (bo->usage & GRALLOC_USAGE_HW_FB) { + ret = drm_kms_add_fb(drm, bo); + if (ret) { + LOGE("failed to add fb"); + drm_gem_drv_free(drm, bo); + return ret; + } + } + + *stride /= bpp; + *handle = &bo->base; + + return 0; +} + +static int +drm_mod_open_gpu0(struct drm_module_t *drm, hw_device_t **dev) +{ + struct alloc_device_t *alloc; + int ret; + + ret = drm_gem_init(drm); + if (ret) + return ret; + + alloc = calloc(1, sizeof(*alloc)); + if (!alloc) + return -EINVAL; + + alloc->common.tag = HARDWARE_DEVICE_TAG; + alloc->common.version = 0; + alloc->common.module = (hw_module_t *) drm; + alloc->common.close = drm_mod_close_gpu0; + + alloc->alloc = drm_mod_alloc_gpu0; + alloc->free = drm_mod_free_gpu0; + + *dev = &alloc->common; + + return 0; +} + +static int +drm_mod_close_fb0(struct hw_device_t *dev) +{ + struct framebuffer_device_t *fb = (struct framebuffer_device_t *) dev; + + free(fb); + + return 0; +} + +static int +drm_mod_set_swap_interval_fb0(struct framebuffer_device_t *fb, int interval) +{ + if (interval < fb->minSwapInterval || interval > fb->maxSwapInterval) + return -EINVAL; + return 0; +} + +static int +drm_mod_post_fb0(struct framebuffer_device_t *fb, buffer_handle_t handle) +{ + struct drm_module_t *drm = (struct drm_module_t *) fb->common.module; + struct drm_bo_t *bo; + + bo = drm_gem_validate(handle); + if (!bo) + return -EINVAL; + + return drm_kms_post(drm, bo); +} + +#include +static int +drm_mod_composition_complete_fb0(struct framebuffer_device_t *fb) +{ + eglWaitClient(); + return 0; +} + +static int +drm_mod_open_fb0(struct drm_module_t *drm, struct hw_device_t **dev) +{ + struct framebuffer_device_t *fb; + int ret; + + fb = calloc(1, sizeof(*fb)); + if (!fb) + return -ENOMEM; + + ret = drm_gem_init(drm); + if (!ret) + ret = drm_kms_init(drm); + if (ret) { + free(fb); + return ret; + } + + fb->common.tag = HARDWARE_DEVICE_TAG; + fb->common.version = 0; + fb->common.module = (hw_module_t *) drm; + fb->common.close = drm_mod_close_fb0; + + fb->setSwapInterval = drm_mod_set_swap_interval_fb0; + fb->post = drm_mod_post_fb0; + fb->compositionComplete = drm_mod_composition_complete_fb0; + + *((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; + *((int *) &fb->minSwapInterval) = drm->swap_interval; + *((int *) &fb->maxSwapInterval) = drm->swap_interval; + + *dev = &fb->common; + + LOGI("mode.hdisplay %d\n" + "mode.vdisplay %d\n" + "mode.vrefresh %d\n" + "format 0x%x\n" + "xdpi %d\n" + "ydpi %d\n", + drm->mode.hdisplay, + drm->mode.vdisplay, + drm->mode.vrefresh, + drm->format, + drm->xdpi, drm->ydpi); + + return 0; +} + +static int +drm_mod_open(const struct hw_module_t *mod, const char *name, struct hw_device_t **dev) +{ + struct drm_module_t *drm = (struct drm_module_t *) mod; + int ret; + + if (strcmp(name, GRALLOC_HARDWARE_GPU0) == 0) + ret = drm_mod_open_gpu0(drm, dev); + else if (strcmp(name, GRALLOC_HARDWARE_FB0) == 0) + ret = drm_mod_open_fb0(drm, dev); + else + ret = -EINVAL; + + return ret; +} + +static struct hw_module_methods_t drm_mod_methods = { + .open = drm_mod_open +}; + +struct drm_module_t HAL_MODULE_INFO_SYM = { + .base = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = GRALLOC_HARDWARE_MODULE_ID, + .name = "DRM Memory Allocator", + .author = "Chia-I Wu", + .methods = &drm_mod_methods + }, + .registerBuffer = drm_mod_register_buffer, + .unregisterBuffer = drm_mod_unregister_buffer, + .lock = drm_mod_lock, + .unlock = drm_mod_unlock, + .perform = drm_mod_perform + }, + .mutex = PTHREAD_MUTEX_INITIALIZER, + .fd = -1 +}; diff --git a/src/gralloc/gralloc_mod.h b/src/gralloc/gralloc_mod.h new file mode 100644 index 0000000000..e06ec864ad --- /dev/null +++ b/src/gralloc/gralloc_mod.h @@ -0,0 +1,65 @@ +#ifndef _GRALLOC_DRM_H +#define _GRALLOC_DRM_H + +#include +#include +#include +#include + +struct drm_module_t { + gralloc_module_t base; + + pthread_mutex_t mutex; + + /* initialized by drm_gem_init */ + int fd; + + /* initialized by drm_kms_init */ + drmModeResPtr resources; + uint32_t crtc_id; + uint32_t connector_id; + drmModeModeInfo mode; + int xdpi, ydpi; + int format; +#ifdef DRM_MODE_FEATURE_DIRTYFB + drmModeClip clip; +#endif + + /* initialized by drm_gem_drv_init */ + void *gem; + int mode_dirty_fb; + int mode_page_flip; + int swap_interval; + int vblank_secondary; + + int first_post; +}; + +static inline int +drm_mod_get_bpp(int format) +{ + int bpp; + + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + bpp = 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + bpp = 3; + break; + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + bpp = 2; + break; + default: + bpp = 0; + break; + } + + return bpp; +} + +#endif /* _GRALLOC_DRM_H */ -- cgit v1.2.3