drm/radeon/r600: fix tiling issues in CS checker.

The CS checker had some incorrect alignment requirements for 2D surfaces,
this made rendering to mipmap levels that were 2D broken.

Also the CB height was being worked out from the BO size, this doesn't work
at all when rendering mipmap levels, instead we work out what height userspace
wanted from slice max and use that to check it fits inside the BO, however
the DDX send the wrong slice max for an unaligned buffer so we have to workaround
for that even though its a userspace bug.

Reviewed-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 250a3a9..f828327 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -170,6 +170,7 @@
 	struct r600_cs_track *track = p->track;
 	u32 bpe = 0, pitch, slice_tile_max, size, tmp, height, pitch_align;
 	volatile u32 *ib = p->ib->ptr;
+	unsigned array_mode;
 
 	if (G_0280A0_TILE_MODE(track->cb_color_info[i])) {
 		dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n");
@@ -185,12 +186,12 @@
 	/* pitch is the number of 8x8 tiles per row */
 	pitch = G_028060_PITCH_TILE_MAX(track->cb_color_size[i]) + 1;
 	slice_tile_max = G_028060_SLICE_TILE_MAX(track->cb_color_size[i]) + 1;
-	height = size / (pitch * 8 * bpe);
+	slice_tile_max *= 64;
+	height = slice_tile_max / (pitch * 8);
 	if (height > 8192)
 		height = 8192;
-	if (height > 7)
-		height &= ~0x7;
-	switch (G_0280A0_ARRAY_MODE(track->cb_color_info[i])) {
+	array_mode = G_0280A0_ARRAY_MODE(track->cb_color_info[i]);
+	switch (array_mode) {
 	case V_0280A0_ARRAY_LINEAR_GENERAL:
 		/* technically height & 0x7 */
 		break;
@@ -222,7 +223,7 @@
 		break;
 	case V_0280A0_ARRAY_2D_TILED_THIN1:
 		pitch_align = max((u32)track->nbanks,
-				  (u32)(((track->group_size / 8) / (bpe * track->nsamples)) * track->nbanks));
+				  (u32)(((track->group_size / 8) / (bpe * track->nsamples)) * track->nbanks)) / 8;
 		if (!IS_ALIGNED(pitch, pitch_align)) {
 			dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n",
 				__func__, __LINE__, pitch);
@@ -243,8 +244,18 @@
 	/* check offset */
 	tmp = height * pitch * 8 * bpe;
 	if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) {
-		dev_warn(p->dev, "%s offset[%d] %d too big\n", __func__, i, track->cb_color_bo_offset[i]);
-		return -EINVAL;
+		if (array_mode == V_0280A0_ARRAY_LINEAR_GENERAL) {
+			/* the initial DDX does bad things with the CB size occasionally */
+			/* it rounds up height too far for slice tile max but the BO is smaller */
+			tmp = (height - 7) * 8 * bpe;
+			if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) {
+				dev_warn(p->dev, "%s offset[%d] %d %d %lu too big\n", __func__, i, track->cb_color_bo_offset[i], tmp, radeon_bo_size(track->cb_color_bo[i]));
+				return -EINVAL;
+			}
+		} else {
+			dev_warn(p->dev, "%s offset[%d] %d %d %lu too big\n", __func__, i, track->cb_color_bo_offset[i], tmp, radeon_bo_size(track->cb_color_bo[i]));
+			return -EINVAL;
+		}
 	}
 	if (!IS_ALIGNED(track->cb_color_bo_offset[i], track->group_size)) {
 		dev_warn(p->dev, "%s offset[%d] %d not aligned\n", __func__, i, track->cb_color_bo_offset[i]);
@@ -361,7 +372,7 @@
 				break;
 			case V_028010_ARRAY_2D_TILED_THIN1:
 				pitch_align = max((u32)track->nbanks,
-						  (u32)(((track->group_size / 8) / bpe) * track->nbanks));
+						  (u32)(((track->group_size / 8) / bpe) * track->nbanks)) / 8;
 				if (!IS_ALIGNED(pitch, pitch_align)) {
 					dev_warn(p->dev, "%s:%d db pitch (%d) invalid\n",
 						 __func__, __LINE__, pitch);
@@ -1138,7 +1149,7 @@
 		break;
 	case V_038000_ARRAY_2D_TILED_THIN1:
 		pitch_align = max((u32)track->nbanks,
-				  (u32)(((track->group_size / 8) / bpe) * track->nbanks));
+				  (u32)(((track->group_size / 8) / bpe) * track->nbanks)) / 8;
 		if (!IS_ALIGNED(pitch, pitch_align)) {
 			dev_warn(p->dev, "%s:%d tex pitch (%d) invalid\n",
 				__func__, __LINE__, pitch);