i915: implement alignment restrictions

The previous version was a quick and dirty implementation, this one
should be complete.

BUG=chromium:434924
TEST=

Change-Id: Ia6840bd75c7ea1c8d083ae9c5a3da6699f468273
Signed-off-by: Stéphane Marchesin <marcheu@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/231731
Reviewed-by: Haixia Shi <hshi@chromium.org>
diff --git a/i915.c b/i915.c
index 8d120c4..2d3dc62 100644
--- a/i915.c
+++ b/i915.c
@@ -66,41 +66,64 @@
 	gbm->priv = NULL;
 }
 
-static int i915_get_pitch(struct gbm_device *gbm, uint32_t pitch, int tiling_mode)
+static void i915_align_dimensions(struct gbm_device *gbm, uint32_t tiling_mode, uint32_t *width, uint32_t *height, int bpp)
 {
-	struct gbm_i915_device *dev_priv = (struct gbm_i915_device *)gbm->priv;
-	if (tiling_mode == I915_TILING_NONE)
-		return ALIGN(pitch, 64);
+	struct gbm_i915_device *i915_gbm = (struct gbm_i915_device *)gbm->priv;
+	uint32_t width_alignment = 4, height_alignment = 4;
 
-	if (dev_priv->gen >= 4) {
-		uint32_t tile_width = tiling_mode == I915_TILING_X ? 512 : 128;
-		return ALIGN(pitch, tile_width);
-	} else {
-		uint32_t i = 128;
-		while(i < pitch)
-			i <<= 1;
-		return i;
+	switch(tiling_mode) {
+		default:
+		case I915_TILING_NONE:
+			width_alignment = 64 / bpp;
+			break;
+
+		case I915_TILING_X:
+			width_alignment = 512 / bpp;
+			height_alignment = 8;
+			break;
+
+		case I915_TILING_Y:
+			if (i915_gbm->gen == 3) {
+				width_alignment = 512 / bpp;
+				height_alignment = 8;
+			} else  {
+				width_alignment = 128 / bpp;
+				height_alignment = 32;
+			}
+			break;
 	}
+
+	if (i915_gbm->gen > 3) {
+		*width = ALIGN(*width, width_alignment);
+		*height = ALIGN(*height, height_alignment);
+	} else {
+		uint32_t w;
+		for (w = width_alignment; w < *width * bpp;  w <<= 1)
+			;
+		*width = w;
+		*height = ALIGN(*height, height_alignment);
+	}
+}
+
+static int i915_verify_dimensions(struct gbm_device *gbm, uint32_t stride, uint32_t height)
+{
+	struct gbm_i915_device *i915_gbm = (struct gbm_i915_device *)gbm->priv;
+	if (i915_gbm->gen <= 3 && stride > 8192)
+		return 0;
+
+	return 1;
 }
 
 int gbm_i915_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 gbm_device *gbm = bo->gbm;
+	int bpp = gbm_bytes_from_format(format);
 	struct drm_i915_gem_create gem_create;
 	struct drm_i915_gem_set_tiling gem_set_tiling;
 	uint32_t tiling_mode = I915_TILING_NONE;
+	size_t size;
 	int ret;
 
-	memset(&gem_create, 0, sizeof(gem_create));
-	gem_create.size = size;
-
-	ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_I915_GEM_CREATE, &gem_create);
-	if (ret)
-		return ret;
-
-	bo->handle.u32 = gem_create.handle;
-	bo->size = size;
-
 	if (flags & GBM_BO_USE_CURSOR)
 		tiling_mode = I915_TILING_NONE;
 	else if (flags & GBM_BO_USE_SCANOUT)
@@ -108,22 +131,36 @@
 	else if (flags & GBM_BO_USE_RENDERING)
 		tiling_mode = I915_TILING_Y;
 
-	bo->stride = i915_get_pitch(bo->gbm,
-		width * gbm_bytes_from_format(format),
-		tiling_mode);
+	i915_align_dimensions(gbm, tiling_mode, &width, &height, bpp);
+
+	bo->stride = width * bpp;
+
+	if (!i915_verify_dimensions(gbm, bo->stride, height))
+		return EINVAL;
+
+	memset(&gem_create, 0, sizeof(gem_create));
+	size = width * height * bpp;
+	gem_create.size = size;
+
+	ret = drmIoctl(gbm->fd, DRM_IOCTL_I915_GEM_CREATE, &gem_create);
+	if (ret)
+		return ret;
+
+	bo->handle.u32 = gem_create.handle;
+	bo->size = size;
 
 	memset(&gem_set_tiling, 0, sizeof(gem_set_tiling));
 	do {
 		gem_set_tiling.handle = bo->handle.u32;
 		gem_set_tiling.tiling_mode = tiling_mode;
 		gem_set_tiling.stride = bo->stride;
-		ret = drmIoctl(bo->gbm->fd, DRM_IOCTL_I915_GEM_SET_TILING, &gem_set_tiling);
+		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;
-		drmIoctl(bo->gbm->fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
+		drmIoctl(gbm->fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
 		return -errno;
 	}