Support multi-plane buffers.

Enable NV12 format on Exynos.

BUG=chromium:368775
TEST=HW video overlay works on snow and peach_pi

Change-Id: Ia149618fa086b9ba3ef998149c3557052833e33b
Signed-off-by: Yuly Novikov <ynovikov@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/318550
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
diff --git a/exynos.c b/exynos.c
index b8cda49..bde6c11 100644
--- a/exynos.c
+++ b/exynos.c
@@ -6,6 +6,8 @@
 
 #ifdef GBM_EXYNOS
 
+#include <assert.h>
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <xf86drm.h>
@@ -13,28 +15,53 @@
 
 #include "gbm_priv.h"
 #include "helpers.h"
+#include "util.h"
 
 int gbm_exynos_bo_create(struct gbm_bo *bo, uint32_t width, uint32_t height,
 			 uint32_t format, uint32_t flags)
 {
-	size_t size = width * height * gbm_bytes_from_format(format);
-	struct drm_exynos_gem_create gem_create;
-	int ret;
+	size_t plane;
 
-	memset(&gem_create, 0, sizeof(gem_create));
-	gem_create.size = size;
-	gem_create.flags = EXYNOS_BO_NONCONTIG;
-
-	ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_EXYNOS_GEM_CREATE, &gem_create);
-	if (ret) {
-		fprintf(stderr, "minigbm: DRM_IOCTL_EXYNOS_GEM_CREATE failed "
-				"(size=%zu)\n", size);
-		return ret;
+	if (format == GBM_FORMAT_NV12) {
+		uint32_t chroma_height;
+		/* V4L2 s5p-mfc requires width to be 16 byte aligned and height 32. */
+		width = ALIGN(width, 16);
+		height = ALIGN(height, 32);
+		chroma_height = ALIGN(height / 2, 32);
+		bo->strides[0] = bo->strides[1] = width;
+		/* MFC v8+ requires 64 byte padding in the end of luma and chroma buffers. */
+		bo->sizes[0] = bo->strides[0] * height + 64;
+		bo->sizes[1] = bo->strides[1] * chroma_height + 64;
+		bo->offsets[0] = bo->offsets[1] = 0;
+	} else if (format == GBM_FORMAT_XRGB8888 || format == GBM_FORMAT_ARGB8888 ||
+			format == GBM_BO_FORMAT_XRGB8888 || format == GBM_BO_FORMAT_ARGB8888 ) {
+		bo->strides[0] = gbm_stride_from_format(format, width);
+		bo->sizes[0] = height * bo->strides[0];
+		bo->offsets[0] = 0;
+	} else {
+		fprintf(stderr, "minigbm: unsupported format %X\n", format);
+		assert(0);
+		return -EINVAL;
 	}
 
-	bo->handle.u32 = gem_create.handle;
-	bo->size = size;
-	bo->stride = width * gbm_bytes_from_format(format);
+	for (plane = 0; plane < bo->num_planes; plane++) {
+		size_t size = bo->sizes[plane];
+		struct drm_exynos_gem_create gem_create;
+		int ret;
+
+		memset(&gem_create, 0, sizeof(gem_create));
+		gem_create.size = size;
+		gem_create.flags = EXYNOS_BO_NONCONTIG;
+
+		ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_EXYNOS_GEM_CREATE, &gem_create);
+		if (ret) {
+			fprintf(stderr, "minigbm: DRM_IOCTL_EXYNOS_GEM_CREATE failed "
+					"(size=%zu)\n", size);
+			return ret;
+		}
+
+		bo->handles[plane].u32 = gem_create.handle;
+	}
 
 	return 0;
 }
@@ -47,6 +74,7 @@
 	.format_list = {
 		{GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_CURSOR | GBM_BO_USE_RENDERING | GBM_BO_USE_WRITE},
 		{GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_CURSOR | GBM_BO_USE_RENDERING | GBM_BO_USE_WRITE},
+		{GBM_FORMAT_NV12, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING | GBM_BO_USE_WRITE},
 	}
 };
 
diff --git a/gbm.c b/gbm.c
index ad031a4..628ed3f 100644
--- a/gbm.c
+++ b/gbm.c
@@ -4,6 +4,7 @@
  * found in the LICENSE file.
  */
 
+#include <assert.h>
 #include <fcntl.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -189,22 +190,23 @@
 
 static struct gbm_bo *gbm_bo_new(struct gbm_device *gbm,
 				 uint32_t width, uint32_t height,
-				 uint32_t format, uint32_t stride)
+				 uint32_t format)
 {
 	struct gbm_bo *bo;
 
-	bo = (struct gbm_bo*) malloc(sizeof(*bo));
+	bo = (struct gbm_bo*) calloc(1, sizeof(*bo));
 	if (!bo)
 		return NULL;
 
 	bo->gbm = gbm;
 	bo->width = width;
 	bo->height = height;
-	bo->stride = stride;
 	bo->format = format;
-	bo->handle.u32 = 0;
-	bo->destroy_user_data = NULL;
-	bo->user_data = NULL;
+	bo->num_planes = gbm_num_planes_from_format(format);
+	if (!bo->num_planes) {
+		free(bo);
+		return NULL;
+	}
 
 	return bo;
 }
@@ -216,8 +218,10 @@
 	struct gbm_bo *bo;
 	int ret;
 
-	bo = gbm_bo_new(gbm, width, height, format,
-			width * gbm_bytes_from_format(format));
+	if (!gbm_device_is_format_supported(gbm, format, 0))
+		return NULL;
+
+	bo = gbm_bo_new(gbm, width, height, format);
 	if (!bo)
 		return NULL;
 
@@ -257,8 +261,14 @@
 	if (!gbm_device_is_format_supported(gbm, fd_data->format, usage))
 		return NULL;
 
-	bo = gbm_bo_new(gbm, fd_data->width, fd_data->height, fd_data->format,
-			fd_data->stride);
+	/* This function can support only single plane formats. */
+	/* If multi-plane import is desired, new function should be added. */
+	if (gbm_num_planes_from_format(fd_data->format) != 1)
+		return NULL;
+
+	bo = gbm_bo_new(gbm, fd_data->width, fd_data->height, fd_data->format);
+	bo->strides[0] = fd_data->stride;
+
 	if (!bo)
 		return NULL;
 
@@ -272,7 +282,7 @@
 		return NULL;
 	}
 
-	bo->handle.u32 = prime_handle.handle;
+	bo->handles[0].u32 = prime_handle.handle;
 
 	return bo;
 }
@@ -292,13 +302,13 @@
 PUBLIC uint32_t
 gbm_bo_get_stride(struct gbm_bo *bo)
 {
-	return bo->stride;
+	return gbm_bo_get_plane_stride(bo, 0);
 }
 
 PUBLIC uint32_t
 gbm_bo_get_stride_or_tiling(struct gbm_bo *bo)
 {
-	return bo->tiling ? bo->tiling : bo->stride;
+	return bo->tiling ? bo->tiling : gbm_bo_get_stride(bo);
 }
 
 PUBLIC uint32_t
@@ -316,23 +326,65 @@
 PUBLIC union gbm_bo_handle
 gbm_bo_get_handle(struct gbm_bo *bo)
 {
-	return bo->handle;
+	return gbm_bo_get_plane_handle(bo, 0);
 }
 
 PUBLIC int
 gbm_bo_get_fd(struct gbm_bo *bo)
 {
-	int fd;
+	return gbm_bo_get_plane_fd(bo, 0);
+}
 
-	if (drmPrimeHandleToFD(gbm_device_get_fd(bo->gbm),
-				gbm_bo_get_handle(bo).u32,
-				DRM_CLOEXEC,
-				&fd))
+PUBLIC size_t
+gbm_bo_get_num_planes(struct gbm_bo *bo)
+{
+	return bo->num_planes;
+}
+
+PUBLIC union gbm_bo_handle
+gbm_bo_get_plane_handle(struct gbm_bo *bo, size_t plane)
+{
+	assert(plane < bo->num_planes);
+	return bo->handles[plane];
+}
+
+PUBLIC int
+gbm_bo_get_plane_fd(struct gbm_bo *bo, size_t plane)
+{
+	int fd;
+	assert(plane < bo->num_planes);
+
+	if (drmPrimeHandleToFD(
+			gbm_device_get_fd(bo->gbm),
+			gbm_bo_get_plane_handle(bo, plane).u32,
+			DRM_CLOEXEC,
+			&fd))
 		return -1;
 	else
 		return fd;
 }
 
+PUBLIC uint32_t
+gbm_bo_get_plane_offset(struct gbm_bo *bo, size_t plane)
+{
+	assert(plane < bo->num_planes);
+	return bo->offsets[plane];
+}
+
+PUBLIC uint32_t
+gbm_bo_get_plane_size(struct gbm_bo *bo, size_t plane)
+{
+	assert(plane < bo->num_planes);
+	return bo->sizes[plane];
+}
+
+PUBLIC uint32_t
+gbm_bo_get_plane_stride(struct gbm_bo *bo, size_t plane)
+{
+	assert(plane < bo->num_planes);
+	return bo->strides[plane];
+}
+
 PUBLIC void
 gbm_bo_set_user_data(struct gbm_bo *bo, void *data,
 		     void (*destroy_user_data)(struct gbm_bo *, void *))
diff --git a/gbm.h b/gbm.h
index 094d746..fbb8cbc 100644
--- a/gbm.h
+++ b/gbm.h
@@ -71,7 +71,7 @@
 /** Format of the allocated buffer */
 enum gbm_bo_format {
    /** RGB with 8 bits per channel in a 32 bit value */
-   GBM_BO_FORMAT_XRGB8888, 
+   GBM_BO_FORMAT_XRGB8888,
    /** ARGB with 8 bits per channel in a 32 bit value */
    GBM_BO_FORMAT_ARGB8888
 };
@@ -273,6 +273,24 @@
 int
 gbm_bo_get_fd(struct gbm_bo *bo);
 
+size_t
+gbm_bo_get_num_planes(struct gbm_bo *bo);
+
+union gbm_bo_handle
+gbm_bo_get_plane_handle(struct gbm_bo *bo, size_t plane);
+
+int
+gbm_bo_get_plane_fd(struct gbm_bo *bo, size_t plane);
+
+uint32_t
+gbm_bo_get_plane_offset(struct gbm_bo *bo, size_t plane);
+
+uint32_t
+gbm_bo_get_plane_size(struct gbm_bo *bo, size_t plane);
+
+uint32_t
+gbm_bo_get_plane_stride(struct gbm_bo *bo, size_t plane);
+
 int
 gbm_bo_write(struct gbm_bo *bo, const void *buf, size_t count);
 
diff --git a/gbm_priv.h b/gbm_priv.h
index 0e42934..5c35505 100644
--- a/gbm_priv.h
+++ b/gbm_priv.h
@@ -12,6 +12,8 @@
 #include <stdlib.h>
 #include "gbm.h"
 
+#define GBM_MAX_PLANES 4
+
 struct gbm_device
 {
 	int fd;
@@ -28,11 +30,13 @@
 	struct gbm_device *gbm;
 	uint32_t width;
 	uint32_t height;
-	uint32_t size;
-	uint32_t stride;
 	uint32_t format;
 	uint32_t tiling;
-	union gbm_bo_handle handle;
+	size_t num_planes;
+	union gbm_bo_handle handles[GBM_MAX_PLANES];
+	uint32_t offsets[GBM_MAX_PLANES];
+	uint32_t sizes[GBM_MAX_PLANES];
+	uint32_t strides[GBM_MAX_PLANES];
 	void *priv;
 	void *user_data;
 	void (*destroy_user_data)(struct gbm_bo *, void *);
diff --git a/helpers.c b/helpers.c
index 605a107..1ad10ce 100644
--- a/helpers.c
+++ b/helpers.c
@@ -4,13 +4,78 @@
  * found in the LICENSE file.
  */
 
-#include <stdlib.h>
+#include <assert.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <xf86drm.h>
 
 #include "gbm_priv.h"
 #include "helpers.h"
+#include "util.h"
+
+size_t gbm_num_planes_from_format(uint32_t format)
+{
+	if (format == GBM_BO_FORMAT_XRGB8888)
+		format = GBM_FORMAT_XRGB8888;
+	if (format == GBM_BO_FORMAT_ARGB8888)
+		format = GBM_FORMAT_ARGB8888;
+
+	switch(format)
+	{
+		case GBM_FORMAT_C8:
+		case GBM_FORMAT_RGB332:
+		case GBM_FORMAT_BGR233:
+		case GBM_FORMAT_XRGB4444:
+		case GBM_FORMAT_XBGR4444:
+		case GBM_FORMAT_RGBX4444:
+		case GBM_FORMAT_BGRX4444:
+		case GBM_FORMAT_ARGB4444:
+		case GBM_FORMAT_ABGR4444:
+		case GBM_FORMAT_RGBA4444:
+		case GBM_FORMAT_BGRA4444:
+		case GBM_FORMAT_XRGB1555:
+		case GBM_FORMAT_XBGR1555:
+		case GBM_FORMAT_RGBX5551:
+		case GBM_FORMAT_BGRX5551:
+		case GBM_FORMAT_ARGB1555:
+		case GBM_FORMAT_ABGR1555:
+		case GBM_FORMAT_RGBA5551:
+		case GBM_FORMAT_BGRA5551:
+		case GBM_FORMAT_RGB565:
+		case GBM_FORMAT_BGR565:
+		case GBM_FORMAT_YUYV:
+		case GBM_FORMAT_YVYU:
+		case GBM_FORMAT_UYVY:
+		case GBM_FORMAT_VYUY:
+		case GBM_FORMAT_RGB888:
+		case GBM_FORMAT_BGR888:
+		case GBM_FORMAT_XRGB8888:
+		case GBM_FORMAT_XBGR8888:
+		case GBM_FORMAT_RGBX8888:
+		case GBM_FORMAT_BGRX8888:
+		case GBM_FORMAT_ARGB8888:
+		case GBM_FORMAT_ABGR8888:
+		case GBM_FORMAT_RGBA8888:
+		case GBM_FORMAT_BGRA8888:
+		case GBM_FORMAT_XRGB2101010:
+		case GBM_FORMAT_XBGR2101010:
+		case GBM_FORMAT_RGBX1010102:
+		case GBM_FORMAT_BGRX1010102:
+		case GBM_FORMAT_ARGB2101010:
+		case GBM_FORMAT_ABGR2101010:
+		case GBM_FORMAT_RGBA1010102:
+		case GBM_FORMAT_BGRA1010102:
+		case GBM_FORMAT_AYUV:
+			return 1;
+		case GBM_FORMAT_NV12:
+			return 2;
+	}
+
+	fprintf(stderr, "minigbm: UNKNOWN FORMAT %d\n", format);
+	return 0;
+}
 
 int gbm_bpp_from_format(uint32_t format)
 {
@@ -26,6 +91,9 @@
 		case GBM_FORMAT_BGR233:
 			return 8;
 
+		case GBM_FORMAT_NV12:
+			return 12;
+
 		case GBM_FORMAT_XRGB4444:
 		case GBM_FORMAT_XBGR4444:
 		case GBM_FORMAT_RGBX4444:
@@ -74,13 +142,15 @@
 			return 32;
 	}
 
-	printf("UNKNOWN FORMAT %d\n", format);
+	fprintf(stderr, "minigbm: UNKNOWN FORMAT %d\n", format);
 	return 0;
 }
 
-int gbm_bytes_from_format(uint32_t format)
+int gbm_stride_from_format(uint32_t format, uint32_t width)
 {
-	return gbm_bpp_from_format(format) / 8;
+	/* Only single-plane formats are supported */
+	assert(gbm_num_planes_from_format(format) == 1);
+	return DIV_ROUND_UP(width * gbm_bpp_from_format(format), 8);
 }
 
 int gbm_is_format_supported(struct gbm_bo *bo)
@@ -94,6 +164,9 @@
 	struct drm_mode_create_dumb create_dumb;
 	int ret;
 
+	/* Only single-plane formats are supported */
+	assert(gbm_num_planes_from_format(format) == 1);
+
 	memset(&create_dumb, 0, sizeof(create_dumb));
 	create_dumb.height = height;
 	create_dumb.width = width;
@@ -102,14 +175,14 @@
 
 	ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
 	if (ret) {
-		fprintf(stderr, "minigbm: DRM_IOCTL_MODE_CREATE_DUMB failed "
-				"(handle=%x)\n", bo->handle.u32);
+		fprintf(stderr, "minigbm: DRM_IOCTL_MODE_CREATE_DUMB failed\n");
 		return ret;
 	}
 
-	bo->handle.u32 = create_dumb.handle;
-	bo->size = create_dumb.size;
-	bo->stride = create_dumb.pitch;
+	bo->handles[0].u32 = create_dumb.handle;
+	bo->offsets[0] = 0;
+	bo->sizes[0] = create_dumb.size;
+	bo->strides[0] = create_dumb.pitch;
 
 	return 0;
 }
@@ -120,12 +193,12 @@
 	int ret;
 
 	memset(&destroy_dumb, 0, sizeof(destroy_dumb));
-	destroy_dumb.handle = bo->handle.u32;
+	destroy_dumb.handle = bo->handles[0].u32;
 
 	ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
 	if (ret) {
 		fprintf(stderr, "minigbm: DRM_IOCTL_MODE_DESTROY_DUMB failed "
-				"(handle=%x)\n", bo->handle.u32);
+				"(handle=%x)\n", bo->handles[0].u32);
 		return ret;
 	}
 
@@ -135,17 +208,28 @@
 int gbm_gem_bo_destroy(struct gbm_bo *bo)
 {
 	struct drm_gem_close gem_close;
-	int ret;
+	int ret, error = 0;
+	size_t plane, i;
 
-	memset(&gem_close, 0, sizeof(gem_close));
-	gem_close.handle = bo->handle.u32;
+	for (plane = 0; plane < bo->num_planes; plane++) {
+		bool already_closed = false;
+		for (i = 1; i < plane && !already_closed; i++)
+			if (bo->handles[i-1].u32 == bo->handles[plane].u32)
+				already_closed = true;
+		if (already_closed)
+			continue;
 
-	ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
-	if (ret) {
-		fprintf(stderr, "minigbm: DRM_IOCTL_GEM_CLOSE failed "
-				"(handle=%x)\n", bo->handle.u32);
-		return ret;
+		memset(&gem_close, 0, sizeof(gem_close));
+		gem_close.handle = bo->handles[plane].u32;
+
+		ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
+		if (ret) {
+			fprintf(stderr, "minigbm: DRM_IOCTL_GEM_CLOSE failed "
+					"(handle=%x) error %d\n",
+					bo->handles[plane].u32, ret);
+			error = ret;
+		}
 	}
 
-	return 0;
+	return error;
 }
diff --git a/helpers.h b/helpers.h
index 06b59ba..3f8e5bf 100644
--- a/helpers.h
+++ b/helpers.h
@@ -7,8 +7,9 @@
 #ifndef HELPERS_H
 #define HELPERS_H
 
+size_t gbm_num_planes_from_format(uint32_t format);
 int gbm_bpp_from_format(uint32_t format);
-int gbm_bytes_from_format(uint32_t format);
+int gbm_stride_from_format(uint32_t format, uint32_t width);
 int gbm_is_format_supported(struct gbm_bo *bo);
 int gbm_dumb_bo_create(struct gbm_bo *bo, uint32_t width, uint32_t height,
 		       uint32_t format, uint32_t flags);
diff --git a/i915.c b/i915.c
index 6e2436b..74269ef 100644
--- a/i915.c
+++ b/i915.c
@@ -123,7 +123,7 @@
 		       uint32_t format, uint32_t flags)
 {
 	struct gbm_device *gbm = bo->gbm;
-	int bpp = gbm_bytes_from_format(format);
+	int bpp = gbm_stride_from_format(format, 1);
 	struct drm_i915_gem_create gem_create;
 	struct drm_i915_gem_set_tiling gem_set_tiling;
 	uint32_t tiling_mode = I915_TILING_NONE;
@@ -139,9 +139,9 @@
 
 	i915_align_dimensions(gbm, tiling_mode, &width, &height, bpp);
 
-	bo->stride = width * bpp;
+	bo->strides[0] = width * bpp;
 
-	if (!i915_verify_dimensions(gbm, bo->stride, height))
+	if (!i915_verify_dimensions(gbm, bo->strides[0], height))
 		return EINVAL;
 
 	memset(&gem_create, 0, sizeof(gem_create));
@@ -154,21 +154,22 @@
 				"(size=%zu)\n", size);
 		return ret;
 	}
-	bo->handle.u32 = gem_create.handle;
-	bo->size = size;
+	bo->handles[0].u32 = gem_create.handle;
+	bo->sizes[0] = size;
+	bo->offsets[0] = 0;
 
 	memset(&gem_set_tiling, 0, sizeof(gem_set_tiling));
 	do {
-		gem_set_tiling.handle = bo->handle.u32;
+		gem_set_tiling.handle = bo->handles[0].u32;
 		gem_set_tiling.tiling_mode = tiling_mode;
-		gem_set_tiling.stride = bo->stride;
+		gem_set_tiling.stride = bo->strides[0];
 		ret = drmIoctl(gbm->fd, DRM_IOCTL_I915_GEM_SET_TILING,
 			       &gem_set_tiling);
 	} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
 
 	if (ret == -1) {
 		struct drm_gem_close gem_close;
-		gem_close.handle = bo->handle.u32;
+		gem_close.handle = bo->handles[0].u32;
 		fprintf(stderr, "minigbm: DRM_IOCTL_I915_GEM_SET_TILING failed "
 				"errno=%x (handle=%x, tiling=%x, stride=%x)\n",
 				errno,
diff --git a/mediatek.c b/mediatek.c
index e1d8f29..36eb6cd 100644
--- a/mediatek.c
+++ b/mediatek.c
@@ -17,10 +17,13 @@
 int gbm_mediatek_bo_create(struct gbm_bo *bo, uint32_t width, uint32_t height,
 			   uint32_t format, uint32_t flags)
 {
-	size_t size = width * height * gbm_bytes_from_format(format);
+	size_t size;
 	struct drm_mtk_gem_create gem_create;
 	int ret;
 
+	bo->strides[0] = gbm_stride_from_format(format, width);
+	size = height * bo->strides[0];
+
 	memset(&gem_create, 0, sizeof(gem_create));
 	gem_create.size = size;
 
@@ -31,9 +34,9 @@
 		return ret;
 	}
 
-	bo->handle.u32 = gem_create.handle;
-	bo->size = size;
-	bo->stride = width * gbm_bytes_from_format(format);
+	bo->handles[0].u32 = gem_create.handle;
+	bo->sizes[0] = size;
+	bo->offsets[0] = 0;
 
 	return 0;
 }
diff --git a/rockchip.c b/rockchip.c
index 0a721da..3df3595 100644
--- a/rockchip.c
+++ b/rockchip.c
@@ -17,10 +17,13 @@
 int gbm_rockchip_bo_create(struct gbm_bo *bo, uint32_t width, uint32_t height,
 			   uint32_t format, uint32_t flags)
 {
-	size_t size = width * height * gbm_bytes_from_format(format);
+	size_t size;
 	struct drm_rockchip_gem_create gem_create;
 	int ret;
 
+	bo->strides[0] = gbm_stride_from_format(format, width);
+	size = height * bo->strides[0];
+
 	memset(&gem_create, 0, sizeof(gem_create));
 	gem_create.size = size;
 
@@ -31,9 +34,9 @@
 		return ret;
 	}
 
-	bo->handle.u32 = gem_create.handle;
-	bo->size = size;
-	bo->stride = width * gbm_bytes_from_format(format);
+	bo->handles[0].u32 = gem_create.handle;
+	bo->sizes[0] = size;
+	bo->offsets[0] = 0;
 
 	return 0;
 }
diff --git a/tegra.c b/tegra.c
index 4ffe037..1cfdda7 100644
--- a/tegra.c
+++ b/tegra.c
@@ -13,6 +13,7 @@
 
 #include "gbm_priv.h"
 #include "helpers.h"
+#include "util.h"
 
 /*
  * GOB (Group Of Bytes) is the basic unit of the blocklinear layout.
@@ -48,26 +49,21 @@
 	return block_height_log2;
 }
 
-static inline uint32_t align_up(uint32_t value, uint32_t alignment)
-{
-	return (value + (alignment-1)) & ~(alignment-1);
-}
-
 static void compute_layout_blocklinear(int width, int height, int format,
 				       enum nv_mem_kind *kind,
 				       uint32_t *block_height_log2,
 				       uint32_t *stride, uint32_t *size)
 {
-	int pitch = width * gbm_bytes_from_format(format);
+	int pitch = gbm_stride_from_format(format, width);
 
 	/* Align to blocklinear blocks. */
-	pitch = align_up(pitch, NV_BLOCKLINEAR_GOB_WIDTH);
+	pitch = ALIGN(pitch, NV_BLOCKLINEAR_GOB_WIDTH);
 
 	/* Compute padded height. */
 	*block_height_log2 = compute_block_height_log2(height);
 	int block_height = 1 << *block_height_log2;
 	int padded_height =
-		align_up(height, NV_BLOCKLINEAR_GOB_HEIGHT * block_height);
+		ALIGN(height, NV_BLOCKLINEAR_GOB_HEIGHT * block_height);
 
 	int bytes = pitch * padded_height;
 
@@ -75,7 +71,7 @@
 	 * This will reduce the required page table size (see discussion in NV
 	 * bug 1321091), and also acts as a WAR for NV bug 1325421.
 	 */
-	bytes = align_up(bytes, NV_PREFERRED_PAGE_SIZE);
+	bytes = ALIGN(bytes, NV_PREFERRED_PAGE_SIZE);
 
 	*kind = NV_MEM_KIND_GENERIC_16Bx2;
 	*stride = pitch;
@@ -85,7 +81,7 @@
 static void compute_layout_linear(int width, int height, int format,
 				  uint32_t *stride, uint32_t *size)
 {
-	*stride = width * gbm_bytes_from_format(format);
+	*stride = gbm_stride_from_format(format, width);
 	*size = *stride * height;
 }
 
@@ -114,15 +110,16 @@
 		return ret;
 	}
 
-	bo->handle.u32 = gem_create.handle;
-	bo->size = size;
-	bo->stride = stride;
+	bo->handles[0].u32 = gem_create.handle;
+	bo->offsets[0] = 0;
+	bo->sizes[0] = size;
+	bo->strides[0] = stride;
 
 	if (kind != NV_MEM_KIND_PITCH) {
 		struct drm_tegra_gem_set_tiling gem_tile;
 
 		memset(&gem_tile, 0, sizeof(gem_tile));
-		gem_tile.handle = bo->handle.u32;
+		gem_tile.handle = bo->handles[0].u32;
 		gem_tile.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
 		gem_tile.value = block_height_log2;
 
diff --git a/util.h b/util.h
index 197312a..9606336 100644
--- a/util.h
+++ b/util.h
@@ -11,5 +11,6 @@
 #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
 #define PUBLIC __attribute__((visibility("default")))
 #define ALIGN(A, B) (((A) + (B) - 1) / (B) * (B))
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
 
 #endif