drm_gralloc: Add support for Rockchip DRM

This patch adds backend for libdrm_rockchip to support drm_gralloc on
systems with Rockchip DRM.

Change-Id: Iedcf1cb25cc2462681f0355f939018037171d207
Signed-off-by: Tomasz Figa <tfiga@google.com>
diff --git a/Android.mk b/Android.mk
index ee53679..3f84810 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,6 +25,7 @@
 
 intel_drivers := i915 i965 i915g ilo
 radeon_drivers := r300g r600g
+rockchip_drivers := rockchip
 nouveau_drivers := nouveau
 vmwgfx_drivers := vmwgfx
 
@@ -32,6 +33,7 @@
 	prebuilt \
 	$(intel_drivers) \
 	$(radeon_drivers) \
+	$(rockchip_drivers) \
 	$(nouveau_drivers) \
 	$(vmwgfx_drivers)
 
@@ -116,6 +118,12 @@
 LOCAL_SHARED_LIBRARIES += libdrm_nouveau
 endif
 
+ifneq ($(filter $(rockchip_drivers), $(DRM_GPU_DRIVERS)),)
+LOCAL_SRC_FILES += gralloc_drm_rockchip.c
+LOCAL_CFLAGS += -DENABLE_ROCKCHIP
+LOCAL_SHARED_LIBRARIES += libdrm_rockchip
+endif
+
 ifeq ($(strip $(DRM_USES_PIPE)),true)
 LOCAL_SRC_FILES += gralloc_drm_pipe.c
 LOCAL_CFLAGS += -DENABLE_PIPE
diff --git a/gralloc_drm.cpp b/gralloc_drm.cpp
index ebff717..6f90a84 100644
--- a/gralloc_drm.cpp
+++ b/gralloc_drm.cpp
@@ -79,6 +79,10 @@
 		if (!drv && !strcmp(version->name, "radeon"))
 			drv = gralloc_drm_drv_create_for_radeon(fd);
 #endif
+#ifdef ENABLE_ROCKCHIP
+		if (!drv && !strcmp(version->name, "rockchip"))
+			drv = gralloc_drm_drv_create_for_rockchip(fd);
+#endif
 #ifdef ENABLE_NOUVEAU
 		if (!drv && !strcmp(version->name, "nouveau"))
 			drv = gralloc_drm_drv_create_for_nouveau(fd);
diff --git a/gralloc_drm_priv.h b/gralloc_drm_priv.h
index 9a87b78..24c398f 100644
--- a/gralloc_drm_priv.h
+++ b/gralloc_drm_priv.h
@@ -91,6 +91,7 @@
 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_pipe(int fd, const char *name);
 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_intel(int fd);
 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_radeon(int fd);
+struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_rockchip(int fd);
 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_nouveau(int fd);
 
 #ifdef __cplusplus
diff --git a/gralloc_drm_rockchip.c b/gralloc_drm_rockchip.c
new file mode 100644
index 0000000..b383208
--- /dev/null
+++ b/gralloc_drm_rockchip.c
@@ -0,0 +1,185 @@
+#define LOG_TAG "GRALLOC-ROCKCHIP"
+
+#include <cutils/log.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <drm.h>
+#include <rockchip/rockchip_drmif.h>
+
+#include "gralloc_drm.h"
+#include "gralloc_drm_priv.h"
+
+#define UNUSED(...) (void)(__VA_ARGS__)
+
+struct rockchip_info {
+	struct gralloc_drm_drv_t base;
+
+	struct rockchip_device *rockchip;
+	int fd;
+};
+
+struct rockchip_buffer {
+	struct gralloc_drm_bo_t base;
+
+	struct rockchip_bo *bo;
+};
+
+static void drm_gem_rockchip_destroy(struct gralloc_drm_drv_t *drv)
+{
+	struct rockchip_info *info = (struct rockchip_info *)drv;
+
+	if (info->rockchip)
+		rockchip_device_destroy(info->rockchip);
+	free(info);
+}
+
+static struct gralloc_drm_bo_t *drm_gem_rockchip_alloc(
+		struct gralloc_drm_drv_t *drv,
+		struct gralloc_drm_handle_t *handle)
+{
+	struct rockchip_info *info = (struct rockchip_info *)drv;
+	struct rockchip_buffer *buf;
+	struct drm_gem_close args;
+	int ret, cpp, pitch;
+	uint32_t size, gem_handle;
+
+	buf = calloc(1, sizeof(*buf));
+	if (!buf) {
+		ALOGE("Failed to allocate buffer wrapper\n");
+		return NULL;
+	}
+
+	cpp = gralloc_drm_get_bpp(handle->format);
+	if (!cpp) {
+		ALOGE("unrecognized format 0x%x", handle->format);
+		return NULL;
+	}
+
+	gralloc_drm_align_geometry(handle->format,
+			&handle->width, &handle->height);
+
+	/* TODO: We need to sort out alignment */
+	pitch = ALIGN(handle->width * cpp, 64);
+	size = handle->height * pitch;
+
+	if (handle->prime_fd >= 0) {
+		ret = drmPrimeFDToHandle(info->fd, handle->prime_fd,
+			&gem_handle);
+		if (ret) {
+			char *c = NULL;
+			ALOGE("failed to convert prime fd to handle %d ret=%d",
+				handle->prime_fd, ret);
+			*c = 0;
+			goto err;
+		}
+		ALOGV("Got handle %d for fd %d\n", gem_handle, handle->prime_fd);
+
+		buf->bo = rockchip_bo_from_handle(info->rockchip, gem_handle,
+			0, size);
+		if (!buf->bo) {
+			ALOGE("failed to wrap bo handle=%d size=%d\n",
+				gem_handle, size);
+
+			memset(&args, 0, sizeof(args));
+			args.handle = gem_handle;
+			drmIoctl(info->fd, DRM_IOCTL_GEM_CLOSE, &args);
+			return NULL;
+		}
+	} else {
+		buf->bo = rockchip_bo_create(info->rockchip, size, 0);
+		if (!buf->bo) {
+			ALOGE("failed to allocate bo %dx%dx%dx%d\n",
+				handle->height, pitch, cpp, size);
+			goto err;
+		}
+
+		gem_handle = rockchip_bo_handle(buf->bo);
+		ret = drmPrimeHandleToFD(info->fd, gem_handle, 0,
+			&handle->prime_fd);
+		ALOGV("Got fd %d for handle %d\n", handle->prime_fd, gem_handle);
+		if (ret) {
+			ALOGE("failed to get prime fd %d", ret);
+			goto err_unref;
+		}
+
+		buf->base.fb_handle = gem_handle;
+	}
+
+	handle->name = 0;
+	handle->stride = pitch;
+	buf->base.handle = handle;
+
+	return &buf->base;
+
+err_unref:
+	rockchip_bo_destroy(buf->bo);
+err:
+	free(buf);
+	return NULL;
+}
+
+static void drm_gem_rockchip_free(struct gralloc_drm_drv_t *drv,
+		struct gralloc_drm_bo_t *bo)
+{
+	struct rockchip_buffer *buf = (struct rockchip_buffer *)bo;
+
+	UNUSED(drv);
+
+	if (bo->handle && bo->handle->prime_fd)
+		close(bo->handle->prime_fd);
+
+	/* TODO: Is destroy correct here? */
+	rockchip_bo_destroy(buf->bo);
+	free(buf);
+}
+
+static int drm_gem_rockchip_map(struct gralloc_drm_drv_t *drv,
+		struct gralloc_drm_bo_t *bo, int x, int y, int w, int h,
+		int enable_write, void **addr)
+{
+	struct rockchip_buffer *buf = (struct rockchip_buffer *)bo;
+
+	UNUSED(drv, x, y, w, h, enable_write);
+
+	*addr = rockchip_bo_map(buf->bo);
+	if (!*addr) {
+		ALOGE("failed to map bo\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void drm_gem_rockchip_unmap(struct gralloc_drm_drv_t *drv,
+		struct gralloc_drm_bo_t *bo)
+{
+	UNUSED(drv, bo);
+}
+
+struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_rockchip(int fd)
+{
+	struct rockchip_info *info;
+	int ret;
+
+	info = calloc(1, sizeof(*info));
+	if (!info) {
+		ALOGE("Failed to allocate rockchip gralloc device\n");
+		return NULL;
+	}
+
+	info->rockchip = rockchip_device_create(fd);
+	if (!info->rockchip) {
+		ALOGE("Failed to create new rockchip instance\n");
+		free(info);
+		return NULL;
+	}
+
+	info->fd = fd;
+	info->base.destroy = drm_gem_rockchip_destroy;
+	info->base.alloc = drm_gem_rockchip_alloc;
+	info->base.free = drm_gem_rockchip_free;
+	info->base.map = drm_gem_rockchip_map;
+	info->base.unmap = drm_gem_rockchip_unmap;
+
+	return &info->base;
+}