minigbm/i915: Add support for I915_FORMAT_MOD_Y_TILED_CCS

This adds support for allocating buffers with the
I915_FORMAT_MOD_Y_TILED_CCS modifier, which enables Intel's rendering
buffer compression.

(reland of crrev.com/c/1706735)

BUG=979736

Change-Id: I90a8bc5fad0e1133d527b5ef4cff56d371763fc6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/minigbm/+/2044490
Reviewed-by: Mark Yacoub <markyacoub@google.com>
Reviewed-by: Robert Tarasov <tutankhamen@chromium.org>
Tested-by: Mark Yacoub <markyacoub@google.com>
Tested-by: Robert Tarasov <tutankhamen@chromium.org>
Auto-Submit: Mark Yacoub <markyacoub@google.com>
Commit-Queue: Mark Yacoub <markyacoub@google.com>
diff --git a/i915.c b/i915.c
index 6538aef..27ff209 100644
--- a/i915.c
+++ b/i915.c
@@ -356,6 +356,7 @@
 		bo->meta.tiling = I915_TILING_X;
 		break;
 	case I915_FORMAT_MOD_Y_TILED:
+	case I915_FORMAT_MOD_Y_TILED_CCS:
 		bo->meta.tiling = I915_TILING_Y;
 		break;
 	}
@@ -373,6 +374,47 @@
 		 */
 		uint32_t stride = ALIGN(width, 32);
 		drv_bo_from_format(bo, stride, height, format);
+	} else if (modifier == I915_FORMAT_MOD_Y_TILED_CCS) {
+		/*
+		 * For compressed surfaces, we need a color control surface
+		 * (CCS). Color compression is only supported for Y tiled
+		 * surfaces, and for each 32x16 tiles in the main surface we
+		 * need a tile in the control surface.  Y tiles are 128 bytes
+		 * wide and 32 lines tall and we use that to first compute the
+		 * width and height in tiles of the main surface. stride and
+		 * height are already multiples of 128 and 32, respectively:
+		 */
+		uint32_t stride = drv_stride_from_format(format, width, 0);
+		uint32_t width_in_tiles = DIV_ROUND_UP(stride, 128);
+		uint32_t height_in_tiles = DIV_ROUND_UP(height, 32);
+		uint32_t size = width_in_tiles * height_in_tiles * 4096;
+		uint32_t offset = 0;
+
+		bo->meta.strides[0] = width_in_tiles * 128;
+		bo->meta.sizes[0] = size;
+		bo->meta.offsets[0] = offset;
+		offset += size;
+
+		/*
+		 * Now, compute the width and height in tiles of the control
+		 * surface by dividing and rounding up.
+		 */
+		uint32_t ccs_width_in_tiles = DIV_ROUND_UP(width_in_tiles, 32);
+		uint32_t ccs_height_in_tiles = DIV_ROUND_UP(height_in_tiles, 16);
+		uint32_t ccs_size = ccs_width_in_tiles * ccs_height_in_tiles * 4096;
+
+		/*
+		 * With stride and height aligned to y tiles, offset is
+		 * already a multiple of 4096, which is the required alignment
+		 * of the CCS.
+		 */
+		bo->meta.strides[1] = ccs_width_in_tiles * 128;
+		bo->meta.sizes[1] = ccs_size;
+		bo->meta.offsets[1] = offset;
+		offset += ccs_size;
+
+		bo->meta.num_planes = 2;
+		bo->meta.total_size = offset;
 	} else {
 		i915_bo_from_format(bo, width, height, format);
 	}
@@ -424,6 +466,7 @@
 					 uint32_t format, const uint64_t *modifiers, uint32_t count)
 {
 	static const uint64_t modifier_order[] = {
+		I915_FORMAT_MOD_Y_TILED_CCS,
 		I915_FORMAT_MOD_Y_TILED,
 		I915_FORMAT_MOD_X_TILED,
 		DRM_FORMAT_MOD_LINEAR,
@@ -470,6 +513,9 @@
 	int ret;
 	void *addr;
 
+	if (bo->meta.format_modifiers[0] == I915_FORMAT_MOD_Y_TILED_CCS)
+		return MAP_FAILED;
+
 	if (bo->meta.tiling == I915_TILING_NONE) {
 		struct drm_i915_gem_mmap gem_map;
 		memset(&gem_map, 0, sizeof(gem_map));