radeon: add surface allocator helper v10
authorJerome Glisse <jglisse@redhat.com>
Sat, 10 Dec 2011 02:07:15 +0000 (21:07 -0500)
committerJerome Glisse <jglisse@redhat.com>
Wed, 1 Feb 2012 22:11:29 +0000 (17:11 -0500)
The surface allocator is able to build complete miptree when allocating
surface for r600/r700/evergreen/northern islands GPU family. It also
compute bo size and alignment for render buffer, depth buffer and
scanout buffer.

v2 fix r6xx/r7xx 2D tiling width align computation
v3 add tile split support and fix 1d texture alignment
v4 rework to more properly support compressed format, split surface pixel
   size and surface element size in separate fields
v5 support texture array (still issue on r6xx)
v6 split surface value computation and mipmap tree building, rework eg
   and newer computation
v7 add a check for tile split and 2d tiled
v8 initialize mode value before testing it in all case, reenable
   2D macro tile mode on r6xx for cubemap and array. Fix cubemap
   to force array size to the number of face.
v9 fix handling of stencil buffer on evergreen
v10 on evergreen depth buffer need to have enough room for a stencil
    buffer just after depth one

Signed-off-by: Jerome Glisse <jglisse@redhat.com>
include/drm/radeon_drm.h
radeon/Makefile.am
radeon/r600_pci_ids.h [new file with mode: 0644]
radeon/radeon_surface.c [new file with mode: 0644]
radeon/radeon_surface.h [new file with mode: 0644]

index 3b762d6..00d66b3 100644 (file)
@@ -802,13 +802,23 @@ struct drm_radeon_gem_create {
        uint32_t        flags;
 };
 
-#define RADEON_TILING_MACRO       0x1
-#define RADEON_TILING_MICRO       0x2
-#define RADEON_TILING_SWAP_16BIT  0x4
-#define RADEON_TILING_SWAP_32BIT  0x8
-#define RADEON_TILING_SURFACE     0x10 /* this object requires a surface
-                                       * when mapped - i.e. front buffer */
-#define RADEON_TILING_MICRO_SQUARE 0x20
+#define RADEON_TILING_MACRO                            0x1
+#define RADEON_TILING_MICRO                            0x2
+#define RADEON_TILING_SWAP_16BIT                       0x4
+#define RADEON_TILING_SWAP_32BIT                       0x8
+/* this object requires a surface when mapped - i.e. front buffer */
+#define RADEON_TILING_SURFACE                          0x10
+#define RADEON_TILING_MICRO_SQUARE                     0x20
+#define RADEON_TILING_EG_BANKW_SHIFT                   8
+#define RADEON_TILING_EG_BANKW_MASK                    0xf
+#define RADEON_TILING_EG_BANKH_SHIFT                   12
+#define RADEON_TILING_EG_BANKH_MASK                    0xf
+#define RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT       16
+#define RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK                0xf
+#define RADEON_TILING_EG_TILE_SPLIT_SHIFT              24
+#define RADEON_TILING_EG_TILE_SPLIT_MASK               0xf
+#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT      28
+#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK       0xf
 
 struct drm_radeon_gem_set_tiling {
        uint32_t        handle;
index dc94b5f..e64aff4 100644 (file)
@@ -40,6 +40,7 @@ libdrm_radeon_la_SOURCES = \
        radeon_cs_space.c \
        radeon_bo.c \
        radeon_cs.c \
+       radeon_surface.c \
        bof.c \
        bof.h
 
@@ -47,6 +48,7 @@ libdrm_radeonincludedir = ${includedir}/libdrm
 libdrm_radeoninclude_HEADERS = \
        radeon_bo.h \
        radeon_cs.h \
+       radeon_surface.h \
        radeon_bo_gem.h \
        radeon_cs_gem.h \
        radeon_bo_int.h \
diff --git a/radeon/r600_pci_ids.h b/radeon/r600_pci_ids.h
new file mode 100644 (file)
index 0000000..0ffb741
--- /dev/null
@@ -0,0 +1,271 @@
+CHIPSET(0x9400, R600_9400, R600)
+CHIPSET(0x9401, R600_9401, R600)
+CHIPSET(0x9402, R600_9402, R600)
+CHIPSET(0x9403, R600_9403, R600)
+CHIPSET(0x9405, R600_9405, R600)
+CHIPSET(0x940A, R600_940A, R600)
+CHIPSET(0x940B, R600_940B, R600)
+CHIPSET(0x940F, R600_940F, R600)
+
+CHIPSET(0x94C0, RV610_94C0, RV610)
+CHIPSET(0x94C1, RV610_94C1, RV610)
+CHIPSET(0x94C3, RV610_94C3, RV610)
+CHIPSET(0x94C4, RV610_94C4, RV610)
+CHIPSET(0x94C5, RV610_94C5, RV610)
+CHIPSET(0x94C6, RV610_94C6, RV610)
+CHIPSET(0x94C7, RV610_94C7, RV610)
+CHIPSET(0x94C8, RV610_94C8, RV610)
+CHIPSET(0x94C9, RV610_94C9, RV610)
+CHIPSET(0x94CB, RV610_94CB, RV610)
+CHIPSET(0x94CC, RV610_94CC, RV610)
+CHIPSET(0x94CD, RV610_94CD, RV610)
+
+CHIPSET(0x9580, RV630_9580, RV630)
+CHIPSET(0x9581, RV630_9581, RV630)
+CHIPSET(0x9583, RV630_9583, RV630)
+CHIPSET(0x9586, RV630_9586, RV630)
+CHIPSET(0x9587, RV630_9587, RV630)
+CHIPSET(0x9588, RV630_9588, RV630)
+CHIPSET(0x9589, RV630_9589, RV630)
+CHIPSET(0x958A, RV630_958A, RV630)
+CHIPSET(0x958B, RV630_958B, RV630)
+CHIPSET(0x958C, RV630_958C, RV630)
+CHIPSET(0x958D, RV630_958D, RV630)
+CHIPSET(0x958E, RV630_958E, RV630)
+CHIPSET(0x958F, RV630_958F, RV630)
+
+CHIPSET(0x9500, RV670_9500, RV670)
+CHIPSET(0x9501, RV670_9501, RV670)
+CHIPSET(0x9504, RV670_9504, RV670)
+CHIPSET(0x9505, RV670_9505, RV670)
+CHIPSET(0x9506, RV670_9506, RV670)
+CHIPSET(0x9507, RV670_9507, RV670)
+CHIPSET(0x9508, RV670_9508, RV670)
+CHIPSET(0x9509, RV670_9509, RV670)
+CHIPSET(0x950F, RV670_950F, RV670)
+CHIPSET(0x9511, RV670_9511, RV670)
+CHIPSET(0x9515, RV670_9515, RV670)
+CHIPSET(0x9517, RV670_9517, RV670)
+CHIPSET(0x9519, RV670_9519, RV670)
+
+CHIPSET(0x95C0, RV620_95C0, RV620)
+CHIPSET(0x95C2, RV620_95C2, RV620)
+CHIPSET(0x95C4, RV620_95C4, RV620)
+CHIPSET(0x95C5, RV620_95C5, RV620)
+CHIPSET(0x95C6, RV620_95C6, RV620)
+CHIPSET(0x95C7, RV620_95C7, RV620)
+CHIPSET(0x95C9, RV620_95C9, RV620)
+CHIPSET(0x95CC, RV620_95CC, RV620)
+CHIPSET(0x95CD, RV620_95CD, RV620)
+CHIPSET(0x95CE, RV620_95CE, RV620)
+CHIPSET(0x95CF, RV620_95CF, RV620)
+
+CHIPSET(0x9590, RV635_9590, RV635)
+CHIPSET(0x9591, RV635_9591, RV635)
+CHIPSET(0x9593, RV635_9593, RV635)
+CHIPSET(0x9595, RV635_9595, RV635)
+CHIPSET(0x9596, RV635_9596, RV635)
+CHIPSET(0x9597, RV635_9597, RV635)
+CHIPSET(0x9598, RV635_9598, RV635)
+CHIPSET(0x9599, RV635_9599, RV635)
+CHIPSET(0x959B, RV635_959B, RV635)
+
+CHIPSET(0x9610, RS780_9610, RS780)
+CHIPSET(0x9611, RS780_9611, RS780)
+CHIPSET(0x9612, RS780_9612, RS780)
+CHIPSET(0x9613, RS780_9613, RS780)
+CHIPSET(0x9614, RS780_9614, RS780)
+CHIPSET(0x9615, RS780_9615, RS780)
+CHIPSET(0x9616, RS780_9616, RS780)
+
+CHIPSET(0x9710, RS880_9710, RS880)
+CHIPSET(0x9711, RS880_9711, RS880)
+CHIPSET(0x9712, RS880_9712, RS880)
+CHIPSET(0x9713, RS880_9713, RS880)
+CHIPSET(0x9714, RS880_9714, RS880)
+CHIPSET(0x9715, RS880_9715, RS880)
+
+CHIPSET(0x9440, RV770_9440, RV770)
+CHIPSET(0x9441, RV770_9441, RV770)
+CHIPSET(0x9442, RV770_9442, RV770)
+CHIPSET(0x9443, RV770_9443, RV770)
+CHIPSET(0x9444, RV770_9444, RV770)
+CHIPSET(0x9446, RV770_9446, RV770)
+CHIPSET(0x944A, RV770_944A, RV770)
+CHIPSET(0x944B, RV770_944B, RV770)
+CHIPSET(0x944C, RV770_944C, RV770)
+CHIPSET(0x944E, RV770_944E, RV770)
+CHIPSET(0x9450, RV770_9450, RV770)
+CHIPSET(0x9452, RV770_9452, RV770)
+CHIPSET(0x9456, RV770_9456, RV770)
+CHIPSET(0x945A, RV770_945A, RV770)
+CHIPSET(0x945B, RV770_945B, RV770)
+CHIPSET(0x945E, RV770_945E, RV770)
+CHIPSET(0x9460, RV790_9460, RV770)
+CHIPSET(0x9462, RV790_9462, RV770)
+CHIPSET(0x946A, RV770_946A, RV770)
+CHIPSET(0x946B, RV770_946B, RV770)
+CHIPSET(0x947A, RV770_947A, RV770)
+CHIPSET(0x947B, RV770_947B, RV770)
+
+CHIPSET(0x9480, RV730_9480, RV730)
+CHIPSET(0x9487, RV730_9487, RV730)
+CHIPSET(0x9488, RV730_9488, RV730)
+CHIPSET(0x9489, RV730_9489, RV730)
+CHIPSET(0x948A, RV730_948A, RV730)
+CHIPSET(0x948F, RV730_948F, RV730)
+CHIPSET(0x9490, RV730_9490, RV730)
+CHIPSET(0x9491, RV730_9491, RV730)
+CHIPSET(0x9495, RV730_9495, RV730)
+CHIPSET(0x9498, RV730_9498, RV730)
+CHIPSET(0x949C, RV730_949C, RV730)
+CHIPSET(0x949E, RV730_949E, RV730)
+CHIPSET(0x949F, RV730_949F, RV730)
+
+CHIPSET(0x9540, RV710_9540, RV710)
+CHIPSET(0x9541, RV710_9541, RV710)
+CHIPSET(0x9542, RV710_9542, RV710)
+CHIPSET(0x954E, RV710_954E, RV710)
+CHIPSET(0x954F, RV710_954F, RV710)
+CHIPSET(0x9552, RV710_9552, RV710)
+CHIPSET(0x9553, RV710_9553, RV710)
+CHIPSET(0x9555, RV710_9555, RV710)
+CHIPSET(0x9557, RV710_9557, RV710)
+CHIPSET(0x955F, RV710_955F, RV710)
+
+CHIPSET(0x94A0, RV740_94A0, RV740)
+CHIPSET(0x94A1, RV740_94A1, RV740)
+CHIPSET(0x94A3, RV740_94A3, RV740)
+CHIPSET(0x94B1, RV740_94B1, RV740)
+CHIPSET(0x94B3, RV740_94B3, RV740)
+CHIPSET(0x94B4, RV740_94B4, RV740)
+CHIPSET(0x94B5, RV740_94B5, RV740)
+CHIPSET(0x94B9, RV740_94B9, RV740)
+
+CHIPSET(0x68E0, CEDAR_68E0, CEDAR)
+CHIPSET(0x68E1, CEDAR_68E1, CEDAR)
+CHIPSET(0x68E4, CEDAR_68E4, CEDAR)
+CHIPSET(0x68E5, CEDAR_68E5, CEDAR)
+CHIPSET(0x68E8, CEDAR_68E8, CEDAR)
+CHIPSET(0x68E9, CEDAR_68E9, CEDAR)
+CHIPSET(0x68F1, CEDAR_68F1, CEDAR)
+CHIPSET(0x68F2, CEDAR_68F2, CEDAR)
+CHIPSET(0x68F8, CEDAR_68F8, CEDAR)
+CHIPSET(0x68F9, CEDAR_68F9, CEDAR)
+CHIPSET(0x68FE, CEDAR_68FE, CEDAR)
+
+CHIPSET(0x68C0, REDWOOD_68C0, REDWOOD)
+CHIPSET(0x68C1, REDWOOD_68C1, REDWOOD)
+CHIPSET(0x68C8, REDWOOD_68C8, REDWOOD)
+CHIPSET(0x68C9, REDWOOD_68C9, REDWOOD)
+CHIPSET(0x68D8, REDWOOD_68D8, REDWOOD)
+CHIPSET(0x68D9, REDWOOD_68D9, REDWOOD)
+CHIPSET(0x68DA, REDWOOD_68DA, REDWOOD)
+CHIPSET(0x68DE, REDWOOD_68DE, REDWOOD)
+
+CHIPSET(0x68A0, JUNIPER_68A0, JUNIPER)
+CHIPSET(0x68A1, JUNIPER_68A1, JUNIPER)
+CHIPSET(0x68A8, JUNIPER_68A8, JUNIPER)
+CHIPSET(0x68A9, JUNIPER_68A9, JUNIPER)
+CHIPSET(0x68B0, JUNIPER_68B0, JUNIPER)
+CHIPSET(0x68B8, JUNIPER_68B8, JUNIPER)
+CHIPSET(0x68B9, JUNIPER_68B9, JUNIPER)
+CHIPSET(0x68BA, JUNIPER_68BA, JUNIPER)
+CHIPSET(0x68BE, JUNIPER_68BE, JUNIPER)
+CHIPSET(0x68BF, JUNIPER_68BF, JUNIPER)
+
+CHIPSET(0x6880, CYPRESS_6880, CYPRESS)
+CHIPSET(0x6888, CYPRESS_6888, CYPRESS)
+CHIPSET(0x6889, CYPRESS_6889, CYPRESS)
+CHIPSET(0x688A, CYPRESS_688A, CYPRESS)
+CHIPSET(0x6898, CYPRESS_6898, CYPRESS)
+CHIPSET(0x6899, CYPRESS_6899, CYPRESS)
+CHIPSET(0x689B, CYPRESS_689B, CYPRESS)
+CHIPSET(0x689E, CYPRESS_689E, CYPRESS)
+
+CHIPSET(0x689C, HEMLOCK_689C, HEMLOCK)
+CHIPSET(0x689D, HEMLOCK_689D, HEMLOCK)
+
+CHIPSET(0x9802, PALM_9802, PALM)
+CHIPSET(0x9803, PALM_9803, PALM)
+CHIPSET(0x9804, PALM_9804, PALM)
+CHIPSET(0x9805, PALM_9805, PALM)
+CHIPSET(0x9806, PALM_9806, PALM)
+CHIPSET(0x9807, PALM_9807, PALM)
+
+CHIPSET(0x9640, SUMO_9640,  SUMO)
+CHIPSET(0x9641, SUMO_9641,  SUMO)
+CHIPSET(0x9642, SUMO2_9642, SUMO2)
+CHIPSET(0x9643, SUMO2_9643, SUMO2)
+CHIPSET(0x9644, SUMO2_9644, SUMO2)
+CHIPSET(0x9645, SUMO2_9645, SUMO2)
+CHIPSET(0x9647, SUMO_9647,  SUMO)
+CHIPSET(0x9648, SUMO_9648,  SUMO)
+CHIPSET(0x964a, SUMO_964A,  SUMO)
+CHIPSET(0x964e, SUMO_964E,  SUMO)
+CHIPSET(0x964f, SUMO_964F,  SUMO)
+
+CHIPSET(0x6700, CAYMAN_6700, CAYMAN)
+CHIPSET(0x6701, CAYMAN_6701, CAYMAN)
+CHIPSET(0x6702, CAYMAN_6702, CAYMAN)
+CHIPSET(0x6703, CAYMAN_6703, CAYMAN)
+CHIPSET(0x6704, CAYMAN_6704, CAYMAN)
+CHIPSET(0x6705, CAYMAN_6705, CAYMAN)
+CHIPSET(0x6706, CAYMAN_6706, CAYMAN)
+CHIPSET(0x6707, CAYMAN_6707, CAYMAN)
+CHIPSET(0x6708, CAYMAN_6708, CAYMAN)
+CHIPSET(0x6709, CAYMAN_6709, CAYMAN)
+CHIPSET(0x6718, CAYMAN_6718, CAYMAN)
+CHIPSET(0x6719, CAYMAN_6719, CAYMAN)
+CHIPSET(0x671C, CAYMAN_671C, CAYMAN)
+CHIPSET(0x671D, CAYMAN_671D, CAYMAN)
+CHIPSET(0x671F, CAYMAN_671F, CAYMAN)
+
+CHIPSET(0x6720, BARTS_6720, BARTS)
+CHIPSET(0x6721, BARTS_6721, BARTS)
+CHIPSET(0x6722, BARTS_6722, BARTS)
+CHIPSET(0x6723, BARTS_6723, BARTS)
+CHIPSET(0x6724, BARTS_6724, BARTS)
+CHIPSET(0x6725, BARTS_6725, BARTS)
+CHIPSET(0x6726, BARTS_6726, BARTS)
+CHIPSET(0x6727, BARTS_6727, BARTS)
+CHIPSET(0x6728, BARTS_6728, BARTS)
+CHIPSET(0x6729, BARTS_6729, BARTS)
+CHIPSET(0x6738, BARTS_6738, BARTS)
+CHIPSET(0x6739, BARTS_6739, BARTS)
+CHIPSET(0x673E, BARTS_673E, BARTS)
+CHIPSET(0x6740, TURKS_6740, TURKS)
+CHIPSET(0x6741, TURKS_6741, TURKS)
+CHIPSET(0x6742, TURKS_6742, TURKS)
+CHIPSET(0x6743, TURKS_6743, TURKS)
+CHIPSET(0x6744, TURKS_6744, TURKS)
+CHIPSET(0x6745, TURKS_6745, TURKS)
+CHIPSET(0x6746, TURKS_6746, TURKS)
+CHIPSET(0x6747, TURKS_6747, TURKS)
+CHIPSET(0x6748, TURKS_6748, TURKS)
+CHIPSET(0x6749, TURKS_6749, TURKS)
+CHIPSET(0x6750, TURKS_6750, TURKS)
+CHIPSET(0x6758, TURKS_6758, TURKS)
+CHIPSET(0x6759, TURKS_6759, TURKS)
+CHIPSET(0x675F, TURKS_675F, TURKS)
+CHIPSET(0x6840, TURKS_6840, TURKS)
+CHIPSET(0x6841, TURKS_6841, TURKS)
+CHIPSET(0x6842, TURKS_6842, TURKS)
+CHIPSET(0x6843, TURKS_6843, TURKS)
+CHIPSET(0x6849, TURKS_6849, TURKS)
+CHIPSET(0x6850, TURKS_6850, TURKS)
+CHIPSET(0x6858, TURKS_6858, TURKS)
+CHIPSET(0x6859, TURKS_6859, TURKS)
+
+CHIPSET(0x6760, CAICOS_6760, CAICOS)
+CHIPSET(0x6761, CAICOS_6761, CAICOS)
+CHIPSET(0x6762, CAICOS_6762, CAICOS)
+CHIPSET(0x6763, CAICOS_6763, CAICOS)
+CHIPSET(0x6764, CAICOS_6764, CAICOS)
+CHIPSET(0x6765, CAICOS_6765, CAICOS)
+CHIPSET(0x6766, CAICOS_6766, CAICOS)
+CHIPSET(0x6767, CAICOS_6767, CAICOS)
+CHIPSET(0x6768, CAICOS_6768, CAICOS)
+CHIPSET(0x6770, CAICOS_6770, CAICOS)
+CHIPSET(0x6778, CAICOS_6778, CAICOS)
+CHIPSET(0x6779, CAICOS_6779, CAICOS)
diff --git a/radeon/radeon_surface.c b/radeon/radeon_surface.c
new file mode 100644 (file)
index 0000000..0f01e2e
--- /dev/null
@@ -0,0 +1,995 @@
+/*
+ * Copyright © 2011 Red Hat All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ *      Jérôme Glisse <jglisse@redhat.com>
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include "drm.h"
+#include "xf86drm.h"
+#include "radeon_drm.h"
+#include "radeon_surface.h"
+
+#define ALIGN(value, alignment) (((value) + alignment - 1) & ~(alignment - 1))
+#define MAX2(A, B)              ((A) > (B) ? (A) : (B))
+#define MIN2(A, B)              ((A) < (B) ? (A) : (B))
+
+/* keep this private */
+enum radeon_family {
+    CHIP_UNKNOWN,
+    CHIP_R600,
+    CHIP_RV610,
+    CHIP_RV630,
+    CHIP_RV670,
+    CHIP_RV620,
+    CHIP_RV635,
+    CHIP_RS780,
+    CHIP_RS880,
+    CHIP_RV770,
+    CHIP_RV730,
+    CHIP_RV710,
+    CHIP_RV740,
+    CHIP_CEDAR,
+    CHIP_REDWOOD,
+    CHIP_JUNIPER,
+    CHIP_CYPRESS,
+    CHIP_HEMLOCK,
+    CHIP_PALM,
+    CHIP_SUMO,
+    CHIP_SUMO2,
+    CHIP_BARTS,
+    CHIP_TURKS,
+    CHIP_CAICOS,
+    CHIP_CAYMAN,
+    CHIP_LAST,
+};
+
+typedef int (*hw_init_surface_t)(struct radeon_surface_manager *surf_man,
+                                 struct radeon_surface *surf);
+typedef int (*hw_best_surface_t)(struct radeon_surface_manager *surf_man,
+                                 struct radeon_surface *surf);
+
+struct radeon_hw_info {
+    /* apply to r6, eg */
+    uint32_t                    group_bytes;
+    uint32_t                    num_banks;
+    uint32_t                    num_pipes;
+    /* apply to eg */
+    uint32_t                    row_size;
+    unsigned                    allow_2d;
+};
+
+struct radeon_surface_manager {
+    int                         fd;
+    uint32_t                    device_id;
+    struct radeon_hw_info       hw_info;
+    unsigned                    family;
+    hw_init_surface_t           surface_init;
+    hw_best_surface_t           surface_best;
+};
+
+/* helper */
+static int radeon_get_value(int fd, unsigned req, uint32_t *value)
+{
+    struct drm_radeon_info info = {};
+    int r;
+
+    *value = 0;
+    info.request = req;
+    info.value = (uintptr_t)value;
+    r = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info,
+                            sizeof(struct drm_radeon_info));
+    return r;
+}
+
+static int radeon_get_family(struct radeon_surface_manager *surf_man)
+{
+    switch (surf_man->device_id) {
+#define CHIPSET(pci_id, name, fam) case pci_id: surf_man->family = CHIP_##fam; break;
+#include "r600_pci_ids.h"
+#undef CHIPSET
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static unsigned next_power_of_two(unsigned x)
+{
+   if (x <= 1)
+       return 1;
+
+   return (1 << ((sizeof(unsigned) * 8) - __builtin_clz(x - 1)));
+}
+
+static unsigned mip_minify(unsigned size, unsigned level)
+{
+    unsigned val;
+
+    val = MAX2(1, size >> level);
+    if (level > 0)
+        val = next_power_of_two(val);
+    return val;
+}
+
+static void surf_minify(struct radeon_surface *surf,
+                        unsigned level,
+                        uint32_t xalign, uint32_t yalign, uint32_t zalign,
+                        unsigned offset)
+{
+    surf->level[level].nblk_x = mip_minify(surf->nblk_x, level);
+    surf->level[level].nblk_y = mip_minify(surf->nblk_y, level);
+    surf->level[level].nblk_z = mip_minify(surf->nblk_z, level);
+    surf->level[level].npix_x = mip_minify(surf->npix_x, level);
+    surf->level[level].npix_y = mip_minify(surf->npix_y, level);
+    surf->level[level].npix_z = mip_minify(surf->npix_z, level);
+    if (surf->level[level].mode == RADEON_SURF_MODE_2D) {
+        if (surf->level[level].nblk_x < xalign || surf->level[level].nblk_y < yalign) {
+            surf->level[level].mode = RADEON_SURF_MODE_1D;
+            return;
+        }
+    }
+    surf->level[level].nblk_x  = ALIGN(surf->level[level].nblk_x, xalign);
+    surf->level[level].nblk_y  = ALIGN(surf->level[level].nblk_y, yalign);
+    surf->level[level].nblk_z  = ALIGN(surf->level[level].nblk_z, zalign);
+
+    surf->level[level].offset = offset;
+    surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe;
+    surf->level[level].slice_size = surf->level[level].pitch_bytes * surf->level[level].nblk_y;
+
+    surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
+}
+
+/* ===========================================================================
+ * r600/r700 family
+ */
+static int r6_init_hw_info(struct radeon_surface_manager *surf_man)
+{
+    uint32_t tiling_config;
+    int r;
+
+    r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
+                         &tiling_config);
+    if (r) {
+        return r;
+    }
+
+    switch ((tiling_config & 0xe) >> 1) {
+    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:
+        return -EINVAL;
+    }
+
+    switch ((tiling_config & 0x30) >> 4) {
+    case 0:
+        surf_man->hw_info.num_banks = 4;
+        break;
+    case 1:
+        surf_man->hw_info.num_banks = 8;
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    switch ((tiling_config & 0xc0) >> 6) {
+    case 0:
+        surf_man->hw_info.group_bytes = 256;
+        break;
+    case 1:
+        surf_man->hw_info.group_bytes = 512;
+        break;
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int r6_surface_init_linear(struct radeon_surface_manager *surf_man,
+                                  struct radeon_surface *surf,
+                                  uint64_t offset, unsigned start_level)
+{
+    uint32_t xalign, yalign, zalign;
+    unsigned i;
+
+    /* compute alignment */
+    if (!start_level) {
+        surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
+    }
+    /* the 32 alignment is for scanout, cb or db but to allow texture to be
+     * easily bound as such we force this alignment to all surface
+     */
+    xalign = MAX2(32, surf_man->hw_info.group_bytes / surf->bpe);
+    yalign = 1;
+    zalign = 1;
+
+    /* build mipmap tree */
+    for (i = start_level; i <= surf->last_level; i++) {
+        surf->level[i].mode = RADEON_SURF_MODE_LINEAR;
+        surf_minify(surf, i, xalign, yalign, zalign, offset);
+        /* level0 and first mipmap need to have alignment */
+        offset = surf->bo_size;
+        if ((i == 0)) {
+            offset = ALIGN(offset, surf->bo_alignment);
+        }
+    }
+    return 0;
+}
+
+static int r6_surface_init_linear_aligned(struct radeon_surface_manager *surf_man,
+                                          struct radeon_surface *surf,
+                                          uint64_t offset, unsigned start_level)
+{
+    uint32_t xalign, yalign, zalign;
+    unsigned i;
+
+    /* compute alignment */
+    if (!start_level) {
+        surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
+    }
+    xalign = MAX2(64, surf_man->hw_info.group_bytes / surf->bpe);
+    yalign = 1;
+    zalign = 1;
+
+    /* build mipmap tree */
+    for (i = start_level; i <= surf->last_level; i++) {
+        surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
+        surf_minify(surf, i, xalign, yalign, zalign, offset);
+        /* level0 and first mipmap need to have alignment */
+        offset = surf->bo_size;
+        if ((i == 0)) {
+            offset = ALIGN(offset, surf->bo_alignment);
+        }
+    }
+    return 0;
+}
+
+static int r6_surface_init_1d(struct radeon_surface_manager *surf_man,
+                              struct radeon_surface *surf,
+                              uint64_t offset, unsigned start_level)
+{
+    uint32_t xalign, yalign, zalign, tilew;
+    unsigned i;
+
+    /* compute alignment */
+    tilew = 8;
+    xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
+    xalign = MAX2(tilew, xalign);
+    yalign = tilew;
+    zalign = 1;
+    if (!start_level) {
+        surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
+    }
+
+    /* build mipmap tree */
+    for (i = start_level; i <= surf->last_level; i++) {
+        surf->level[i].mode = RADEON_SURF_MODE_1D;
+        surf_minify(surf, i, xalign, yalign, zalign, offset);
+        /* level0 and first mipmap need to have alignment */
+        offset = surf->bo_size;
+        if ((i == 0)) {
+            offset = ALIGN(offset, surf->bo_alignment);
+        }
+    }
+    return 0;
+}
+
+static int r6_surface_init_2d(struct radeon_surface_manager *surf_man,
+                              struct radeon_surface *surf,
+                              uint64_t offset, unsigned start_level)
+{
+    uint32_t xalign, yalign, zalign, tilew;
+    unsigned i;
+
+    /* compute alignment */
+    tilew = 8;
+    zalign = 1;
+    xalign = (surf_man->hw_info.group_bytes * surf_man->hw_info.num_banks) /
+             (tilew * surf->bpe * surf->nsamples);
+    xalign = MAX2(tilew * surf_man->hw_info.num_banks, xalign);
+    yalign = tilew * surf_man->hw_info.num_pipes;
+    if (!start_level) {
+        surf->bo_alignment =
+            MAX2(surf_man->hw_info.num_pipes *
+                 surf_man->hw_info.num_banks *
+                 surf->bpe * 64,
+                 xalign * yalign * surf->nsamples * surf->bpe);
+    }
+
+    /* build mipmap tree */
+    for (i = start_level; i <= surf->last_level; i++) {
+        surf->level[i].mode = RADEON_SURF_MODE_2D;
+        surf_minify(surf, i, xalign, yalign, zalign, offset);
+        if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
+            return r6_surface_init_1d(surf_man, surf, offset, i);
+        }
+        /* level0 and first mipmap need to have alignment */
+        offset = surf->bo_size;
+        if ((i == 0)) {
+            offset = ALIGN(offset, surf->bo_alignment);
+        }
+    }
+    return 0;
+}
+
+static int r6_surface_init(struct radeon_surface_manager *surf_man,
+                           struct radeon_surface *surf)
+{
+    unsigned mode;
+    int r;
+
+    /* tiling mode */
+    mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
+
+    /* check surface dimension */
+    if (surf->npix_x > 8192 || surf->npix_y > 8192 || surf->npix_z > 8192) {
+        return -EINVAL;
+    }
+
+    /* check mipmap last_level */
+    if (surf->last_level > 14) {
+        return -EINVAL;
+    }
+
+    /* check tiling mode */
+    switch (mode) {
+    case RADEON_SURF_MODE_LINEAR:
+        r = r6_surface_init_linear(surf_man, surf, 0, 0);
+        break;
+    case RADEON_SURF_MODE_LINEAR_ALIGNED:
+        r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
+        break;
+    case RADEON_SURF_MODE_1D:
+        r = r6_surface_init_1d(surf_man, surf, 0, 0);
+        break;
+    case RADEON_SURF_MODE_2D:
+        r = r6_surface_init_2d(surf_man, surf, 0, 0);
+        break;
+    default:
+        return -EINVAL;
+    }
+    return r;
+}
+
+static int r6_surface_best(struct radeon_surface_manager *surf_man,
+                           struct radeon_surface *surf)
+{
+    /* no value to optimize for r6xx/r7xx */
+    return 0;
+}
+
+
+/* ===========================================================================
+ * evergreen family
+ */
+static int eg_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 >= 14) {
+        surf_man->hw_info.allow_2d = 1;
+    }
+
+    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:
+        return -EINVAL;
+    }
+
+    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:
+        return -EINVAL;
+    }
+
+    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:
+        return -EINVAL;
+    }
+
+    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:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static void eg_surf_minify(struct radeon_surface *surf,
+                           unsigned level,
+                           unsigned slice_pt,
+                           unsigned mtilew,
+                           unsigned mtileh,
+                           unsigned mtileb,
+                           unsigned offset)
+{
+    unsigned mtile_pr, mtile_ps;
+
+    surf->level[level].nblk_x = mip_minify(surf->nblk_x, level);
+    surf->level[level].nblk_y = mip_minify(surf->nblk_y, level);
+    surf->level[level].nblk_z = mip_minify(surf->nblk_z, level);
+    surf->level[level].npix_x = mip_minify(surf->npix_x, level);
+    surf->level[level].npix_y = mip_minify(surf->npix_y, level);
+    surf->level[level].npix_z = mip_minify(surf->npix_z, level);
+    if (surf->level[level].mode == RADEON_SURF_MODE_2D) {
+        if (surf->level[level].nblk_x < mtilew || surf->level[level].nblk_y < mtileh) {
+            surf->level[level].mode = RADEON_SURF_MODE_1D;
+            return;
+        }
+    }
+    surf->level[level].nblk_x  = ALIGN(surf->level[level].nblk_x, mtilew);
+    surf->level[level].nblk_y  = ALIGN(surf->level[level].nblk_y, mtileh);
+    surf->level[level].nblk_z  = ALIGN(surf->level[level].nblk_z, 1);
+
+    /* macro tile per row */
+    mtile_pr = surf->level[level].nblk_x / mtilew;
+    /* macro tile per slice */
+    mtile_ps = (mtile_pr * surf->level[level].nblk_y) / mtileh;
+
+    surf->level[level].offset = offset;
+    surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe * slice_pt;
+    surf->level[level].slice_size = mtile_ps * mtileb * slice_pt;
+
+    surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
+}
+
+static int eg_surface_init_1d(struct radeon_surface_manager *surf_man,
+                              struct radeon_surface *surf,
+                              uint64_t offset, unsigned start_level)
+{
+    uint32_t xalign, yalign, zalign, tilew;
+    unsigned i;
+
+    /* compute alignment */
+    tilew = 8;
+    xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
+    if (surf->flags & RADEON_SURF_SBUFFER) {
+        surf->stencil_offset = 0;
+        surf->stencil_tile_split = 0;
+        xalign = surf_man->hw_info.group_bytes / (tilew * surf->nsamples);
+    }
+    xalign = MAX2(tilew, xalign);
+    yalign = tilew;
+    zalign = 1;
+    if (!start_level) {
+        surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
+    }
+
+    /* build mipmap tree */
+    for (i = start_level; i <= surf->last_level; i++) {
+        surf->level[i].mode = RADEON_SURF_MODE_1D;
+        surf_minify(surf, i, xalign, yalign, zalign, 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_SBUFFER) {
+        surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
+        surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
+    }
+
+    return 0;
+}
+
+static int eg_surface_init_2d(struct radeon_surface_manager *surf_man,
+                              struct radeon_surface *surf,
+                              uint64_t offset, unsigned start_level)
+{
+    unsigned tilew, tileh, tileb;
+    unsigned mtilew, mtileh, mtileb;
+    unsigned slice_pt;
+    unsigned i;
+
+    surf->stencil_offset = 0;
+    /* compute tile values */
+    tilew = 8;
+    tileh = 8;
+    tileb = tilew * tileh * surf->bpe * surf->nsamples;
+    /* slices per tile */
+    slice_pt = 1;
+    if (tileb > surf->tile_split) {
+        slice_pt = tileb / surf->tile_split;
+    }
+    tileb = tileb / slice_pt;
+
+    /* macro tile width & height */
+    mtilew = (tilew * surf->bankw * surf_man->hw_info.num_pipes) * surf->mtilea;
+    mtileh = (tileh * surf->bankh * surf_man->hw_info.num_banks) / surf->mtilea;
+    /* macro tile bytes */
+    mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb;
+
+    /* check if surface is big enought */
+    if (surf->nblk_x < mtilew || surf->nblk_y < mtileh) {
+        surf->level[start_level].mode = RADEON_SURF_MODE_1D;
+        return eg_surface_init_1d(surf_man, surf, offset, start_level);
+    }
+
+    if (!start_level) {
+        surf->bo_alignment = MAX2(256, mtileb);
+    }
+
+    /* build mipmap tree */
+    for (i = start_level; i <= surf->last_level; i++) {
+        surf->level[i].mode = RADEON_SURF_MODE_2D;
+        eg_surf_minify(surf, i, slice_pt, mtilew, mtileh, mtileb, offset);
+        if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
+            return r6_surface_init_1d(surf_man, surf, 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_SBUFFER) {
+        surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
+        surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
+    }
+
+    return 0;
+}
+
+static int eg_surface_sanity(struct radeon_surface_manager *surf_man,
+                             struct radeon_surface *surf,
+                             unsigned mode)
+{
+    unsigned tileb;
+
+    /* 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;
+    }
+
+    /* check tile split */
+    switch (surf->tile_split) {
+    case 0:
+        if (mode == RADEON_SURF_MODE_2D) {
+            return -EINVAL;
+        }
+    case 64:
+    case 128:
+    case 256:
+    case 512:
+    case 1024:
+    case 2048:
+    case 4096:
+        break;
+    default:
+        return -EINVAL;
+    }
+    switch (surf->mtilea) {
+    case 0:
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+        break;
+    default:
+        return -EINVAL;
+    }
+    /* check aspect ratio */
+    if (surf_man->hw_info.num_banks < surf->mtilea) {
+        return -EINVAL;
+    }
+    /* check bank width */
+    switch (surf->bankw) {
+    case 0:
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+        break;
+    default:
+        return -EINVAL;
+    }
+    /* check bank height */
+    switch (surf->bankh) {
+    case 0:
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+        break;
+    default:
+        return -EINVAL;
+    }
+    tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
+    if ((tileb * surf->bankh * surf->bankw) < surf_man->hw_info.group_bytes) {
+        if (mode == RADEON_SURF_MODE_2D) {
+            return -EINVAL;
+        }
+    }
+
+    /* force 1d on kernel that can't do 2d */
+    if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
+        mode = RADEON_SURF_MODE_1D;
+    }
+
+    return 0;
+}
+
+static int eg_surface_init(struct radeon_surface_manager *surf_man,
+                           struct radeon_surface *surf)
+{
+    unsigned mode;
+    int r;
+
+    /* tiling mode */
+    mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
+
+    /* for some reason eg need to have room for stencil right after depth */
+    if (surf->flags & RADEON_SURF_ZBUFFER) {
+        surf->flags |= RADEON_SURF_SBUFFER;
+    }
+
+    r = eg_surface_sanity(surf_man, surf, mode);
+    if (r) {
+        return r;
+    }
+
+    /* check tiling mode */
+    switch (mode) {
+    case RADEON_SURF_MODE_LINEAR:
+        r = r6_surface_init_linear(surf_man, surf, 0, 0);
+        break;
+    case RADEON_SURF_MODE_LINEAR_ALIGNED:
+        r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
+        break;
+    case RADEON_SURF_MODE_1D:
+        r = eg_surface_init_1d(surf_man, surf, 0, 0);
+        break;
+    case RADEON_SURF_MODE_2D:
+        r = eg_surface_init_2d(surf_man, surf, 0, 0);
+        break;
+    default:
+        return -EINVAL;
+    }
+    return r;
+}
+
+static unsigned log2_int(unsigned x)
+{
+    unsigned l;
+
+    if (x < 2) {
+        return 0;
+    }
+    for (l = 2; ; l++) {
+        if ((unsigned)(1 << l) > x) {
+            return l - 1;
+        }
+    }
+    return 0;
+}
+
+/* compute best tile_split, bankw, bankh, mtilea
+ * depending on surface
+ */
+static int eg_surface_best(struct radeon_surface_manager *surf_man,
+                           struct radeon_surface *surf)
+{
+    unsigned mode, tileb, h_over_w;
+    int r;
+
+    /* tiling mode */
+    mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
+
+    /* for some reason eg need to have room for stencil right after depth */
+    if (surf->flags & RADEON_SURF_ZBUFFER) {
+        surf->flags |= RADEON_SURF_SBUFFER;
+    }
+
+    /* set some default value to avoid sanity check choking on them */
+    surf->tile_split = 1024;
+    surf->bankw = 1;
+    surf->bankh = 1;
+    surf->mtilea = surf_man->hw_info.num_banks;
+    tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
+    for (; surf->bankh <= 8; surf->bankh *= 2) {
+        if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
+            break;
+        }
+    }
+    if (surf->mtilea > 8) {
+        surf->mtilea = 8;
+    }
+
+    r = eg_surface_sanity(surf_man, surf, mode);
+    if (r) {
+        return r;
+    }
+
+    if (mode != RADEON_SURF_MODE_2D) {
+        /* nothing to do for non 2D tiled surface */
+        return 0;
+    }
+
+    /* set tile split to row size, optimize latter for multi-sample surface
+     * tile split >= 256 for render buffer surface. Also depth surface want
+     * smaller value for optimal performances.
+     */
+    surf->tile_split = surf_man->hw_info.row_size;
+    surf->stencil_tile_split = surf_man->hw_info.row_size / 2;
+
+    /* bankw or bankh greater than 1 increase alignment requirement, not
+     * sure if it's worth using smaller bankw & bankh to stick with 2D
+     * tiling on small surface rather than falling back to 1D tiling.
+     * Use recommanded value based on tile size for now.
+     *
+     * fmask buffer has different optimal value figure them out once we
+     * use it.
+     */
+    if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
+        /* assume 1 bytes for stencil, we optimize for stencil as stencil
+         * and depth shares surface values
+         */
+        tileb = MIN2(surf->tile_split, 64 * surf->nsamples);
+    } else {
+        tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
+    }
+
+    /* use bankw of 1 to minimize width alignment, might be interesting to
+     * increase it for large surface
+     */
+    surf->bankw = 1;
+    switch (tileb) {
+    case 64:
+        surf->bankh = 4;
+        break;
+    case 128:
+    case 256:
+        surf->bankh = 2;
+        break;
+    default:
+        surf->bankh = 1;
+        break;
+    }
+    /* double check the constraint */
+    for (; surf->bankh <= 8; surf->bankh *= 2) {
+        if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
+            break;
+        }
+    }
+
+    h_over_w = (((surf->bankh * surf_man->hw_info.num_banks) << 16) /
+                (surf->bankw * surf_man->hw_info.num_pipes)) >> 16;
+    surf->mtilea = 1 << (log2_int(h_over_w) >> 1);
+
+    return 0;
+}
+
+
+/* ===========================================================================
+ * public API
+ */
+struct radeon_surface_manager *radeon_surface_manager_new(int fd)
+{
+    struct radeon_surface_manager *surf_man;
+
+    surf_man = calloc(1, sizeof(struct radeon_surface_manager));
+    if (surf_man == NULL) {
+        return NULL;
+    }
+    surf_man->fd = fd;
+    if (radeon_get_value(fd, RADEON_INFO_DEVICE_ID, &surf_man->device_id)) {
+        goto out_err;
+    }
+    if (radeon_get_family(surf_man)) {
+        goto out_err;
+    }
+
+    if (surf_man->family <= CHIP_RV740) {
+        if (r6_init_hw_info(surf_man)) {
+            goto out_err;
+        }
+        surf_man->surface_init = &r6_surface_init;
+        surf_man->surface_best = &r6_surface_best;
+    } else {
+        if (eg_init_hw_info(surf_man)) {
+            goto out_err;
+        }
+        surf_man->surface_init = &eg_surface_init;
+        surf_man->surface_best = &eg_surface_best;
+    }
+
+    return surf_man;
+out_err:
+    free(surf_man);
+    return NULL;
+}
+
+void radeon_surface_manager_free(struct radeon_surface_manager *surf_man)
+{
+    free(surf_man);
+}
+
+static int radeon_surface_sanity(struct radeon_surface_manager *surf_man,
+                                 struct radeon_surface *surf,
+                                 unsigned type,
+                                 unsigned mode)
+{
+    if (surf_man == NULL || surf_man->surface_init == NULL || surf == NULL) {
+        return -EINVAL;
+    }
+
+    /* all dimension must be at least 1 ! */
+    if (!surf->npix_x || !surf->npix_y || !surf->npix_z) {
+        return -EINVAL;
+    }
+    if (!surf->nblk_x || !surf->nblk_y || !surf->nblk_z) {
+        return -EINVAL;
+    }
+    if (surf->npix_x < surf->nblk_x || surf->npix_y < surf->nblk_y || surf->npix_z < surf->nblk_z) {
+        return -EINVAL;
+    }
+    if (!surf->array_size) {
+        return -EINVAL;
+    }
+    /* array size must be a power of 2 */
+    surf->array_size = next_power_of_two(surf->array_size);
+
+    switch (surf->nsamples) {
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+        break;
+    default:
+        return -EINVAL;
+    }
+    /* check type */
+    switch (type) {
+    case RADEON_SURF_TYPE_1D:
+        if (surf->npix_y > 1) {
+            return -EINVAL;
+        }
+    case RADEON_SURF_TYPE_2D:
+        if (surf->npix_z > 1) {
+            return -EINVAL;
+        }
+        break;
+    case RADEON_SURF_TYPE_CUBEMAP:
+        if (surf->npix_z > 1) {
+            return -EINVAL;
+        }
+        /* deal with cubemap as they were texture array */
+        if (surf_man->family >= CHIP_RV770) {
+            surf->array_size = 8;
+        } else {
+            surf->array_size = 6;
+        }
+        break;
+    case RADEON_SURF_TYPE_3D:
+        break;
+    case RADEON_SURF_TYPE_1D_ARRAY:
+        if (surf->npix_y > 1) {
+            return -EINVAL;
+        }
+    case RADEON_SURF_TYPE_2D_ARRAY:
+        break;
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+int radeon_surface_init(struct radeon_surface_manager *surf_man,
+                        struct radeon_surface *surf)
+{
+    unsigned mode, type;
+    int r;
+
+    type = RADEON_SURF_GET(surf->flags, TYPE);
+    mode = RADEON_SURF_GET(surf->flags, MODE);
+
+    r = radeon_surface_sanity(surf_man, surf, type, mode);
+    if (r) {
+        return r;
+    }
+    return surf_man->surface_init(surf_man, surf);
+}
+
+int radeon_surface_best(struct radeon_surface_manager *surf_man,
+                        struct radeon_surface *surf)
+{
+    unsigned mode, type;
+    int r;
+
+    type = RADEON_SURF_GET(surf->flags, TYPE);
+    mode = RADEON_SURF_GET(surf->flags, MODE);
+
+    r = radeon_surface_sanity(surf_man, surf, type, mode);
+    if (r) {
+        return r;
+    }
+    return surf_man->surface_best(surf_man, surf);
+}
diff --git a/radeon/radeon_surface.h b/radeon/radeon_surface.h
new file mode 100644 (file)
index 0000000..3e5fbed
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2011 Red Hat All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
+ * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors:
+ *      Jérôme Glisse <jglisse@redhat.com>
+ */
+#ifndef RADEON_SURFACE_H
+#define RADEON_SURFACE_H
+
+/* Note :
+ *
+ * For texture array, the n layer are stored one after the other within each
+ * mipmap level. 0 value for field than can be hint is always valid.
+ */
+
+#define RADEON_SURF_MAX_LEVEL                   32
+
+#define RADEON_SURF_TYPE_MASK                   0xFF
+#define RADEON_SURF_TYPE_SHIFT                  0
+#define     RADEON_SURF_TYPE_1D                     0
+#define     RADEON_SURF_TYPE_2D                     1
+#define     RADEON_SURF_TYPE_3D                     2
+#define     RADEON_SURF_TYPE_CUBEMAP                3
+#define     RADEON_SURF_TYPE_1D_ARRAY               4
+#define     RADEON_SURF_TYPE_2D_ARRAY               5
+#define RADEON_SURF_MODE_MASK                   0xFF
+#define RADEON_SURF_MODE_SHIFT                  8
+#define     RADEON_SURF_MODE_LINEAR                 0
+#define     RADEON_SURF_MODE_LINEAR_ALIGNED         1
+#define     RADEON_SURF_MODE_1D                     2
+#define     RADEON_SURF_MODE_2D                     3
+#define RADEON_SURF_SCANOUT                     (1 << 16)
+#define RADEON_SURF_ZBUFFER                     (1 << 17)
+#define RADEON_SURF_SBUFFER                     (1 << 18)
+
+#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)
+#define RADEON_SURF_CLR(v, field)   ((v) & ~(RADEON_SURF_ ## field ## _MASK << RADEON_SURF_ ## field ## _SHIFT))
+
+/* first field up to mode need to match r6 struct so that we can reuse
+ * same function for linear & linear aligned
+ */
+struct radeon_surface_level {
+    uint64_t                    offset;
+    uint64_t                    slice_size;
+    uint32_t                    npix_x;
+    uint32_t                    npix_y;
+    uint32_t                    npix_z;
+    uint32_t                    nblk_x;
+    uint32_t                    nblk_y;
+    uint32_t                    nblk_z;
+    uint32_t                    pitch_bytes;
+    uint32_t                    mode;
+};
+
+struct radeon_surface {
+    uint32_t                    npix_x;
+    uint32_t                    npix_y;
+    uint32_t                    npix_z;
+    uint32_t                    nblk_x;
+    uint32_t                    nblk_y;
+    uint32_t                    nblk_z;
+    uint32_t                    array_size;
+    uint32_t                    last_level;
+    uint32_t                    bpe;
+    uint32_t                    nsamples;
+    uint32_t                    flags;
+    /* Following is updated/fill by the allocator. It's allowed to
+     * set some of the value but they are use as hint and can be
+     * overridden (things lile bankw/bankh on evergreen for
+     * instance).
+     */
+    uint64_t                    bo_size;
+    uint64_t                    bo_alignment;
+    /* apply to eg */
+    uint32_t                    bankw;
+    uint32_t                    bankh;
+    uint32_t                    mtilea;
+    uint32_t                    tile_split;
+    uint32_t                    stencil_tile_split;
+    uint64_t                    stencil_offset;
+    struct radeon_surface_level level[RADEON_SURF_MAX_LEVEL];
+};
+
+struct radeon_surface_manager *radeon_surface_manager_new(int fd);
+void radeon_surface_manager_free(struct radeon_surface_manager *surf_man);
+int radeon_surface_init(struct radeon_surface_manager *surf_man,
+                        struct radeon_surface *surf);
+int radeon_surface_best(struct radeon_surface_manager *surf_man,
+                        struct radeon_surface *surf);
+
+#endif