radeon: force 1D array mode for z/stencil surface
[glsdk/libdrm.git] / radeon / radeon_surface.c
1 /*
2  * Copyright © 2011 Red Hat All Rights Reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sub license, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
14  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15  * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
16  * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
19  * USE OR OTHER DEALINGS IN THE SOFTWARE.
20  *
21  * The above copyright notice and this permission notice (including the
22  * next paragraph) shall be included in all copies or substantial portions
23  * of the Software.
24  */
25 /*
26  * Authors:
27  *      Jérôme Glisse <jglisse@redhat.com>
28  */
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/mman.h>
34 #include <sys/ioctl.h>
35 #include "drm.h"
36 #include "xf86drm.h"
37 #include "radeon_drm.h"
38 #include "radeon_surface.h"
40 #define ALIGN(value, alignment) (((value) + alignment - 1) & ~(alignment - 1))
41 #define MAX2(A, B)              ((A) > (B) ? (A) : (B))
42 #define MIN2(A, B)              ((A) < (B) ? (A) : (B))
44 /* keep this private */
45 enum radeon_family {
46     CHIP_UNKNOWN,
47     CHIP_R600,
48     CHIP_RV610,
49     CHIP_RV630,
50     CHIP_RV670,
51     CHIP_RV620,
52     CHIP_RV635,
53     CHIP_RS780,
54     CHIP_RS880,
55     CHIP_RV770,
56     CHIP_RV730,
57     CHIP_RV710,
58     CHIP_RV740,
59     CHIP_CEDAR,
60     CHIP_REDWOOD,
61     CHIP_JUNIPER,
62     CHIP_CYPRESS,
63     CHIP_HEMLOCK,
64     CHIP_PALM,
65     CHIP_SUMO,
66     CHIP_SUMO2,
67     CHIP_BARTS,
68     CHIP_TURKS,
69     CHIP_CAICOS,
70     CHIP_CAYMAN,
71     CHIP_ARUBA,
72     CHIP_TAHITI,
73     CHIP_PITCAIRN,
74     CHIP_VERDE,
75     CHIP_LAST,
76 };
78 typedef int (*hw_init_surface_t)(struct radeon_surface_manager *surf_man,
79                                  struct radeon_surface *surf);
80 typedef int (*hw_best_surface_t)(struct radeon_surface_manager *surf_man,
81                                  struct radeon_surface *surf);
83 struct radeon_hw_info {
84     /* apply to r6, eg */
85     uint32_t                    group_bytes;
86     uint32_t                    num_banks;
87     uint32_t                    num_pipes;
88     /* apply to eg */
89     uint32_t                    row_size;
90     unsigned                    allow_2d;
91 };
93 struct radeon_surface_manager {
94     int                         fd;
95     uint32_t                    device_id;
96     struct radeon_hw_info       hw_info;
97     unsigned                    family;
98     hw_init_surface_t           surface_init;
99     hw_best_surface_t           surface_best;
100 };
102 /* helper */
103 static int radeon_get_value(int fd, unsigned req, uint32_t *value)
105     struct drm_radeon_info info = {};
106     int r;
108     *value = 0;
109     info.request = req;
110     info.value = (uintptr_t)value;
111     r = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info,
112                             sizeof(struct drm_radeon_info));
113     return r;
116 static int radeon_get_family(struct radeon_surface_manager *surf_man)
118     switch (surf_man->device_id) {
119 #define CHIPSET(pci_id, name, fam) case pci_id: surf_man->family = CHIP_##fam; break;
120 #include "r600_pci_ids.h"
121 #undef CHIPSET
122     default:
123         return -EINVAL;
124     }
125     return 0;
128 static unsigned next_power_of_two(unsigned x)
130    if (x <= 1)
131        return 1;
133    return (1 << ((sizeof(unsigned) * 8) - __builtin_clz(x - 1)));
136 static unsigned mip_minify(unsigned size, unsigned level)
138     unsigned val;
140     val = MAX2(1, size >> level);
141     if (level > 0)
142         val = next_power_of_two(val);
143     return val;
146 static void surf_minify(struct radeon_surface *surf,
147                         unsigned level,
148                         uint32_t xalign, uint32_t yalign, uint32_t zalign,
149                         unsigned offset)
151     surf->level[level].npix_x = mip_minify(surf->npix_x, level);
152     surf->level[level].npix_y = mip_minify(surf->npix_y, level);
153     surf->level[level].npix_z = mip_minify(surf->npix_z, level);
154     surf->level[level].nblk_x = (surf->level[level].npix_x + surf->blk_w - 1) / surf->blk_w;
155     surf->level[level].nblk_y = (surf->level[level].npix_y + surf->blk_h - 1) / surf->blk_h;
156     surf->level[level].nblk_z = (surf->level[level].npix_z + surf->blk_d - 1) / surf->blk_d;
157     if (surf->level[level].mode == RADEON_SURF_MODE_2D) {
158         if (surf->level[level].nblk_x < xalign || surf->level[level].nblk_y < yalign) {
159             surf->level[level].mode = RADEON_SURF_MODE_1D;
160             return;
161         }
162     }
163     surf->level[level].nblk_x  = ALIGN(surf->level[level].nblk_x, xalign);
164     surf->level[level].nblk_y  = ALIGN(surf->level[level].nblk_y, yalign);
165     surf->level[level].nblk_z  = ALIGN(surf->level[level].nblk_z, zalign);
167     surf->level[level].offset = offset;
168     surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe;
169     surf->level[level].slice_size = surf->level[level].pitch_bytes * surf->level[level].nblk_y;
171     surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
174 /* ===========================================================================
175  * r600/r700 family
176  */
177 static int r6_init_hw_info(struct radeon_surface_manager *surf_man)
179     uint32_t tiling_config;
180     drmVersionPtr version;
181     int r;
183     r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
184                          &tiling_config);
185     if (r) {
186         return r;
187     }
189     surf_man->hw_info.allow_2d = 0;
190     version = drmGetVersion(surf_man->fd);
191     if (version && version->version_minor >= 14) {
192         surf_man->hw_info.allow_2d = 1;
193     }
195     switch ((tiling_config & 0xe) >> 1) {
196     case 0:
197         surf_man->hw_info.num_pipes = 1;
198         break;
199     case 1:
200         surf_man->hw_info.num_pipes = 2;
201         break;
202     case 2:
203         surf_man->hw_info.num_pipes = 4;
204         break;
205     case 3:
206         surf_man->hw_info.num_pipes = 8;
207         break;
208     default:
209         surf_man->hw_info.num_pipes = 8;
210         surf_man->hw_info.allow_2d = 0;
211         break;
212     }
214     switch ((tiling_config & 0x30) >> 4) {
215     case 0:
216         surf_man->hw_info.num_banks = 4;
217         break;
218     case 1:
219         surf_man->hw_info.num_banks = 8;
220         break;
221     default:
222         surf_man->hw_info.num_banks = 8;
223         surf_man->hw_info.allow_2d = 0;
224         break;
225     }
227     switch ((tiling_config & 0xc0) >> 6) {
228     case 0:
229         surf_man->hw_info.group_bytes = 256;
230         break;
231     case 1:
232         surf_man->hw_info.group_bytes = 512;
233         break;
234     default:
235         surf_man->hw_info.group_bytes = 256;
236         surf_man->hw_info.allow_2d = 0;
237         break;
238     }
239     return 0;
242 static int r6_surface_init_linear(struct radeon_surface_manager *surf_man,
243                                   struct radeon_surface *surf,
244                                   uint64_t offset, unsigned start_level)
246     uint32_t xalign, yalign, zalign;
247     unsigned i;
249     /* compute alignment */
250     if (!start_level) {
251         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
252     }
253     /* the 32 alignment is for scanout, cb or db but to allow texture to be
254      * easily bound as such we force this alignment to all surface
255      */
256     xalign = MAX2(1, surf_man->hw_info.group_bytes / surf->bpe);
257     yalign = 1;
258     zalign = 1;
259     if (surf->flags & RADEON_SURF_SCANOUT) {
260         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
261     }
263     /* build mipmap tree */
264     for (i = start_level; i <= surf->last_level; i++) {
265         surf->level[i].mode = RADEON_SURF_MODE_LINEAR;
266         surf_minify(surf, i, xalign, yalign, zalign, offset);
267         /* level0 and first mipmap need to have alignment */
268         offset = surf->bo_size;
269         if ((i == 0)) {
270             offset = ALIGN(offset, surf->bo_alignment);
271         }
272     }
273     return 0;
276 static int r6_surface_init_linear_aligned(struct radeon_surface_manager *surf_man,
277                                           struct radeon_surface *surf,
278                                           uint64_t offset, unsigned start_level)
280     uint32_t xalign, yalign, zalign;
281     unsigned i;
283     /* compute alignment */
284     if (!start_level) {
285         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
286     }
287     xalign = MAX2(64, surf_man->hw_info.group_bytes / surf->bpe);
288     yalign = 1;
289     zalign = 1;
291     /* build mipmap tree */
292     for (i = start_level; i <= surf->last_level; i++) {
293         surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
294         surf_minify(surf, i, xalign, yalign, zalign, offset);
295         /* level0 and first mipmap need to have alignment */
296         offset = surf->bo_size;
297         if ((i == 0)) {
298             offset = ALIGN(offset, surf->bo_alignment);
299         }
300     }
301     return 0;
304 static int r6_surface_init_1d(struct radeon_surface_manager *surf_man,
305                               struct radeon_surface *surf,
306                               uint64_t offset, unsigned start_level)
308     uint32_t xalign, yalign, zalign, tilew;
309     unsigned i;
311     /* compute alignment */
312     tilew = 8;
313     xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
314     xalign = MAX2(tilew, xalign);
315     yalign = tilew;
316     zalign = 1;
317     if (surf->flags & RADEON_SURF_SCANOUT) {
318         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
319     }
320     if (!start_level) {
321         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
322     }
324     /* build mipmap tree */
325     for (i = start_level; i <= surf->last_level; i++) {
326         surf->level[i].mode = RADEON_SURF_MODE_1D;
327         surf_minify(surf, i, xalign, yalign, zalign, offset);
328         /* level0 and first mipmap need to have alignment */
329         offset = surf->bo_size;
330         if ((i == 0)) {
331             offset = ALIGN(offset, surf->bo_alignment);
332         }
333     }
334     return 0;
337 static int r6_surface_init_2d(struct radeon_surface_manager *surf_man,
338                               struct radeon_surface *surf,
339                               uint64_t offset, unsigned start_level)
341     uint32_t xalign, yalign, zalign, tilew;
342     unsigned i;
344     /* compute alignment */
345     tilew = 8;
346     zalign = 1;
347     xalign = (surf_man->hw_info.group_bytes * surf_man->hw_info.num_banks) /
348              (tilew * surf->bpe * surf->nsamples);
349     xalign = MAX2(tilew * surf_man->hw_info.num_banks, xalign);
350     yalign = tilew * surf_man->hw_info.num_pipes;
351     if (surf->flags & RADEON_SURF_SCANOUT) {
352         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
353     }
354     if (!start_level) {
355         surf->bo_alignment =
356             MAX2(surf_man->hw_info.num_pipes *
357                  surf_man->hw_info.num_banks *
358                  surf->bpe * 64,
359                  xalign * yalign * surf->nsamples * surf->bpe);
360     }
362     /* build mipmap tree */
363     for (i = start_level; i <= surf->last_level; i++) {
364         surf->level[i].mode = RADEON_SURF_MODE_2D;
365         surf_minify(surf, i, xalign, yalign, zalign, offset);
366         if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
367             return r6_surface_init_1d(surf_man, surf, offset, i);
368         }
369         /* level0 and first mipmap need to have alignment */
370         offset = surf->bo_size;
371         if ((i == 0)) {
372             offset = ALIGN(offset, surf->bo_alignment);
373         }
374     }
375     return 0;
378 static int r6_surface_init(struct radeon_surface_manager *surf_man,
379                            struct radeon_surface *surf)
381     unsigned mode;
382     int r;
384     /* tiling mode */
385     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
387     /* always enable z & stencil together */
388     if (surf->flags & RADEON_SURF_ZBUFFER) {
389         surf->flags |= RADEON_SURF_SBUFFER;
390     }
391     if (surf->flags & RADEON_SURF_SBUFFER) {
392         surf->flags |= RADEON_SURF_ZBUFFER;
393     }
394     if (surf->flags & RADEON_SURF_ZBUFFER) {
395         /* zbuffer only support 1D or 2D tiled surface */
396         switch (mode) {
397         case RADEON_SURF_MODE_1D:
398         case RADEON_SURF_MODE_2D:
399             break;
400         default:
401             mode = RADEON_SURF_MODE_1D;
402             surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
403             surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
404             break;
405         }
406     }
408     /* force 1d on kernel that can't do 2d */
409     if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
410         mode = RADEON_SURF_MODE_1D;
411         surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
412         surf->flags |= RADEON_SURF_SET(mode, MODE);
413     }
415     /* check surface dimension */
416     if (surf->npix_x > 8192 || surf->npix_y > 8192 || surf->npix_z > 8192) {
417         return -EINVAL;
418     }
420     /* check mipmap last_level */
421     if (surf->last_level > 14) {
422         return -EINVAL;
423     }
425     /* check tiling mode */
426     switch (mode) {
427     case RADEON_SURF_MODE_LINEAR:
428         r = r6_surface_init_linear(surf_man, surf, 0, 0);
429         break;
430     case RADEON_SURF_MODE_LINEAR_ALIGNED:
431         r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
432         break;
433     case RADEON_SURF_MODE_1D:
434         r = r6_surface_init_1d(surf_man, surf, 0, 0);
435         break;
436     case RADEON_SURF_MODE_2D:
437         r = r6_surface_init_2d(surf_man, surf, 0, 0);
438         break;
439     default:
440         return -EINVAL;
441     }
442     return r;
445 static int r6_surface_best(struct radeon_surface_manager *surf_man,
446                            struct radeon_surface *surf)
448     /* no value to optimize for r6xx/r7xx */
449     return 0;
453 /* ===========================================================================
454  * evergreen family
455  */
456 static int eg_init_hw_info(struct radeon_surface_manager *surf_man)
458     uint32_t tiling_config;
459     drmVersionPtr version;
460     int r;
462     r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
463                          &tiling_config);
464     if (r) {
465         return r;
466     }
468     surf_man->hw_info.allow_2d = 0;
469     version = drmGetVersion(surf_man->fd);
470     if (version && version->version_minor >= 16) {
471         surf_man->hw_info.allow_2d = 1;
472     }
474     switch (tiling_config & 0xf) {
475     case 0:
476         surf_man->hw_info.num_pipes = 1;
477         break;
478     case 1:
479         surf_man->hw_info.num_pipes = 2;
480         break;
481     case 2:
482         surf_man->hw_info.num_pipes = 4;
483         break;
484     case 3:
485         surf_man->hw_info.num_pipes = 8;
486         break;
487     default:
488         surf_man->hw_info.num_pipes = 8;
489         surf_man->hw_info.allow_2d = 0;
490         break;
491     }
493     switch ((tiling_config & 0xf0) >> 4) {
494     case 0:
495         surf_man->hw_info.num_banks = 4;
496         break;
497     case 1:
498         surf_man->hw_info.num_banks = 8;
499         break;
500     case 2:
501         surf_man->hw_info.num_banks = 16;
502         break;
503     default:
504         surf_man->hw_info.num_banks = 8;
505         surf_man->hw_info.allow_2d = 0;
506         break;
507     }
509     switch ((tiling_config & 0xf00) >> 8) {
510     case 0:
511         surf_man->hw_info.group_bytes = 256;
512         break;
513     case 1:
514         surf_man->hw_info.group_bytes = 512;
515         break;
516     default:
517         surf_man->hw_info.group_bytes = 256;
518         surf_man->hw_info.allow_2d = 0;
519         break;
520     }
522     switch ((tiling_config & 0xf000) >> 12) {
523     case 0:
524         surf_man->hw_info.row_size = 1024;
525         break;
526     case 1:
527         surf_man->hw_info.row_size = 2048;
528         break;
529     case 2:
530         surf_man->hw_info.row_size = 4096;
531         break;
532     default:
533         surf_man->hw_info.row_size = 4096;
534         surf_man->hw_info.allow_2d = 0;
535         break;
536     }
537     return 0;
540 static void eg_surf_minify(struct radeon_surface *surf,
541                            unsigned level,
542                            unsigned slice_pt,
543                            unsigned mtilew,
544                            unsigned mtileh,
545                            unsigned mtileb,
546                            unsigned offset)
548     unsigned mtile_pr, mtile_ps;
550     surf->level[level].npix_x = mip_minify(surf->npix_x, level);
551     surf->level[level].npix_y = mip_minify(surf->npix_y, level);
552     surf->level[level].npix_z = mip_minify(surf->npix_z, level);
553     surf->level[level].nblk_x = (surf->level[level].npix_x + surf->blk_w - 1) / surf->blk_w;
554     surf->level[level].nblk_y = (surf->level[level].npix_y + surf->blk_h - 1) / surf->blk_h;
555     surf->level[level].nblk_z = (surf->level[level].npix_z + surf->blk_d - 1) / surf->blk_d;
556     if (surf->level[level].mode == RADEON_SURF_MODE_2D) {
557         if (surf->level[level].nblk_x < mtilew || surf->level[level].nblk_y < mtileh) {
558             surf->level[level].mode = RADEON_SURF_MODE_1D;
559             return;
560         }
561     }
562     surf->level[level].nblk_x  = ALIGN(surf->level[level].nblk_x, mtilew);
563     surf->level[level].nblk_y  = ALIGN(surf->level[level].nblk_y, mtileh);
564     surf->level[level].nblk_z  = ALIGN(surf->level[level].nblk_z, 1);
566     /* macro tile per row */
567     mtile_pr = surf->level[level].nblk_x / mtilew;
568     /* macro tile per slice */
569     mtile_ps = (mtile_pr * surf->level[level].nblk_y) / mtileh;
571     surf->level[level].offset = offset;
572     surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe * slice_pt;
573     surf->level[level].slice_size = mtile_ps * mtileb * slice_pt;
575     surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
578 static int eg_surface_init_1d(struct radeon_surface_manager *surf_man,
579                               struct radeon_surface *surf,
580                               uint64_t offset, unsigned start_level)
582     uint32_t xalign, yalign, zalign, tilew;
583     unsigned i;
585     /* compute alignment */
586     tilew = 8;
587     xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
588     if (surf->flags & RADEON_SURF_SBUFFER) {
589         xalign = surf_man->hw_info.group_bytes / (tilew * surf->nsamples);
590     }
591     xalign = MAX2(tilew, xalign);
592     yalign = tilew;
593     zalign = 1;
594     if (surf->flags & RADEON_SURF_SCANOUT) {
595         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
596     }
597     if (!start_level) {
598         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
599     }
601     /* build mipmap tree */
602     for (i = start_level; i <= surf->last_level; i++) {
603         surf->level[i].mode = RADEON_SURF_MODE_1D;
604         surf_minify(surf, i, xalign, yalign, zalign, offset);
605         /* level0 and first mipmap need to have alignment */
606         offset = surf->bo_size;
607         if ((i == 0)) {
608             offset = ALIGN(offset, surf->bo_alignment);
609         }
610     }
612     if (surf->flags & RADEON_SURF_SBUFFER) {
613         surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
614         surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
615     }
617     return 0;
620 static int eg_surface_init_2d(struct radeon_surface_manager *surf_man,
621                               struct radeon_surface *surf,
622                               uint64_t offset, unsigned start_level)
624     unsigned tilew, tileh, tileb;
625     unsigned mtilew, mtileh, mtileb;
626     unsigned slice_pt;
627     unsigned i;
629     /* compute tile values */
630     tilew = 8;
631     tileh = 8;
632     tileb = tilew * tileh * surf->bpe * surf->nsamples;
633     /* slices per tile */
634     slice_pt = 1;
635     if (tileb > surf->tile_split) {
636         slice_pt = tileb / surf->tile_split;
637     }
638     tileb = tileb / slice_pt;
640     /* macro tile width & height */
641     mtilew = (tilew * surf->bankw * surf_man->hw_info.num_pipes) * surf->mtilea;
642     mtileh = (tileh * surf->bankh * surf_man->hw_info.num_banks) / surf->mtilea;
643     /* macro tile bytes */
644     mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb;
646     if (!start_level) {
647         surf->bo_alignment = MAX2(256, mtileb);
648     }
650     /* build mipmap tree */
651     for (i = start_level; i <= surf->last_level; i++) {
652         surf->level[i].mode = RADEON_SURF_MODE_2D;
653         eg_surf_minify(surf, i, slice_pt, mtilew, mtileh, mtileb, offset);
654         if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
655             return eg_surface_init_1d(surf_man, surf, offset, i);
656         }
657         /* level0 and first mipmap need to have alignment */
658         offset = surf->bo_size;
659         if ((i == 0)) {
660             offset = ALIGN(offset, surf->bo_alignment);
661         }
662     }
664     if (surf->flags & RADEON_SURF_SBUFFER) {
665         surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
666         surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
667     }
669     return 0;
672 static int eg_surface_sanity(struct radeon_surface_manager *surf_man,
673                              struct radeon_surface *surf,
674                              unsigned mode)
676     unsigned tileb;
678     /* check surface dimension */
679     if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) {
680         return -EINVAL;
681     }
683     /* check mipmap last_level */
684     if (surf->last_level > 15) {
685         return -EINVAL;
686     }
688     /* force 1d on kernel that can't do 2d */
689     if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
690         mode = RADEON_SURF_MODE_1D;
691         surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
692         surf->flags |= RADEON_SURF_SET(mode, MODE);
693     }
695     /* check tile split */
696     if (mode == RADEON_SURF_MODE_2D) {
697         switch (surf->tile_split) {
698         case 64:
699         case 128:
700         case 256:
701         case 512:
702         case 1024:
703         case 2048:
704         case 4096:
705             break;
706         default:
707             return -EINVAL;
708         }
709         switch (surf->mtilea) {
710         case 1:
711         case 2:
712         case 4:
713         case 8:
714             break;
715         default:
716             return -EINVAL;
717         }
718         /* check aspect ratio */
719         if (surf_man->hw_info.num_banks < surf->mtilea) {
720             return -EINVAL;
721         }
722         /* check bank width */
723         switch (surf->bankw) {
724         case 1:
725         case 2:
726         case 4:
727         case 8:
728             break;
729         default:
730             return -EINVAL;
731         }
732         /* check bank height */
733         switch (surf->bankh) {
734         case 1:
735         case 2:
736         case 4:
737         case 8:
738             break;
739         default:
740             return -EINVAL;
741         }
742         tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
743         if ((tileb * surf->bankh * surf->bankw) < surf_man->hw_info.group_bytes) {
744             return -EINVAL;
745         }
746     }
748     return 0;
751 static int eg_surface_init(struct radeon_surface_manager *surf_man,
752                            struct radeon_surface *surf)
754     unsigned mode;
755     int r;
757     /* tiling mode */
758     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
760     /* for some reason eg need to have room for stencil right after depth */
761     if (surf->flags & RADEON_SURF_ZBUFFER) {
762         surf->flags |= RADEON_SURF_SBUFFER;
763     }
764     if (surf->flags & RADEON_SURF_SBUFFER) {
765         surf->flags |= RADEON_SURF_ZBUFFER;
766     }
767     if (surf->flags & RADEON_SURF_ZBUFFER) {
768         /* zbuffer only support 1D or 2D tiled surface */
769         switch (mode) {
770         case RADEON_SURF_MODE_1D:
771         case RADEON_SURF_MODE_2D:
772             break;
773         default:
774             mode = RADEON_SURF_MODE_1D;
775             surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
776             surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
777             break;
778         }
779     }
781     r = eg_surface_sanity(surf_man, surf, mode);
782     if (r) {
783         return r;
784     }
786     surf->stencil_offset = 0;
787     surf->stencil_tile_split = 0;
789     /* check tiling mode */
790     switch (mode) {
791     case RADEON_SURF_MODE_LINEAR:
792         r = r6_surface_init_linear(surf_man, surf, 0, 0);
793         break;
794     case RADEON_SURF_MODE_LINEAR_ALIGNED:
795         r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
796         break;
797     case RADEON_SURF_MODE_1D:
798         r = eg_surface_init_1d(surf_man, surf, 0, 0);
799         break;
800     case RADEON_SURF_MODE_2D:
801         r = eg_surface_init_2d(surf_man, surf, 0, 0);
802         break;
803     default:
804         return -EINVAL;
805     }
806     return r;
809 static unsigned log2_int(unsigned x)
811     unsigned l;
813     if (x < 2) {
814         return 0;
815     }
816     for (l = 2; ; l++) {
817         if ((unsigned)(1 << l) > x) {
818             return l - 1;
819         }
820     }
821     return 0;
824 /* compute best tile_split, bankw, bankh, mtilea
825  * depending on surface
826  */
827 static int eg_surface_best(struct radeon_surface_manager *surf_man,
828                            struct radeon_surface *surf)
830     unsigned mode, tileb, h_over_w;
831     int r;
833     /* tiling mode */
834     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
836     /* for some reason eg need to have room for stencil right after depth */
837     if (surf->flags & RADEON_SURF_ZBUFFER) {
838         surf->flags |= RADEON_SURF_SBUFFER;
839     }
841     /* set some default value to avoid sanity check choking on them */
842     surf->tile_split = 1024;
843     surf->bankw = 1;
844     surf->bankh = 1;
845     surf->mtilea = surf_man->hw_info.num_banks;
846     tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
847     for (; surf->bankh <= 8; surf->bankh *= 2) {
848         if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
849             break;
850         }
851     }
852     if (surf->mtilea > 8) {
853         surf->mtilea = 8;
854     }
856     r = eg_surface_sanity(surf_man, surf, mode);
857     if (r) {
858         return r;
859     }
861     if (mode != RADEON_SURF_MODE_2D) {
862         /* nothing to do for non 2D tiled surface */
863         return 0;
864     }
866     /* set tile split to row size, optimize latter for multi-sample surface
867      * tile split >= 256 for render buffer surface. Also depth surface want
868      * smaller value for optimal performances.
869      */
870     surf->tile_split = surf_man->hw_info.row_size;
871     surf->stencil_tile_split = surf_man->hw_info.row_size / 2;
873     /* bankw or bankh greater than 1 increase alignment requirement, not
874      * sure if it's worth using smaller bankw & bankh to stick with 2D
875      * tiling on small surface rather than falling back to 1D tiling.
876      * Use recommanded value based on tile size for now.
877      *
878      * fmask buffer has different optimal value figure them out once we
879      * use it.
880      */
881     if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
882         /* assume 1 bytes for stencil, we optimize for stencil as stencil
883          * and depth shares surface values
884          */
885         tileb = MIN2(surf->tile_split, 64 * surf->nsamples);
886     } else {
887         tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
888     }
890     /* use bankw of 1 to minimize width alignment, might be interesting to
891      * increase it for large surface
892      */
893     surf->bankw = 1;
894     switch (tileb) {
895     case 64:
896         surf->bankh = 4;
897         break;
898     case 128:
899     case 256:
900         surf->bankh = 2;
901         break;
902     default:
903         surf->bankh = 1;
904         break;
905     }
906     /* double check the constraint */
907     for (; surf->bankh <= 8; surf->bankh *= 2) {
908         if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
909             break;
910         }
911     }
913     h_over_w = (((surf->bankh * surf_man->hw_info.num_banks) << 16) /
914                 (surf->bankw * surf_man->hw_info.num_pipes)) >> 16;
915     surf->mtilea = 1 << (log2_int(h_over_w) >> 1);
917     return 0;
921 /* ===========================================================================
922  * public API
923  */
924 struct radeon_surface_manager *radeon_surface_manager_new(int fd)
926     struct radeon_surface_manager *surf_man;
928     surf_man = calloc(1, sizeof(struct radeon_surface_manager));
929     if (surf_man == NULL) {
930         return NULL;
931     }
932     surf_man->fd = fd;
933     if (radeon_get_value(fd, RADEON_INFO_DEVICE_ID, &surf_man->device_id)) {
934         goto out_err;
935     }
936     if (radeon_get_family(surf_man)) {
937         goto out_err;
938     }
940     if (surf_man->family <= CHIP_RV740) {
941         if (r6_init_hw_info(surf_man)) {
942             goto out_err;
943         }
944         surf_man->surface_init = &r6_surface_init;
945         surf_man->surface_best = &r6_surface_best;
946     } else {
947         if (eg_init_hw_info(surf_man)) {
948             goto out_err;
949         }
950         surf_man->surface_init = &eg_surface_init;
951         surf_man->surface_best = &eg_surface_best;
952     }
954     return surf_man;
955 out_err:
956     free(surf_man);
957     return NULL;
960 void radeon_surface_manager_free(struct radeon_surface_manager *surf_man)
962     free(surf_man);
965 static int radeon_surface_sanity(struct radeon_surface_manager *surf_man,
966                                  struct radeon_surface *surf,
967                                  unsigned type,
968                                  unsigned mode)
970     if (surf_man == NULL || surf_man->surface_init == NULL || surf == NULL) {
971         return -EINVAL;
972     }
974     /* all dimension must be at least 1 ! */
975     if (!surf->npix_x || !surf->npix_y || !surf->npix_z) {
976         return -EINVAL;
977     }
978     if (!surf->blk_w || !surf->blk_h || !surf->blk_d) {
979         return -EINVAL;
980     }
981     if (!surf->array_size) {
982         return -EINVAL;
983     }
984     /* array size must be a power of 2 */
985     surf->array_size = next_power_of_two(surf->array_size);
987     switch (surf->nsamples) {
988     case 1:
989     case 2:
990     case 4:
991     case 8:
992         break;
993     default:
994         return -EINVAL;
995     }
996     /* check type */
997     switch (type) {
998     case RADEON_SURF_TYPE_1D:
999         if (surf->npix_y > 1) {
1000             return -EINVAL;
1001         }
1002     case RADEON_SURF_TYPE_2D:
1003         if (surf->npix_z > 1) {
1004             return -EINVAL;
1005         }
1006         break;
1007     case RADEON_SURF_TYPE_CUBEMAP:
1008         if (surf->npix_z > 1) {
1009             return -EINVAL;
1010         }
1011         /* deal with cubemap as they were texture array */
1012         if (surf_man->family >= CHIP_RV770) {
1013             surf->array_size = 8;
1014         } else {
1015             surf->array_size = 6;
1016         }
1017         break;
1018     case RADEON_SURF_TYPE_3D:
1019         break;
1020     case RADEON_SURF_TYPE_1D_ARRAY:
1021         if (surf->npix_y > 1) {
1022             return -EINVAL;
1023         }
1024     case RADEON_SURF_TYPE_2D_ARRAY:
1025         break;
1026     default:
1027         return -EINVAL;
1028     }
1029     return 0;
1032 int radeon_surface_init(struct radeon_surface_manager *surf_man,
1033                         struct radeon_surface *surf)
1035     unsigned mode, type;
1036     int r;
1038     type = RADEON_SURF_GET(surf->flags, TYPE);
1039     mode = RADEON_SURF_GET(surf->flags, MODE);
1041     r = radeon_surface_sanity(surf_man, surf, type, mode);
1042     if (r) {
1043         return r;
1044     }
1045     return surf_man->surface_init(surf_man, surf);
1048 int radeon_surface_best(struct radeon_surface_manager *surf_man,
1049                         struct radeon_surface *surf)
1051     unsigned mode, type;
1052     int r;
1054     type = RADEON_SURF_GET(surf->flags, TYPE);
1055     mode = RADEON_SURF_GET(surf->flags, MODE);
1057     r = radeon_surface_sanity(surf_man, surf, type, mode);
1058     if (r) {
1059         return r;
1060     }
1061     return surf_man->surface_best(surf_man, surf);