diff mbox

[2/2] radeon: add si tiling support v5

Message ID 1365632432-5078-2-git-send-email-j.glisse@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jerome Glisse April 10, 2013, 10:20 p.m. UTC
From: Jerome Glisse <jglisse@redhat.com>

v2: Only writte tile index if flags for it is set
v3: Remove useless allow2d scanout flags
v4: Split radeon_drm.h update to its own patch
v5: update against lastest next tree for radeon

Signed-off-by: Jerome Glisse <jglisse@redhat.com>
---
 radeon/radeon_surface.c | 658 ++++++++++++++++++++++++++++++++++++++++++++----
 radeon/radeon_surface.h |  31 +++
 2 files changed, 644 insertions(+), 45 deletions(-)

Comments

Michel Dänzer April 12, 2013, 12:23 p.m. UTC | #1
On Mit, 2013-04-10 at 18:20 -0400, j.glisse@gmail.com wrote: 
> From: Jerome Glisse <jglisse@redhat.com>
> 
> v2: Only writte tile index if flags for it is set
> v3: Remove useless allow2d scanout flags
> v4: Split radeon_drm.h update to its own patch
> v5: update against lastest next tree for radeon
> 
> Signed-off-by: Jerome Glisse <jglisse@redhat.com>

Reviewed-by: Michel Dänzer <michel.daenzer@amd.com>
diff mbox

Patch

diff --git a/radeon/radeon_surface.c b/radeon/radeon_surface.c
index 5935c23..288b5e2 100644
--- a/radeon/radeon_surface.c
+++ b/radeon/radeon_surface.c
@@ -83,12 +83,14 @@  typedef int (*hw_best_surface_t)(struct radeon_surface_manager *surf_man,
 
 struct radeon_hw_info {
     /* apply to r6, eg */
-    uint32_t                    group_bytes;
-    uint32_t                    num_banks;
-    uint32_t                    num_pipes;
+    uint32_t                        group_bytes;
+    uint32_t                        num_banks;
+    uint32_t                        num_pipes;
     /* apply to eg */
-    uint32_t                    row_size;
-    unsigned                    allow_2d;
+    uint32_t                        row_size;
+    unsigned                        allow_2d;
+    /* apply to si */
+    uint32_t                        tile_mode_array[32];
 };
 
 struct radeon_surface_manager {
@@ -1000,12 +1002,403 @@  static int eg_surface_best(struct radeon_surface_manager *surf_man,
 /* ===========================================================================
  * Southern Islands family
  */
+#define SI__GB_TILE_MODE__PIPE_CONFIG(x)        (((x) >> 6) & 0x1f)
+#define     SI__PIPE_CONFIG__ADDR_SURF_P2               0
+#define     SI__PIPE_CONFIG__ADDR_SURF_P4_8x16          4
+#define     SI__PIPE_CONFIG__ADDR_SURF_P4_16x16         5
+#define     SI__PIPE_CONFIG__ADDR_SURF_P4_16x32         6
+#define     SI__PIPE_CONFIG__ADDR_SURF_P4_32x32         7
+#define     SI__PIPE_CONFIG__ADDR_SURF_P8_16x16_8x16    8
+#define     SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_8x16    9
+#define     SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_8x16    10
+#define     SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_16x16   11
+#define     SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x16   12
+#define     SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x32   13
+#define     SI__PIPE_CONFIG__ADDR_SURF_P8_32x64_32x32   14
+#define SI__GB_TILE_MODE__TILE_SPLIT(x)         (((x) >> 11) & 0x7)
+#define     SI__TILE_SPLIT__64B                         0
+#define     SI__TILE_SPLIT__128B                        1
+#define     SI__TILE_SPLIT__256B                        2
+#define     SI__TILE_SPLIT__512B                        3
+#define     SI__TILE_SPLIT__1024B                       4
+#define     SI__TILE_SPLIT__2048B                       5
+#define     SI__TILE_SPLIT__4096B                       6
+#define SI__GB_TILE_MODE__BANK_WIDTH(x)         (((x) >> 14) & 0x3)
+#define     SI__BANK_WIDTH__1                           0
+#define     SI__BANK_WIDTH__2                           1
+#define     SI__BANK_WIDTH__4                           2
+#define     SI__BANK_WIDTH__8                           3
+#define SI__GB_TILE_MODE__BANK_HEIGHT(x)        (((x) >> 16) & 0x3)
+#define     SI__BANK_HEIGHT__1                          0
+#define     SI__BANK_HEIGHT__2                          1
+#define     SI__BANK_HEIGHT__4                          2
+#define     SI__BANK_HEIGHT__8                          3
+#define SI__GB_TILE_MODE__MACRO_TILE_ASPECT(x)  (((x) >> 18) & 0x3)
+#define     SI__MACRO_TILE_ASPECT__1                    0
+#define     SI__MACRO_TILE_ASPECT__2                    1
+#define     SI__MACRO_TILE_ASPECT__4                    2
+#define     SI__MACRO_TILE_ASPECT__8                    3
+#define SI__GB_TILE_MODE__NUM_BANKS(x)          (((x) >> 20) & 0x3)
+#define     SI__NUM_BANKS__2_BANK                       0
+#define     SI__NUM_BANKS__4_BANK                       1
+#define     SI__NUM_BANKS__8_BANK                       2
+#define     SI__NUM_BANKS__16_BANK                      3
+
+
+static void si_gb_tile_mode(uint32_t gb_tile_mode,
+                            unsigned *num_pipes,
+                            unsigned *num_banks,
+                            uint32_t *macro_tile_aspect,
+                            uint32_t *bank_w,
+                            uint32_t *bank_h,
+                            uint32_t *tile_split)
+{
+    if (num_pipes) {
+        switch (SI__GB_TILE_MODE__PIPE_CONFIG(gb_tile_mode)) {
+        case SI__PIPE_CONFIG__ADDR_SURF_P2:
+        default:
+            *num_pipes = 2;
+            break;
+        case SI__PIPE_CONFIG__ADDR_SURF_P4_8x16:
+        case SI__PIPE_CONFIG__ADDR_SURF_P4_16x16:
+        case SI__PIPE_CONFIG__ADDR_SURF_P4_16x32:
+        case SI__PIPE_CONFIG__ADDR_SURF_P4_32x32:
+            *num_pipes = 4;
+            break;
+        case SI__PIPE_CONFIG__ADDR_SURF_P8_16x16_8x16:
+        case SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_8x16:
+        case SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_8x16:
+        case SI__PIPE_CONFIG__ADDR_SURF_P8_16x32_16x16:
+        case SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x16:
+        case SI__PIPE_CONFIG__ADDR_SURF_P8_32x32_16x32:
+        case SI__PIPE_CONFIG__ADDR_SURF_P8_32x64_32x32:
+            *num_pipes = 8;
+            break;
+        }
+    }
+    if (num_banks) {
+        switch (SI__GB_TILE_MODE__NUM_BANKS(gb_tile_mode)) {
+        default:
+        case SI__NUM_BANKS__2_BANK:
+            *num_banks = 2;
+            break;
+        case SI__NUM_BANKS__4_BANK:
+            *num_banks = 4;
+            break;
+        case SI__NUM_BANKS__8_BANK:
+            *num_banks = 8;
+            break;
+        case SI__NUM_BANKS__16_BANK:
+            *num_banks = 16;
+            break;
+        }
+    }
+    if (macro_tile_aspect) {
+        switch (SI__GB_TILE_MODE__MACRO_TILE_ASPECT(gb_tile_mode)) {
+        default:
+        case SI__MACRO_TILE_ASPECT__1:
+            *macro_tile_aspect = 1;
+            break;
+        case SI__MACRO_TILE_ASPECT__2:
+            *macro_tile_aspect = 2;
+            break;
+        case SI__MACRO_TILE_ASPECT__4:
+            *macro_tile_aspect = 4;
+            break;
+        case SI__MACRO_TILE_ASPECT__8:
+            *macro_tile_aspect = 8;
+            break;
+        }
+    }
+    if (bank_w) {
+        switch (SI__GB_TILE_MODE__BANK_WIDTH(gb_tile_mode)) {
+        default:
+        case SI__BANK_WIDTH__1:
+            *bank_w = 1;
+            break;
+        case SI__BANK_WIDTH__2:
+            *bank_w = 2;
+            break;
+        case SI__BANK_WIDTH__4:
+            *bank_w = 4;
+            break;
+        case SI__BANK_WIDTH__8:
+            *bank_w = 8;
+            break;
+        }
+    }
+    if (bank_h) {
+        switch (SI__GB_TILE_MODE__BANK_HEIGHT(gb_tile_mode)) {
+        default:
+        case SI__BANK_HEIGHT__1:
+            *bank_h = 1;
+            break;
+        case SI__BANK_HEIGHT__2:
+            *bank_h = 2;
+            break;
+        case SI__BANK_HEIGHT__4:
+            *bank_h = 4;
+            break;
+        case SI__BANK_HEIGHT__8:
+            *bank_h = 8;
+            break;
+        }
+    }
+    if (tile_split) {
+        switch (SI__GB_TILE_MODE__TILE_SPLIT(gb_tile_mode)) {
+        default:
+        case SI__TILE_SPLIT__64B:
+            *tile_split = 64;
+            break;
+        case SI__TILE_SPLIT__128B:
+            *tile_split = 128;
+            break;
+        case SI__TILE_SPLIT__256B:
+            *tile_split = 256;
+            break;
+        case SI__TILE_SPLIT__512B:
+            *tile_split = 512;
+            break;
+        case SI__TILE_SPLIT__1024B:
+            *tile_split = 1024;
+            break;
+        case SI__TILE_SPLIT__2048B:
+            *tile_split = 2048;
+            break;
+        case SI__TILE_SPLIT__4096B:
+            *tile_split = 4096;
+            break;
+        }
+    }
+}
+
+static int si_init_hw_info(struct radeon_surface_manager *surf_man)
+{
+    uint32_t tiling_config;
+    drmVersionPtr version;
+    int r;
+
+    r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
+                         &tiling_config);
+    if (r) {
+        return r;
+    }
+
+    surf_man->hw_info.allow_2d = 0;
+    version = drmGetVersion(surf_man->fd);
+    if (version && version->version_minor >= 33) {
+        if (!radeon_get_value(surf_man->fd, RADEON_INFO_SI_TILE_MODE_ARRAY, surf_man->hw_info.tile_mode_array)) {
+            surf_man->hw_info.allow_2d = 1;
+        }
+    }
+    drmFreeVersion(version);
+
+    switch (tiling_config & 0xf) {
+    case 0:
+        surf_man->hw_info.num_pipes = 1;
+        break;
+    case 1:
+        surf_man->hw_info.num_pipes = 2;
+        break;
+    case 2:
+        surf_man->hw_info.num_pipes = 4;
+        break;
+    case 3:
+        surf_man->hw_info.num_pipes = 8;
+        break;
+    default:
+        surf_man->hw_info.num_pipes = 8;
+        surf_man->hw_info.allow_2d = 0;
+        break;
+    }
+
+    switch ((tiling_config & 0xf0) >> 4) {
+    case 0:
+        surf_man->hw_info.num_banks = 4;
+        break;
+    case 1:
+        surf_man->hw_info.num_banks = 8;
+        break;
+    case 2:
+        surf_man->hw_info.num_banks = 16;
+        break;
+    default:
+        surf_man->hw_info.num_banks = 8;
+        surf_man->hw_info.allow_2d = 0;
+        break;
+    }
+
+    switch ((tiling_config & 0xf00) >> 8) {
+    case 0:
+        surf_man->hw_info.group_bytes = 256;
+        break;
+    case 1:
+        surf_man->hw_info.group_bytes = 512;
+        break;
+    default:
+        surf_man->hw_info.group_bytes = 256;
+        surf_man->hw_info.allow_2d = 0;
+        break;
+    }
+
+    switch ((tiling_config & 0xf000) >> 12) {
+    case 0:
+        surf_man->hw_info.row_size = 1024;
+        break;
+    case 1:
+        surf_man->hw_info.row_size = 2048;
+        break;
+    case 2:
+        surf_man->hw_info.row_size = 4096;
+        break;
+    default:
+        surf_man->hw_info.row_size = 4096;
+        surf_man->hw_info.allow_2d = 0;
+        break;
+    }
+    return 0;
+}
+
+static int si_surface_sanity(struct radeon_surface_manager *surf_man,
+                             struct radeon_surface *surf,
+                             unsigned mode, unsigned *tile_mode, unsigned *stencil_tile_mode)
+{
+    uint32_t gb_tile_mode;
+
+    /* check surface dimension */
+    if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) {
+        return -EINVAL;
+    }
+
+    /* check mipmap last_level */
+    if (surf->last_level > 15) {
+        return -EINVAL;
+    }
+
+    /* force 1d on kernel that can't do 2d */
+    if (mode > RADEON_SURF_MODE_1D &&
+        (!surf_man->hw_info.allow_2d || !(surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX))) {
+        if (surf->nsamples > 1) {
+            fprintf(stderr, "radeon: Cannot use 1D tiling for an MSAA surface (%i).\n", __LINE__);
+            return -EFAULT;
+        }
+        mode = RADEON_SURF_MODE_1D;
+        surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
+        surf->flags |= RADEON_SURF_SET(mode, MODE);
+    }
+
+    if (surf->nsamples > 1 && mode != RADEON_SURF_MODE_2D) {
+        return -EINVAL;
+    }
+
+    if (!surf->tile_split) {
+        /* default value */
+        surf->mtilea = 1;
+        surf->bankw = 1;
+        surf->bankw = 1;
+        surf->tile_split = 64;
+        surf->stencil_tile_split = 64;
+    }
+
+    switch (mode) {
+    case RADEON_SURF_MODE_2D:
+        if (surf->flags & RADEON_SURF_SBUFFER) {
+            switch (surf->nsamples) {
+            case 1:
+                *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D;
+                break;
+            case 2:
+                *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_2AA;
+                break;
+            case 4:
+                *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_4AA;
+                break;
+            case 8:
+                *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_8AA;
+                break;
+            default:
+                return -EINVAL;
+            }
+            /* retrieve tiling mode value */
+            gb_tile_mode = surf_man->hw_info.tile_mode_array[*stencil_tile_mode];
+            si_gb_tile_mode(gb_tile_mode, NULL, NULL, NULL, NULL, NULL, &surf->stencil_tile_split);
+        }
+        if (surf->flags & RADEON_SURF_ZBUFFER) {
+            switch (surf->nsamples) {
+            case 1:
+                *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D;
+                break;
+            case 2:
+                *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_2AA;
+                break;
+            case 4:
+                *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_4AA;
+                break;
+            case 8:
+                *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_2D_8AA;
+                break;
+            default:
+                return -EINVAL;
+            }
+        } else if (surf->flags & RADEON_SURF_SCANOUT) {
+            switch (surf->bpe) {
+            case 2:
+                *tile_mode = SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP;
+                break;
+            case 4:
+                *tile_mode = SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP;
+                break;
+            default:
+                return -EINVAL;
+            }
+        } else {
+            switch (surf->bpe) {
+            case 1:
+                *tile_mode = SI_TILE_MODE_COLOR_2D_8BPP;
+                break;
+            case 2:
+                *tile_mode = SI_TILE_MODE_COLOR_2D_16BPP;
+                break;
+            case 4:
+                *tile_mode = SI_TILE_MODE_COLOR_2D_32BPP;
+                break;
+            case 8:
+            case 16:
+                *tile_mode = SI_TILE_MODE_COLOR_2D_64BPP;
+                break;
+            default:
+                return -EINVAL;
+            }
+        }
+        /* retrieve tiling mode value */
+        gb_tile_mode = surf_man->hw_info.tile_mode_array[*tile_mode];
+        si_gb_tile_mode(gb_tile_mode, NULL, NULL, &surf->mtilea, &surf->bankw, &surf->bankh, &surf->tile_split);
+        break;
+    case RADEON_SURF_MODE_1D:
+        if (surf->flags & RADEON_SURF_SBUFFER) {
+            *stencil_tile_mode = SI_TILE_MODE_DEPTH_STENCIL_1D;
+        }
+        if (surf->flags & RADEON_SURF_ZBUFFER) {
+            *tile_mode = SI_TILE_MODE_DEPTH_STENCIL_1D;
+        } else if (surf->flags & RADEON_SURF_SCANOUT) {
+            *tile_mode = SI_TILE_MODE_COLOR_1D_SCANOUT;
+        } else {
+            *tile_mode = SI_TILE_MODE_COLOR_1D;
+        }
+        break;
+    case RADEON_SURF_MODE_LINEAR_ALIGNED:
+    default:
+        *tile_mode = SI_TILE_MODE_COLOR_LINEAR_ALIGNED;
+    }
+
+    return 0;
+}
 
 static void si_surf_minify(struct radeon_surface *surf,
-			   struct radeon_surface_level *surflevel,
-			   unsigned bpe, unsigned level,
-			   uint32_t xalign, uint32_t yalign, uint32_t zalign, uint32_t slice_align,
-			   unsigned offset)
+                           struct radeon_surface_level *surflevel,
+                           unsigned bpe, unsigned level,
+                           uint32_t xalign, uint32_t yalign, uint32_t zalign,
+                           uint32_t slice_align, unsigned offset)
 {
     surflevel->npix_x = mip_minify(surf->npix_x, level);
     surflevel->npix_y = mip_minify(surf->npix_y, level);
@@ -1043,8 +1436,52 @@  static void si_surf_minify(struct radeon_surface *surf,
     surf->bo_size = offset + surflevel->slice_size * surflevel->nblk_z * surf->array_size;
 }
 
+static void si_surf_minify_2d(struct radeon_surface *surf,
+                              struct radeon_surface_level *surflevel,
+                              unsigned bpe, unsigned level, unsigned slice_pt,
+                              uint32_t xalign, uint32_t yalign, uint32_t zalign,
+                              unsigned mtileb, unsigned offset)
+{
+    unsigned mtile_pr, mtile_ps;
+
+    surflevel->npix_x = mip_minify(surf->npix_x, level);
+    surflevel->npix_y = mip_minify(surf->npix_y, level);
+    surflevel->npix_z = mip_minify(surf->npix_z, level);
+
+    if (level == 0 && surf->last_level > 0) {
+        surflevel->nblk_x = (next_power_of_two(surflevel->npix_x) + surf->blk_w - 1) / surf->blk_w;
+        surflevel->nblk_y = (next_power_of_two(surflevel->npix_y) + surf->blk_h - 1) / surf->blk_h;
+        surflevel->nblk_z = (next_power_of_two(surflevel->npix_z) + surf->blk_d - 1) / surf->blk_d;
+    } else {
+        surflevel->nblk_x = (surflevel->npix_x + surf->blk_w - 1) / surf->blk_w;
+        surflevel->nblk_y = (surflevel->npix_y + surf->blk_h - 1) / surf->blk_h;
+        surflevel->nblk_z = (surflevel->npix_z + surf->blk_d - 1) / surf->blk_d;
+    }
+
+    if (surf->nsamples == 1 && surflevel->mode == RADEON_SURF_MODE_2D) {
+        if (surflevel->nblk_x < xalign || surflevel->nblk_y < yalign) {
+            surflevel->mode = RADEON_SURF_MODE_1D;
+            return;
+        }
+    }
+    surflevel->nblk_x  = ALIGN(surflevel->nblk_x, xalign);
+    surflevel->nblk_y  = ALIGN(surflevel->nblk_y, yalign);
+    surflevel->nblk_z  = ALIGN(surflevel->nblk_z, zalign);
+
+    /* macro tile per row */
+    mtile_pr = surflevel->nblk_x / xalign;
+    /* macro tile per slice */
+    mtile_ps = (mtile_pr * surflevel->nblk_y) / yalign;
+    surflevel->offset = offset;
+    surflevel->pitch_bytes = surflevel->nblk_x * bpe * slice_pt;
+    surflevel->slice_size = mtile_ps * mtileb * slice_pt;
+
+    surf->bo_size = offset + surflevel->slice_size * surflevel->nblk_z * surf->array_size;
+}
+
 static int si_surface_init_linear_aligned(struct radeon_surface_manager *surf_man,
                                           struct radeon_surface *surf,
+                                          unsigned tile_mode,
                                           uint64_t offset, unsigned start_level)
 {
     uint32_t xalign, yalign, zalign, slice_align;
@@ -1062,13 +1499,15 @@  static int si_surface_init_linear_aligned(struct radeon_surface_manager *surf_ma
     /* build mipmap tree */
     for (i = start_level; i <= surf->last_level; i++) {
         surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
-        si_surf_minify(surf, surf->level+i, surf->bpe, i, xalign, yalign,
-                       zalign, slice_align, offset);
+        si_surf_minify(surf, surf->level+i, surf->bpe, i, xalign, yalign, zalign, slice_align, offset);
         /* level0 and first mipmap need to have alignment */
         offset = surf->bo_size;
         if ((i == 0)) {
             offset = ALIGN(offset, surf->bo_alignment);
         }
+        if (surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX) {
+            surf->tiling_index[i] = tile_mode;
+        }
     }
     return 0;
 }
@@ -1076,7 +1515,7 @@  static int si_surface_init_linear_aligned(struct radeon_surface_manager *surf_ma
 static int si_surface_init_1d(struct radeon_surface_manager *surf_man,
                               struct radeon_surface *surf,
                               struct radeon_surface_level *level,
-                              unsigned bpe,
+                              unsigned bpe, unsigned tile_mode,
                               uint64_t offset, unsigned start_level)
 {
     uint32_t xalign, yalign, zalign, slice_align;
@@ -1092,11 +1531,10 @@  static int si_surface_init_1d(struct radeon_surface_manager *surf_man,
     }
 
     if (!start_level) {
-        unsigned alignment = MAX2(256, surf_man->hw_info.group_bytes);
-        surf->bo_alignment = MAX2(surf->bo_alignment, alignment);
+        surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
 
         if (offset) {
-            offset = ALIGN(offset, alignment);
+            offset = ALIGN(offset, surf->bo_alignment);
         }
     }
 
@@ -1109,28 +1547,140 @@  static int si_surface_init_1d(struct radeon_surface_manager *surf_man,
         if ((i == 0)) {
             offset = ALIGN(offset, surf->bo_alignment);
         }
+        if (surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX) {
+            if (surf->level == level) {
+                surf->tiling_index[i] = tile_mode;
+                /* it's ok because stencil is done after */
+                surf->stencil_tiling_index[i] = tile_mode;
+            } else {
+                surf->stencil_tiling_index[i] = tile_mode;
+            }
+        }
     }
     return 0;
 }
 
 static int si_surface_init_1d_miptrees(struct radeon_surface_manager *surf_man,
-                                       struct radeon_surface *surf)
+                                       struct radeon_surface *surf,
+                                       unsigned tile_mode, unsigned stencil_tile_mode)
 {
-    unsigned zs_flags = RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER;
-    int r, is_depth_stencil = (surf->flags & zs_flags) == zs_flags;
-    /* Old libdrm headers didn't have stencil_level in it. This prevents crashes. */
-    struct radeon_surface_level tmp[RADEON_SURF_MAX_LEVEL];
-    struct radeon_surface_level *stencil_level =
-        (surf->flags & RADEON_SURF_HAS_SBUFFER_MIPTREE) ? surf->stencil_level : tmp;
+    int r;
 
-    r = si_surface_init_1d(surf_man, surf, surf->level, surf->bpe, 0, 0);
-    if (r)
+    r = si_surface_init_1d(surf_man, surf, surf->level, surf->bpe, tile_mode, 0, 0);
+    if (r) {
         return r;
+    }
 
-    if (is_depth_stencil) {
-        r = si_surface_init_1d(surf_man, surf, stencil_level, 1,
-                               surf->bo_size, 0);
-        surf->stencil_offset = stencil_level[0].offset;
+    if (surf->flags & RADEON_SURF_SBUFFER) {
+        r = si_surface_init_1d(surf_man, surf, surf->stencil_level, 1, stencil_tile_mode, surf->bo_size, 0);
+        surf->stencil_offset = surf->stencil_level[0].offset;
+    }
+    return r;
+}
+
+static int si_surface_init_2d(struct radeon_surface_manager *surf_man,
+                              struct radeon_surface *surf,
+                              struct radeon_surface_level *level,
+                              unsigned bpe, unsigned tile_mode,
+                              unsigned num_pipes, unsigned num_banks,
+                              unsigned tile_split,
+                              uint64_t offset,
+                              unsigned start_level)
+{
+    unsigned tilew, tileh, tileb;
+    unsigned mtilew, mtileh, mtileb;
+    unsigned slice_pt;
+    unsigned i;
+
+    /* compute tile values */
+    tilew = 8;
+    tileh = 8;
+    tileb = tilew * tileh * bpe * surf->nsamples;
+    /* slices per tile */
+    slice_pt = 1;
+    if (tileb > tile_split) {
+        slice_pt = tileb / tile_split;
+    }
+    tileb = tileb / slice_pt;
+
+    /* macro tile width & height */
+    mtilew = (tilew * surf->bankw * num_pipes) * surf->mtilea;
+    mtileh = (tileh * surf->bankh * num_banks) / surf->mtilea;
+
+    /* macro tile bytes */
+    mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb;
+
+    if (!start_level) {
+        unsigned alignment = MAX2(256, mtileb);
+        surf->bo_alignment = MAX2(surf->bo_alignment, alignment);
+
+        if (offset) {
+            offset = ALIGN(offset, alignment);
+        }
+    }
+
+    /* build mipmap tree */
+    for (i = start_level; i <= surf->last_level; i++) {
+        level[i].mode = RADEON_SURF_MODE_2D;
+        si_surf_minify_2d(surf, level+i, bpe, i, slice_pt, mtilew, mtileh, 1, mtileb, offset);
+        if (level[i].mode == RADEON_SURF_MODE_1D) {
+            switch (tile_mode) {
+            case SI_TILE_MODE_COLOR_2D_8BPP:
+            case SI_TILE_MODE_COLOR_2D_16BPP:
+            case SI_TILE_MODE_COLOR_2D_32BPP:
+            case SI_TILE_MODE_COLOR_2D_64BPP:
+                tile_mode = SI_TILE_MODE_COLOR_1D;
+                break;
+            case SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP:
+            case SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP:
+                tile_mode = SI_TILE_MODE_COLOR_1D_SCANOUT;
+                break;
+            case SI_TILE_MODE_DEPTH_STENCIL_2D:
+                tile_mode = SI_TILE_MODE_DEPTH_STENCIL_1D;
+                break;
+            default:
+                return -EINVAL;
+            }
+            return si_surface_init_1d(surf_man, surf, level, bpe, tile_mode, offset, i);
+        }
+        /* level0 and first mipmap need to have alignment */
+        offset = surf->bo_size;
+        if ((i == 0)) {
+            offset = ALIGN(offset, surf->bo_alignment);
+        }
+        if (surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX) {
+            if (surf->level == level) {
+                surf->tiling_index[i] = tile_mode;
+                /* it's ok because stencil is done after */
+                surf->stencil_tiling_index[i] = tile_mode;
+            } else {
+                surf->stencil_tiling_index[i] = tile_mode;
+            }
+        }
+    }
+    return 0;
+}
+
+static int si_surface_init_2d_miptrees(struct radeon_surface_manager *surf_man,
+                                       struct radeon_surface *surf,
+                                       unsigned tile_mode, unsigned stencil_tile_mode)
+{
+    unsigned num_pipes, num_banks;
+    uint32_t gb_tile_mode;
+    int r;
+
+    /* retrieve tiling mode value */
+    gb_tile_mode = surf_man->hw_info.tile_mode_array[tile_mode];
+    si_gb_tile_mode(gb_tile_mode, &num_pipes, &num_banks, NULL, NULL, NULL, NULL);
+
+    r = si_surface_init_2d(surf_man, surf, surf->level, surf->bpe, tile_mode, num_pipes, num_banks, surf->tile_split, 0, 0);
+    if (r) {
+        return r;
+    }
+
+    if (surf->flags & RADEON_SURF_SBUFFER) {
+        r = si_surface_init_2d(surf_man, surf, surf->stencil_level, 1, stencil_tile_mode, num_pipes, num_banks, surf->stencil_tile_split, surf->bo_size, 0);
+        surf->stencil_offset = surf->stencil_level[0].offset;
     }
     return r;
 }
@@ -1138,7 +1688,7 @@  static int si_surface_init_1d_miptrees(struct radeon_surface_manager *surf_man,
 static int si_surface_init(struct radeon_surface_manager *surf_man,
                            struct radeon_surface *surf)
 {
-    unsigned mode;
+    unsigned mode, tile_mode, stencil_tile_mode;
     int r;
 
     /* MSAA surfaces support the 2D mode only. */
@@ -1150,12 +1700,6 @@  static int si_surface_init(struct radeon_surface_manager *surf_man,
     /* tiling mode */
     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
 
-    /* those are already handled by the kernel tile mode array but we still
-     * need value that won't be rejected by kernel set tiling function
-     */
-    surf->tile_split = 0;
-    surf->stencil_tile_split = 0;
-
     if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
         /* zbuffer only support 1D or 2D tiled surface */
         switch (mode) {
@@ -1170,7 +1714,7 @@  static int si_surface_init(struct radeon_surface_manager *surf_man,
         }
     }
 
-    r = eg_surface_sanity(surf_man, surf, mode);
+    r = si_surface_sanity(surf_man, surf, mode, &tile_mode, &stencil_tile_mode);
     if (r) {
         return r;
     }
@@ -1184,13 +1728,13 @@  static int si_surface_init(struct radeon_surface_manager *surf_man,
         r = r6_surface_init_linear(surf_man, surf, 0, 0);
         break;
     case RADEON_SURF_MODE_LINEAR_ALIGNED:
-        r = si_surface_init_linear_aligned(surf_man, surf, 0, 0);
+        r = si_surface_init_linear_aligned(surf_man, surf, tile_mode, 0, 0);
         break;
     case RADEON_SURF_MODE_1D:
-        r = si_surface_init_1d_miptrees(surf_man, surf);
+        r = si_surface_init_1d_miptrees(surf_man, surf, tile_mode, stencil_tile_mode);
         break;
     case RADEON_SURF_MODE_2D:
-        r = eg_surface_init_2d_miptrees(surf_man, surf);
+        r = si_surface_init_2d_miptrees(surf_man, surf, tile_mode, stencil_tile_mode);
         break;
     default:
         return -EINVAL;
@@ -1198,6 +1742,28 @@  static int si_surface_init(struct radeon_surface_manager *surf_man,
     return r;
 }
 
+/*
+ * depending on surface
+ */
+static int si_surface_best(struct radeon_surface_manager *surf_man,
+                           struct radeon_surface *surf)
+{
+    unsigned mode, tile_mode, stencil_tile_mode;
+
+    /* tiling mode */
+    mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
+
+    if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER) &&
+        !(surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX)) {
+        /* depth/stencil force 1d tiling for old mesa */
+        surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
+        surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
+    }
+
+    return si_surface_sanity(surf_man, surf, mode, &tile_mode, &stencil_tile_mode);
+}
+
+
 /* ===========================================================================
  * public API
  */
@@ -1223,16 +1789,18 @@  struct radeon_surface_manager *radeon_surface_manager_new(int fd)
         }
         surf_man->surface_init = &r6_surface_init;
         surf_man->surface_best = &r6_surface_best;
-    } else {
+    } else if (surf_man->family <= CHIP_ARUBA) {
         if (eg_init_hw_info(surf_man)) {
             goto out_err;
         }
-        if (surf_man->family <= CHIP_ARUBA) {
-            surf_man->surface_init = &eg_surface_init;
-        } else {
-            surf_man->surface_init = &si_surface_init;
-        }
+        surf_man->surface_init = &eg_surface_init;
         surf_man->surface_best = &eg_surface_best;
+    } else {
+        if (si_init_hw_info(surf_man)) {
+            goto out_err;
+        }
+        surf_man->surface_init = &si_surface_init;
+        surf_man->surface_best = &si_surface_best;
     }
 
     return surf_man;
diff --git a/radeon/radeon_surface.h b/radeon/radeon_surface.h
index ed4b994..2babfd7 100644
--- a/radeon/radeon_surface.h
+++ b/radeon/radeon_surface.h
@@ -55,6 +55,7 @@ 
 #define RADEON_SURF_ZBUFFER                     (1 << 17)
 #define RADEON_SURF_SBUFFER                     (1 << 18)
 #define RADEON_SURF_HAS_SBUFFER_MIPTREE         (1 << 19)
+#define RADEON_SURF_HAS_TILE_MODE_INDEX         (1 << 20)
 
 #define RADEON_SURF_GET(v, field)   (((v) >> RADEON_SURF_ ## field ## _SHIFT) & RADEON_SURF_ ## field ## _MASK)
 #define RADEON_SURF_SET(v, field)   (((v) & RADEON_SURF_ ## field ## _MASK) << RADEON_SURF_ ## field ## _SHIFT)
@@ -76,6 +77,34 @@  struct radeon_surface_level {
     uint32_t                    mode;
 };
 
+enum si_tiling_mode {
+    SI_TILING_AUTO = 0,
+
+    SI_TILING_COLOR_1D,
+    SI_TILING_COLOR_1D_SCANOUT,
+    SI_TILING_COLOR_2D_8BPP,
+    SI_TILING_COLOR_2D_16BPP,
+    SI_TILING_COLOR_2D_32BPP,
+    SI_TILING_COLOR_2D_64BPP,
+    SI_TILING_COLOR_2D_SCANOUT_16BPP,
+    SI_TILING_COLOR_2D_SCANOUT_32BPP,
+    SI_TILING_COLOR_LINEAR,
+
+    SI_TILING_STENCIL_1D,
+    SI_TILING_STENCIL_2D,
+    SI_TILING_STENCIL_2D_2AA,
+    SI_TILING_STENCIL_2D_4AA,
+    SI_TILING_STENCIL_2D_8AA,
+
+    SI_TILING_DEPTH_1D,
+    SI_TILING_DEPTH_2D,
+    SI_TILING_DEPTH_2D_2AA,
+    SI_TILING_DEPTH_2D_4AA,
+    SI_TILING_DEPTH_2D_8AA,
+
+    SI_TILING_LAST_MODE,
+};
+
 struct radeon_surface {
     uint32_t                    npix_x;
     uint32_t                    npix_y;
@@ -104,6 +133,8 @@  struct radeon_surface {
     uint64_t                    stencil_offset;
     struct radeon_surface_level level[RADEON_SURF_MAX_LEVEL];
     struct radeon_surface_level stencil_level[RADEON_SURF_MAX_LEVEL];
+    uint32_t                    tiling_index[RADEON_SURF_MAX_LEVEL];
+    uint32_t                    stencil_tiling_index[RADEON_SURF_MAX_LEVEL];
 };
 
 struct radeon_surface_manager *radeon_surface_manager_new(int fd);