]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/libdrm.git/blob - radeon/radeon_surface.c
radeon: add TN surface support
[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_LAST,
73 };
75 typedef int (*hw_init_surface_t)(struct radeon_surface_manager *surf_man,
76                                  struct radeon_surface *surf);
77 typedef int (*hw_best_surface_t)(struct radeon_surface_manager *surf_man,
78                                  struct radeon_surface *surf);
80 struct radeon_hw_info {
81     /* apply to r6, eg */
82     uint32_t                    group_bytes;
83     uint32_t                    num_banks;
84     uint32_t                    num_pipes;
85     /* apply to eg */
86     uint32_t                    row_size;
87     unsigned                    allow_2d;
88 };
90 struct radeon_surface_manager {
91     int                         fd;
92     uint32_t                    device_id;
93     struct radeon_hw_info       hw_info;
94     unsigned                    family;
95     hw_init_surface_t           surface_init;
96     hw_best_surface_t           surface_best;
97 };
99 /* helper */
100 static int radeon_get_value(int fd, unsigned req, uint32_t *value)
102     struct drm_radeon_info info = {};
103     int r;
105     *value = 0;
106     info.request = req;
107     info.value = (uintptr_t)value;
108     r = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info,
109                             sizeof(struct drm_radeon_info));
110     return r;
113 static int radeon_get_family(struct radeon_surface_manager *surf_man)
115     switch (surf_man->device_id) {
116 #define CHIPSET(pci_id, name, fam) case pci_id: surf_man->family = CHIP_##fam; break;
117 #include "r600_pci_ids.h"
118 #undef CHIPSET
119     default:
120         return -EINVAL;
121     }
122     return 0;
125 static unsigned next_power_of_two(unsigned x)
127    if (x <= 1)
128        return 1;
130    return (1 << ((sizeof(unsigned) * 8) - __builtin_clz(x - 1)));
133 static unsigned mip_minify(unsigned size, unsigned level)
135     unsigned val;
137     val = MAX2(1, size >> level);
138     if (level > 0)
139         val = next_power_of_two(val);
140     return val;
143 static void surf_minify(struct radeon_surface *surf,
144                         unsigned level,
145                         uint32_t xalign, uint32_t yalign, uint32_t zalign,
146                         unsigned offset)
148     surf->level[level].npix_x = mip_minify(surf->npix_x, level);
149     surf->level[level].npix_y = mip_minify(surf->npix_y, level);
150     surf->level[level].npix_z = mip_minify(surf->npix_z, level);
151     surf->level[level].nblk_x = (surf->level[level].npix_x + surf->blk_w - 1) / surf->blk_w;
152     surf->level[level].nblk_y = (surf->level[level].npix_y + surf->blk_h - 1) / surf->blk_h;
153     surf->level[level].nblk_z = (surf->level[level].npix_z + surf->blk_d - 1) / surf->blk_d;
154     if (surf->level[level].mode == RADEON_SURF_MODE_2D) {
155         if (surf->level[level].nblk_x < xalign || surf->level[level].nblk_y < yalign) {
156             surf->level[level].mode = RADEON_SURF_MODE_1D;
157             return;
158         }
159     }
160     surf->level[level].nblk_x  = ALIGN(surf->level[level].nblk_x, xalign);
161     surf->level[level].nblk_y  = ALIGN(surf->level[level].nblk_y, yalign);
162     surf->level[level].nblk_z  = ALIGN(surf->level[level].nblk_z, zalign);
164     surf->level[level].offset = offset;
165     surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe;
166     surf->level[level].slice_size = surf->level[level].pitch_bytes * surf->level[level].nblk_y;
168     surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
171 /* ===========================================================================
172  * r600/r700 family
173  */
174 static int r6_init_hw_info(struct radeon_surface_manager *surf_man)
176     uint32_t tiling_config;
177     drmVersionPtr version;
178     int r;
180     r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
181                          &tiling_config);
182     if (r) {
183         return r;
184     }
186     surf_man->hw_info.allow_2d = 0;
187     version = drmGetVersion(surf_man->fd);
188     if (version && version->version_minor >= 14) {
189         surf_man->hw_info.allow_2d = 1;
190     }
192     switch ((tiling_config & 0xe) >> 1) {
193     case 0:
194         surf_man->hw_info.num_pipes = 1;
195         break;
196     case 1:
197         surf_man->hw_info.num_pipes = 2;
198         break;
199     case 2:
200         surf_man->hw_info.num_pipes = 4;
201         break;
202     case 3:
203         surf_man->hw_info.num_pipes = 8;
204         break;
205     default:
206         return -EINVAL;
207     }
209     switch ((tiling_config & 0x30) >> 4) {
210     case 0:
211         surf_man->hw_info.num_banks = 4;
212         break;
213     case 1:
214         surf_man->hw_info.num_banks = 8;
215         break;
216     default:
217         return -EINVAL;
218     }
220     switch ((tiling_config & 0xc0) >> 6) {
221     case 0:
222         surf_man->hw_info.group_bytes = 256;
223         break;
224     case 1:
225         surf_man->hw_info.group_bytes = 512;
226         break;
227     default:
228         return -EINVAL;
229     }
230     return 0;
233 static int r6_surface_init_linear(struct radeon_surface_manager *surf_man,
234                                   struct radeon_surface *surf,
235                                   uint64_t offset, unsigned start_level)
237     uint32_t xalign, yalign, zalign;
238     unsigned i;
240     /* compute alignment */
241     if (!start_level) {
242         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
243     }
244     /* the 32 alignment is for scanout, cb or db but to allow texture to be
245      * easily bound as such we force this alignment to all surface
246      */
247     xalign = MAX2(1, surf_man->hw_info.group_bytes / surf->bpe);
248     yalign = 1;
249     zalign = 1;
250     if (surf->flags & RADEON_SURF_SCANOUT) {
251         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
252     }
254     /* build mipmap tree */
255     for (i = start_level; i <= surf->last_level; i++) {
256         surf->level[i].mode = RADEON_SURF_MODE_LINEAR;
257         surf_minify(surf, i, xalign, yalign, zalign, offset);
258         /* level0 and first mipmap need to have alignment */
259         offset = surf->bo_size;
260         if ((i == 0)) {
261             offset = ALIGN(offset, surf->bo_alignment);
262         }
263     }
264     return 0;
267 static int r6_surface_init_linear_aligned(struct radeon_surface_manager *surf_man,
268                                           struct radeon_surface *surf,
269                                           uint64_t offset, unsigned start_level)
271     uint32_t xalign, yalign, zalign;
272     unsigned i;
274     /* compute alignment */
275     if (!start_level) {
276         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
277     }
278     xalign = MAX2(64, surf_man->hw_info.group_bytes / surf->bpe);
279     yalign = 1;
280     zalign = 1;
282     /* build mipmap tree */
283     for (i = start_level; i <= surf->last_level; i++) {
284         surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
285         surf_minify(surf, i, xalign, yalign, zalign, offset);
286         /* level0 and first mipmap need to have alignment */
287         offset = surf->bo_size;
288         if ((i == 0)) {
289             offset = ALIGN(offset, surf->bo_alignment);
290         }
291     }
292     return 0;
295 static int r6_surface_init_1d(struct radeon_surface_manager *surf_man,
296                               struct radeon_surface *surf,
297                               uint64_t offset, unsigned start_level)
299     uint32_t xalign, yalign, zalign, tilew;
300     unsigned i;
302     /* compute alignment */
303     tilew = 8;
304     xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
305     xalign = MAX2(tilew, xalign);
306     yalign = tilew;
307     zalign = 1;
308     if (surf->flags & RADEON_SURF_SCANOUT) {
309         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
310     }
311     if (!start_level) {
312         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
313     }
315     /* build mipmap tree */
316     for (i = start_level; i <= surf->last_level; i++) {
317         surf->level[i].mode = RADEON_SURF_MODE_1D;
318         surf_minify(surf, i, xalign, yalign, zalign, offset);
319         /* level0 and first mipmap need to have alignment */
320         offset = surf->bo_size;
321         if ((i == 0)) {
322             offset = ALIGN(offset, surf->bo_alignment);
323         }
324     }
325     return 0;
328 static int r6_surface_init_2d(struct radeon_surface_manager *surf_man,
329                               struct radeon_surface *surf,
330                               uint64_t offset, unsigned start_level)
332     uint32_t xalign, yalign, zalign, tilew;
333     unsigned i;
335     /* compute alignment */
336     tilew = 8;
337     zalign = 1;
338     xalign = (surf_man->hw_info.group_bytes * surf_man->hw_info.num_banks) /
339              (tilew * surf->bpe * surf->nsamples);
340     xalign = MAX2(tilew * surf_man->hw_info.num_banks, xalign);
341     yalign = tilew * surf_man->hw_info.num_pipes;
342     if (surf->flags & RADEON_SURF_SCANOUT) {
343         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
344     }
345     if (!start_level) {
346         surf->bo_alignment =
347             MAX2(surf_man->hw_info.num_pipes *
348                  surf_man->hw_info.num_banks *
349                  surf->bpe * 64,
350                  xalign * yalign * surf->nsamples * surf->bpe);
351     }
353     /* build mipmap tree */
354     for (i = start_level; i <= surf->last_level; i++) {
355         surf->level[i].mode = RADEON_SURF_MODE_2D;
356         surf_minify(surf, i, xalign, yalign, zalign, offset);
357         if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
358             return r6_surface_init_1d(surf_man, surf, offset, i);
359         }
360         /* level0 and first mipmap need to have alignment */
361         offset = surf->bo_size;
362         if ((i == 0)) {
363             offset = ALIGN(offset, surf->bo_alignment);
364         }
365     }
366     return 0;
369 static int r6_surface_init(struct radeon_surface_manager *surf_man,
370                            struct radeon_surface *surf)
372     unsigned mode;
373     int r;
375     /* tiling mode */
376     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
378     /* force 1d on kernel that can't do 2d */
379     if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
380         mode = RADEON_SURF_MODE_1D;
381         surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
382         surf->flags |= RADEON_SURF_SET(mode, MODE);
383     }
385     /* check surface dimension */
386     if (surf->npix_x > 8192 || surf->npix_y > 8192 || surf->npix_z > 8192) {
387         return -EINVAL;
388     }
390     /* check mipmap last_level */
391     if (surf->last_level > 14) {
392         return -EINVAL;
393     }
395     /* check tiling mode */
396     switch (mode) {
397     case RADEON_SURF_MODE_LINEAR:
398         r = r6_surface_init_linear(surf_man, surf, 0, 0);
399         break;
400     case RADEON_SURF_MODE_LINEAR_ALIGNED:
401         r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
402         break;
403     case RADEON_SURF_MODE_1D:
404         r = r6_surface_init_1d(surf_man, surf, 0, 0);
405         break;
406     case RADEON_SURF_MODE_2D:
407         r = r6_surface_init_2d(surf_man, surf, 0, 0);
408         break;
409     default:
410         return -EINVAL;
411     }
412     return r;
415 static int r6_surface_best(struct radeon_surface_manager *surf_man,
416                            struct radeon_surface *surf)
418     /* no value to optimize for r6xx/r7xx */
419     return 0;
423 /* ===========================================================================
424  * evergreen family
425  */
426 static int eg_init_hw_info(struct radeon_surface_manager *surf_man)
428     uint32_t tiling_config;
429     drmVersionPtr version;
430     int r;
432     r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
433                          &tiling_config);
434     if (r) {
435         return r;
436     }
438     surf_man->hw_info.allow_2d = 0;
439     version = drmGetVersion(surf_man->fd);
440     if (version && version->version_minor >= 14) {
441         surf_man->hw_info.allow_2d = 1;
442     }
444     switch (tiling_config & 0xf) {
445     case 0:
446         surf_man->hw_info.num_pipes = 1;
447         break;
448     case 1:
449         surf_man->hw_info.num_pipes = 2;
450         break;
451     case 2:
452         surf_man->hw_info.num_pipes = 4;
453         break;
454     case 3:
455         surf_man->hw_info.num_pipes = 8;
456         break;
457     default:
458         return -EINVAL;
459     }
461     switch ((tiling_config & 0xf0) >> 4) {
462     case 0:
463         surf_man->hw_info.num_banks = 4;
464         break;
465     case 1:
466         surf_man->hw_info.num_banks = 8;
467         break;
468     case 2:
469         surf_man->hw_info.num_banks = 16;
470         break;
471     default:
472         return -EINVAL;
473     }
475     switch ((tiling_config & 0xf00) >> 8) {
476     case 0:
477         surf_man->hw_info.group_bytes = 256;
478         break;
479     case 1:
480         surf_man->hw_info.group_bytes = 512;
481         break;
482     default:
483         return -EINVAL;
484     }
486     switch ((tiling_config & 0xf000) >> 12) {
487     case 0:
488         surf_man->hw_info.row_size = 1024;
489         break;
490     case 1:
491         surf_man->hw_info.row_size = 2048;
492         break;
493     case 2:
494         surf_man->hw_info.row_size = 4096;
495         break;
496     default:
497         return -EINVAL;
498     }
499     return 0;
502 static void eg_surf_minify(struct radeon_surface *surf,
503                            unsigned level,
504                            unsigned slice_pt,
505                            unsigned mtilew,
506                            unsigned mtileh,
507                            unsigned mtileb,
508                            unsigned offset)
510     unsigned mtile_pr, mtile_ps;
512     surf->level[level].npix_x = mip_minify(surf->npix_x, level);
513     surf->level[level].npix_y = mip_minify(surf->npix_y, level);
514     surf->level[level].npix_z = mip_minify(surf->npix_z, level);
515     surf->level[level].nblk_x = (surf->level[level].npix_x + surf->blk_w - 1) / surf->blk_w;
516     surf->level[level].nblk_y = (surf->level[level].npix_y + surf->blk_h - 1) / surf->blk_h;
517     surf->level[level].nblk_z = (surf->level[level].npix_z + surf->blk_d - 1) / surf->blk_d;
518     if (surf->level[level].mode == RADEON_SURF_MODE_2D) {
519         if (surf->level[level].nblk_x < mtilew || surf->level[level].nblk_y < mtileh) {
520             surf->level[level].mode = RADEON_SURF_MODE_1D;
521             return;
522         }
523     }
524     surf->level[level].nblk_x  = ALIGN(surf->level[level].nblk_x, mtilew);
525     surf->level[level].nblk_y  = ALIGN(surf->level[level].nblk_y, mtileh);
526     surf->level[level].nblk_z  = ALIGN(surf->level[level].nblk_z, 1);
528     /* macro tile per row */
529     mtile_pr = surf->level[level].nblk_x / mtilew;
530     /* macro tile per slice */
531     mtile_ps = (mtile_pr * surf->level[level].nblk_y) / mtileh;
533     surf->level[level].offset = offset;
534     surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe * slice_pt;
535     surf->level[level].slice_size = mtile_ps * mtileb * slice_pt;
537     surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
540 static int eg_surface_init_1d(struct radeon_surface_manager *surf_man,
541                               struct radeon_surface *surf,
542                               uint64_t offset, unsigned start_level)
544     uint32_t xalign, yalign, zalign, tilew;
545     unsigned i;
547     /* compute alignment */
548     tilew = 8;
549     xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
550     if (surf->flags & RADEON_SURF_SBUFFER) {
551         surf->stencil_offset = 0;
552         surf->stencil_tile_split = 0;
553         xalign = surf_man->hw_info.group_bytes / (tilew * surf->nsamples);
554     }
555     xalign = MAX2(tilew, xalign);
556     yalign = tilew;
557     zalign = 1;
558     if (surf->flags & RADEON_SURF_SCANOUT) {
559         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
560     }
561     if (!start_level) {
562         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
563     }
565     /* build mipmap tree */
566     for (i = start_level; i <= surf->last_level; i++) {
567         surf->level[i].mode = RADEON_SURF_MODE_1D;
568         surf_minify(surf, i, xalign, yalign, zalign, offset);
569         /* level0 and first mipmap need to have alignment */
570         offset = surf->bo_size;
571         if ((i == 0)) {
572             offset = ALIGN(offset, surf->bo_alignment);
573         }
574     }
576     if (surf->flags & RADEON_SURF_SBUFFER) {
577         surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
578         surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
579     }
581     return 0;
584 static int eg_surface_init_2d(struct radeon_surface_manager *surf_man,
585                               struct radeon_surface *surf,
586                               uint64_t offset, unsigned start_level)
588     unsigned tilew, tileh, tileb;
589     unsigned mtilew, mtileh, mtileb;
590     unsigned slice_pt;
591     unsigned i;
593     surf->stencil_offset = 0;
594     /* compute tile values */
595     tilew = 8;
596     tileh = 8;
597     tileb = tilew * tileh * surf->bpe * surf->nsamples;
598     /* slices per tile */
599     slice_pt = 1;
600     if (tileb > surf->tile_split) {
601         slice_pt = tileb / surf->tile_split;
602     }
603     tileb = tileb / slice_pt;
605     /* macro tile width & height */
606     mtilew = (tilew * surf->bankw * surf_man->hw_info.num_pipes) * surf->mtilea;
607     mtileh = (tileh * surf->bankh * surf_man->hw_info.num_banks) / surf->mtilea;
608     /* macro tile bytes */
609     mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb;
611     if (!start_level) {
612         surf->bo_alignment = MAX2(256, mtileb);
613     }
615     /* build mipmap tree */
616     for (i = start_level; i <= surf->last_level; i++) {
617         surf->level[i].mode = RADEON_SURF_MODE_2D;
618         eg_surf_minify(surf, i, slice_pt, mtilew, mtileh, mtileb, offset);
619         if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
620             return eg_surface_init_1d(surf_man, surf, offset, i);
621         }
622         /* level0 and first mipmap need to have alignment */
623         offset = surf->bo_size;
624         if ((i == 0)) {
625             offset = ALIGN(offset, surf->bo_alignment);
626         }
627     }
629     if (surf->flags & RADEON_SURF_SBUFFER) {
630         surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
631         surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
632     }
634     return 0;
637 static int eg_surface_sanity(struct radeon_surface_manager *surf_man,
638                              struct radeon_surface *surf,
639                              unsigned mode)
641     unsigned tileb;
643     /* check surface dimension */
644     if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) {
645         return -EINVAL;
646     }
648     /* check mipmap last_level */
649     if (surf->last_level > 15) {
650         return -EINVAL;
651     }
653     /* force 1d on kernel that can't do 2d */
654     if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
655         mode = RADEON_SURF_MODE_1D;
656         surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
657         surf->flags |= RADEON_SURF_SET(mode, MODE);
658     }
660     /* check tile split */
661     if (mode == RADEON_SURF_MODE_2D) {
662         switch (surf->tile_split) {
663         case 64:
664         case 128:
665         case 256:
666         case 512:
667         case 1024:
668         case 2048:
669         case 4096:
670             break;
671         default:
672             return -EINVAL;
673         }
674         switch (surf->mtilea) {
675         case 1:
676         case 2:
677         case 4:
678         case 8:
679             break;
680         default:
681             return -EINVAL;
682         }
683         /* check aspect ratio */
684         if (surf_man->hw_info.num_banks < surf->mtilea) {
685             return -EINVAL;
686         }
687         /* check bank width */
688         switch (surf->bankw) {
689         case 1:
690         case 2:
691         case 4:
692         case 8:
693             break;
694         default:
695             return -EINVAL;
696         }
697         /* check bank height */
698         switch (surf->bankh) {
699         case 1:
700         case 2:
701         case 4:
702         case 8:
703             break;
704         default:
705             return -EINVAL;
706         }
707         tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
708         if ((tileb * surf->bankh * surf->bankw) < surf_man->hw_info.group_bytes) {
709             return -EINVAL;
710         }
711     }
713     return 0;
716 static int eg_surface_init(struct radeon_surface_manager *surf_man,
717                            struct radeon_surface *surf)
719     unsigned mode;
720     int r;
722     /* tiling mode */
723     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
725     /* for some reason eg need to have room for stencil right after depth */
726     if (surf->flags & RADEON_SURF_ZBUFFER) {
727         surf->flags |= RADEON_SURF_SBUFFER;
728     }
730     r = eg_surface_sanity(surf_man, surf, mode);
731     if (r) {
732         return r;
733     }
735     /* check tiling mode */
736     switch (mode) {
737     case RADEON_SURF_MODE_LINEAR:
738         r = r6_surface_init_linear(surf_man, surf, 0, 0);
739         break;
740     case RADEON_SURF_MODE_LINEAR_ALIGNED:
741         r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
742         break;
743     case RADEON_SURF_MODE_1D:
744         r = eg_surface_init_1d(surf_man, surf, 0, 0);
745         break;
746     case RADEON_SURF_MODE_2D:
747         r = eg_surface_init_2d(surf_man, surf, 0, 0);
748         break;
749     default:
750         return -EINVAL;
751     }
752     return r;
755 static unsigned log2_int(unsigned x)
757     unsigned l;
759     if (x < 2) {
760         return 0;
761     }
762     for (l = 2; ; l++) {
763         if ((unsigned)(1 << l) > x) {
764             return l - 1;
765         }
766     }
767     return 0;
770 /* compute best tile_split, bankw, bankh, mtilea
771  * depending on surface
772  */
773 static int eg_surface_best(struct radeon_surface_manager *surf_man,
774                            struct radeon_surface *surf)
776     unsigned mode, tileb, h_over_w;
777     int r;
779     /* tiling mode */
780     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
782     /* for some reason eg need to have room for stencil right after depth */
783     if (surf->flags & RADEON_SURF_ZBUFFER) {
784         surf->flags |= RADEON_SURF_SBUFFER;
785     }
787     /* set some default value to avoid sanity check choking on them */
788     surf->tile_split = 1024;
789     surf->bankw = 1;
790     surf->bankh = 1;
791     surf->mtilea = surf_man->hw_info.num_banks;
792     tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
793     for (; surf->bankh <= 8; surf->bankh *= 2) {
794         if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
795             break;
796         }
797     }
798     if (surf->mtilea > 8) {
799         surf->mtilea = 8;
800     }
802     r = eg_surface_sanity(surf_man, surf, mode);
803     if (r) {
804         return r;
805     }
807     if (mode != RADEON_SURF_MODE_2D) {
808         /* nothing to do for non 2D tiled surface */
809         return 0;
810     }
812     /* set tile split to row size, optimize latter for multi-sample surface
813      * tile split >= 256 for render buffer surface. Also depth surface want
814      * smaller value for optimal performances.
815      */
816     surf->tile_split = surf_man->hw_info.row_size;
817     surf->stencil_tile_split = surf_man->hw_info.row_size / 2;
819     /* bankw or bankh greater than 1 increase alignment requirement, not
820      * sure if it's worth using smaller bankw & bankh to stick with 2D
821      * tiling on small surface rather than falling back to 1D tiling.
822      * Use recommanded value based on tile size for now.
823      *
824      * fmask buffer has different optimal value figure them out once we
825      * use it.
826      */
827     if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
828         /* assume 1 bytes for stencil, we optimize for stencil as stencil
829          * and depth shares surface values
830          */
831         tileb = MIN2(surf->tile_split, 64 * surf->nsamples);
832     } else {
833         tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
834     }
836     /* use bankw of 1 to minimize width alignment, might be interesting to
837      * increase it for large surface
838      */
839     surf->bankw = 1;
840     switch (tileb) {
841     case 64:
842         surf->bankh = 4;
843         break;
844     case 128:
845     case 256:
846         surf->bankh = 2;
847         break;
848     default:
849         surf->bankh = 1;
850         break;
851     }
852     /* double check the constraint */
853     for (; surf->bankh <= 8; surf->bankh *= 2) {
854         if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
855             break;
856         }
857     }
859     h_over_w = (((surf->bankh * surf_man->hw_info.num_banks) << 16) /
860                 (surf->bankw * surf_man->hw_info.num_pipes)) >> 16;
861     surf->mtilea = 1 << (log2_int(h_over_w) >> 1);
863     return 0;
867 /* ===========================================================================
868  * public API
869  */
870 struct radeon_surface_manager *radeon_surface_manager_new(int fd)
872     struct radeon_surface_manager *surf_man;
874     surf_man = calloc(1, sizeof(struct radeon_surface_manager));
875     if (surf_man == NULL) {
876         return NULL;
877     }
878     surf_man->fd = fd;
879     if (radeon_get_value(fd, RADEON_INFO_DEVICE_ID, &surf_man->device_id)) {
880         goto out_err;
881     }
882     if (radeon_get_family(surf_man)) {
883         goto out_err;
884     }
886     if (surf_man->family <= CHIP_RV740) {
887         if (r6_init_hw_info(surf_man)) {
888             goto out_err;
889         }
890         surf_man->surface_init = &r6_surface_init;
891         surf_man->surface_best = &r6_surface_best;
892     } else {
893         if (eg_init_hw_info(surf_man)) {
894             goto out_err;
895         }
896         surf_man->surface_init = &eg_surface_init;
897         surf_man->surface_best = &eg_surface_best;
898     }
900     return surf_man;
901 out_err:
902     free(surf_man);
903     return NULL;
906 void radeon_surface_manager_free(struct radeon_surface_manager *surf_man)
908     free(surf_man);
911 static int radeon_surface_sanity(struct radeon_surface_manager *surf_man,
912                                  struct radeon_surface *surf,
913                                  unsigned type,
914                                  unsigned mode)
916     if (surf_man == NULL || surf_man->surface_init == NULL || surf == NULL) {
917         return -EINVAL;
918     }
920     /* all dimension must be at least 1 ! */
921     if (!surf->npix_x || !surf->npix_y || !surf->npix_z) {
922         return -EINVAL;
923     }
924     if (!surf->blk_w || !surf->blk_h || !surf->blk_d) {
925         return -EINVAL;
926     }
927     if (!surf->array_size) {
928         return -EINVAL;
929     }
930     /* array size must be a power of 2 */
931     surf->array_size = next_power_of_two(surf->array_size);
933     switch (surf->nsamples) {
934     case 1:
935     case 2:
936     case 4:
937     case 8:
938         break;
939     default:
940         return -EINVAL;
941     }
942     /* check type */
943     switch (type) {
944     case RADEON_SURF_TYPE_1D:
945         if (surf->npix_y > 1) {
946             return -EINVAL;
947         }
948     case RADEON_SURF_TYPE_2D:
949         if (surf->npix_z > 1) {
950             return -EINVAL;
951         }
952         break;
953     case RADEON_SURF_TYPE_CUBEMAP:
954         if (surf->npix_z > 1) {
955             return -EINVAL;
956         }
957         /* deal with cubemap as they were texture array */
958         if (surf_man->family >= CHIP_RV770) {
959             surf->array_size = 8;
960         } else {
961             surf->array_size = 6;
962         }
963         break;
964     case RADEON_SURF_TYPE_3D:
965         break;
966     case RADEON_SURF_TYPE_1D_ARRAY:
967         if (surf->npix_y > 1) {
968             return -EINVAL;
969         }
970     case RADEON_SURF_TYPE_2D_ARRAY:
971         break;
972     default:
973         return -EINVAL;
974     }
975     return 0;
978 int radeon_surface_init(struct radeon_surface_manager *surf_man,
979                         struct radeon_surface *surf)
981     unsigned mode, type;
982     int r;
984     type = RADEON_SURF_GET(surf->flags, TYPE);
985     mode = RADEON_SURF_GET(surf->flags, MODE);
987     r = radeon_surface_sanity(surf_man, surf, type, mode);
988     if (r) {
989         return r;
990     }
991     return surf_man->surface_init(surf_man, surf);
994 int radeon_surface_best(struct radeon_surface_manager *surf_man,
995                         struct radeon_surface *surf)
997     unsigned mode, type;
998     int r;
1000     type = RADEON_SURF_GET(surf->flags, TYPE);
1001     mode = RADEON_SURF_GET(surf->flags, MODE);
1003     r = radeon_surface_sanity(surf_man, surf, type, mode);
1004     if (r) {
1005         return r;
1006     }
1007     return surf_man->surface_best(surf_man, surf);