a7b488b6b07bc45686f993e5c752f2fcd1a6ca3a
[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         return -EINVAL;
210     }
212     switch ((tiling_config & 0x30) >> 4) {
213     case 0:
214         surf_man->hw_info.num_banks = 4;
215         break;
216     case 1:
217         surf_man->hw_info.num_banks = 8;
218         break;
219     default:
220         return -EINVAL;
221     }
223     switch ((tiling_config & 0xc0) >> 6) {
224     case 0:
225         surf_man->hw_info.group_bytes = 256;
226         break;
227     case 1:
228         surf_man->hw_info.group_bytes = 512;
229         break;
230     default:
231         return -EINVAL;
232     }
233     return 0;
236 static int r6_surface_init_linear(struct radeon_surface_manager *surf_man,
237                                   struct radeon_surface *surf,
238                                   uint64_t offset, unsigned start_level)
240     uint32_t xalign, yalign, zalign;
241     unsigned i;
243     /* compute alignment */
244     if (!start_level) {
245         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
246     }
247     /* the 32 alignment is for scanout, cb or db but to allow texture to be
248      * easily bound as such we force this alignment to all surface
249      */
250     xalign = MAX2(1, surf_man->hw_info.group_bytes / surf->bpe);
251     yalign = 1;
252     zalign = 1;
253     if (surf->flags & RADEON_SURF_SCANOUT) {
254         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
255     }
257     /* build mipmap tree */
258     for (i = start_level; i <= surf->last_level; i++) {
259         surf->level[i].mode = RADEON_SURF_MODE_LINEAR;
260         surf_minify(surf, i, xalign, yalign, zalign, offset);
261         /* level0 and first mipmap need to have alignment */
262         offset = surf->bo_size;
263         if ((i == 0)) {
264             offset = ALIGN(offset, surf->bo_alignment);
265         }
266     }
267     return 0;
270 static int r6_surface_init_linear_aligned(struct radeon_surface_manager *surf_man,
271                                           struct radeon_surface *surf,
272                                           uint64_t offset, unsigned start_level)
274     uint32_t xalign, yalign, zalign;
275     unsigned i;
277     /* compute alignment */
278     if (!start_level) {
279         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
280     }
281     xalign = MAX2(64, surf_man->hw_info.group_bytes / surf->bpe);
282     yalign = 1;
283     zalign = 1;
285     /* build mipmap tree */
286     for (i = start_level; i <= surf->last_level; i++) {
287         surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
288         surf_minify(surf, i, xalign, yalign, zalign, offset);
289         /* level0 and first mipmap need to have alignment */
290         offset = surf->bo_size;
291         if ((i == 0)) {
292             offset = ALIGN(offset, surf->bo_alignment);
293         }
294     }
295     return 0;
298 static int r6_surface_init_1d(struct radeon_surface_manager *surf_man,
299                               struct radeon_surface *surf,
300                               uint64_t offset, unsigned start_level)
302     uint32_t xalign, yalign, zalign, tilew;
303     unsigned i;
305     /* compute alignment */
306     tilew = 8;
307     xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
308     xalign = MAX2(tilew, xalign);
309     yalign = tilew;
310     zalign = 1;
311     if (surf->flags & RADEON_SURF_SCANOUT) {
312         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
313     }
314     if (!start_level) {
315         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
316     }
318     /* build mipmap tree */
319     for (i = start_level; i <= surf->last_level; i++) {
320         surf->level[i].mode = RADEON_SURF_MODE_1D;
321         surf_minify(surf, i, xalign, yalign, zalign, offset);
322         /* level0 and first mipmap need to have alignment */
323         offset = surf->bo_size;
324         if ((i == 0)) {
325             offset = ALIGN(offset, surf->bo_alignment);
326         }
327     }
328     return 0;
331 static int r6_surface_init_2d(struct radeon_surface_manager *surf_man,
332                               struct radeon_surface *surf,
333                               uint64_t offset, unsigned start_level)
335     uint32_t xalign, yalign, zalign, tilew;
336     unsigned i;
338     /* compute alignment */
339     tilew = 8;
340     zalign = 1;
341     xalign = (surf_man->hw_info.group_bytes * surf_man->hw_info.num_banks) /
342              (tilew * surf->bpe * surf->nsamples);
343     xalign = MAX2(tilew * surf_man->hw_info.num_banks, xalign);
344     yalign = tilew * surf_man->hw_info.num_pipes;
345     if (surf->flags & RADEON_SURF_SCANOUT) {
346         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
347     }
348     if (!start_level) {
349         surf->bo_alignment =
350             MAX2(surf_man->hw_info.num_pipes *
351                  surf_man->hw_info.num_banks *
352                  surf->bpe * 64,
353                  xalign * yalign * surf->nsamples * surf->bpe);
354     }
356     /* build mipmap tree */
357     for (i = start_level; i <= surf->last_level; i++) {
358         surf->level[i].mode = RADEON_SURF_MODE_2D;
359         surf_minify(surf, i, xalign, yalign, zalign, offset);
360         if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
361             return r6_surface_init_1d(surf_man, surf, offset, i);
362         }
363         /* level0 and first mipmap need to have alignment */
364         offset = surf->bo_size;
365         if ((i == 0)) {
366             offset = ALIGN(offset, surf->bo_alignment);
367         }
368     }
369     return 0;
372 static int r6_surface_init(struct radeon_surface_manager *surf_man,
373                            struct radeon_surface *surf)
375     unsigned mode;
376     int r;
378     /* tiling mode */
379     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
381     /* force 1d on kernel that can't do 2d */
382     if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
383         mode = RADEON_SURF_MODE_1D;
384         surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
385         surf->flags |= RADEON_SURF_SET(mode, MODE);
386     }
388     /* check surface dimension */
389     if (surf->npix_x > 8192 || surf->npix_y > 8192 || surf->npix_z > 8192) {
390         return -EINVAL;
391     }
393     /* check mipmap last_level */
394     if (surf->last_level > 14) {
395         return -EINVAL;
396     }
398     /* check tiling mode */
399     switch (mode) {
400     case RADEON_SURF_MODE_LINEAR:
401         r = r6_surface_init_linear(surf_man, surf, 0, 0);
402         break;
403     case RADEON_SURF_MODE_LINEAR_ALIGNED:
404         r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
405         break;
406     case RADEON_SURF_MODE_1D:
407         r = r6_surface_init_1d(surf_man, surf, 0, 0);
408         break;
409     case RADEON_SURF_MODE_2D:
410         r = r6_surface_init_2d(surf_man, surf, 0, 0);
411         break;
412     default:
413         return -EINVAL;
414     }
415     return r;
418 static int r6_surface_best(struct radeon_surface_manager *surf_man,
419                            struct radeon_surface *surf)
421     /* no value to optimize for r6xx/r7xx */
422     return 0;
426 /* ===========================================================================
427  * evergreen family
428  */
429 static int eg_init_hw_info(struct radeon_surface_manager *surf_man)
431     uint32_t tiling_config;
432     drmVersionPtr version;
433     int r;
435     r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
436                          &tiling_config);
437     if (r) {
438         return r;
439     }
441     surf_man->hw_info.allow_2d = 0;
442     version = drmGetVersion(surf_man->fd);
443     if (version && version->version_minor >= 14) {
444         surf_man->hw_info.allow_2d = 1;
445     }
447     switch (tiling_config & 0xf) {
448     case 0:
449         surf_man->hw_info.num_pipes = 1;
450         break;
451     case 1:
452         surf_man->hw_info.num_pipes = 2;
453         break;
454     case 2:
455         surf_man->hw_info.num_pipes = 4;
456         break;
457     case 3:
458         surf_man->hw_info.num_pipes = 8;
459         break;
460     default:
461         return -EINVAL;
462     }
464     switch ((tiling_config & 0xf0) >> 4) {
465     case 0:
466         surf_man->hw_info.num_banks = 4;
467         break;
468     case 1:
469         surf_man->hw_info.num_banks = 8;
470         break;
471     case 2:
472         surf_man->hw_info.num_banks = 16;
473         break;
474     default:
475         return -EINVAL;
476     }
478     switch ((tiling_config & 0xf00) >> 8) {
479     case 0:
480         surf_man->hw_info.group_bytes = 256;
481         break;
482     case 1:
483         surf_man->hw_info.group_bytes = 512;
484         break;
485     default:
486         return -EINVAL;
487     }
489     switch ((tiling_config & 0xf000) >> 12) {
490     case 0:
491         surf_man->hw_info.row_size = 1024;
492         break;
493     case 1:
494         surf_man->hw_info.row_size = 2048;
495         break;
496     case 2:
497         surf_man->hw_info.row_size = 4096;
498         break;
499     default:
500         return -EINVAL;
501     }
502     return 0;
505 static void eg_surf_minify(struct radeon_surface *surf,
506                            unsigned level,
507                            unsigned slice_pt,
508                            unsigned mtilew,
509                            unsigned mtileh,
510                            unsigned mtileb,
511                            unsigned offset)
513     unsigned mtile_pr, mtile_ps;
515     surf->level[level].npix_x = mip_minify(surf->npix_x, level);
516     surf->level[level].npix_y = mip_minify(surf->npix_y, level);
517     surf->level[level].npix_z = mip_minify(surf->npix_z, level);
518     surf->level[level].nblk_x = (surf->level[level].npix_x + surf->blk_w - 1) / surf->blk_w;
519     surf->level[level].nblk_y = (surf->level[level].npix_y + surf->blk_h - 1) / surf->blk_h;
520     surf->level[level].nblk_z = (surf->level[level].npix_z + surf->blk_d - 1) / surf->blk_d;
521     if (surf->level[level].mode == RADEON_SURF_MODE_2D) {
522         if (surf->level[level].nblk_x < mtilew || surf->level[level].nblk_y < mtileh) {
523             surf->level[level].mode = RADEON_SURF_MODE_1D;
524             return;
525         }
526     }
527     surf->level[level].nblk_x  = ALIGN(surf->level[level].nblk_x, mtilew);
528     surf->level[level].nblk_y  = ALIGN(surf->level[level].nblk_y, mtileh);
529     surf->level[level].nblk_z  = ALIGN(surf->level[level].nblk_z, 1);
531     /* macro tile per row */
532     mtile_pr = surf->level[level].nblk_x / mtilew;
533     /* macro tile per slice */
534     mtile_ps = (mtile_pr * surf->level[level].nblk_y) / mtileh;
536     surf->level[level].offset = offset;
537     surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe * slice_pt;
538     surf->level[level].slice_size = mtile_ps * mtileb * slice_pt;
540     surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
543 static int eg_surface_init_1d(struct radeon_surface_manager *surf_man,
544                               struct radeon_surface *surf,
545                               uint64_t offset, unsigned start_level)
547     uint32_t xalign, yalign, zalign, tilew;
548     unsigned i;
550     /* compute alignment */
551     tilew = 8;
552     xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
553     if (surf->flags & RADEON_SURF_SBUFFER) {
554         surf->stencil_offset = 0;
555         surf->stencil_tile_split = 0;
556         xalign = surf_man->hw_info.group_bytes / (tilew * surf->nsamples);
557     }
558     xalign = MAX2(tilew, xalign);
559     yalign = tilew;
560     zalign = 1;
561     if (surf->flags & RADEON_SURF_SCANOUT) {
562         xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
563     }
564     if (!start_level) {
565         surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
566     }
568     /* build mipmap tree */
569     for (i = start_level; i <= surf->last_level; i++) {
570         surf->level[i].mode = RADEON_SURF_MODE_1D;
571         surf_minify(surf, i, xalign, yalign, zalign, offset);
572         /* level0 and first mipmap need to have alignment */
573         offset = surf->bo_size;
574         if ((i == 0)) {
575             offset = ALIGN(offset, surf->bo_alignment);
576         }
577     }
579     if (surf->flags & RADEON_SURF_SBUFFER) {
580         surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
581         surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
582     }
584     return 0;
587 static int eg_surface_init_2d(struct radeon_surface_manager *surf_man,
588                               struct radeon_surface *surf,
589                               uint64_t offset, unsigned start_level)
591     unsigned tilew, tileh, tileb;
592     unsigned mtilew, mtileh, mtileb;
593     unsigned slice_pt;
594     unsigned i;
596     surf->stencil_offset = 0;
597     /* compute tile values */
598     tilew = 8;
599     tileh = 8;
600     tileb = tilew * tileh * surf->bpe * surf->nsamples;
601     /* slices per tile */
602     slice_pt = 1;
603     if (tileb > surf->tile_split) {
604         slice_pt = tileb / surf->tile_split;
605     }
606     tileb = tileb / slice_pt;
608     /* macro tile width & height */
609     mtilew = (tilew * surf->bankw * surf_man->hw_info.num_pipes) * surf->mtilea;
610     mtileh = (tileh * surf->bankh * surf_man->hw_info.num_banks) / surf->mtilea;
611     /* macro tile bytes */
612     mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb;
614     if (!start_level) {
615         surf->bo_alignment = MAX2(256, mtileb);
616     }
618     /* build mipmap tree */
619     for (i = start_level; i <= surf->last_level; i++) {
620         surf->level[i].mode = RADEON_SURF_MODE_2D;
621         eg_surf_minify(surf, i, slice_pt, mtilew, mtileh, mtileb, offset);
622         if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
623             return eg_surface_init_1d(surf_man, surf, offset, i);
624         }
625         /* level0 and first mipmap need to have alignment */
626         offset = surf->bo_size;
627         if ((i == 0)) {
628             offset = ALIGN(offset, surf->bo_alignment);
629         }
630     }
632     if (surf->flags & RADEON_SURF_SBUFFER) {
633         surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
634         surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
635     }
637     return 0;
640 static int eg_surface_sanity(struct radeon_surface_manager *surf_man,
641                              struct radeon_surface *surf,
642                              unsigned mode)
644     unsigned tileb;
646     /* check surface dimension */
647     if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) {
648         return -EINVAL;
649     }
651     /* check mipmap last_level */
652     if (surf->last_level > 15) {
653         return -EINVAL;
654     }
656     /* force 1d on kernel that can't do 2d */
657     if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
658         mode = RADEON_SURF_MODE_1D;
659         surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
660         surf->flags |= RADEON_SURF_SET(mode, MODE);
661     }
663     /* check tile split */
664     if (mode == RADEON_SURF_MODE_2D) {
665         switch (surf->tile_split) {
666         case 64:
667         case 128:
668         case 256:
669         case 512:
670         case 1024:
671         case 2048:
672         case 4096:
673             break;
674         default:
675             return -EINVAL;
676         }
677         switch (surf->mtilea) {
678         case 1:
679         case 2:
680         case 4:
681         case 8:
682             break;
683         default:
684             return -EINVAL;
685         }
686         /* check aspect ratio */
687         if (surf_man->hw_info.num_banks < surf->mtilea) {
688             return -EINVAL;
689         }
690         /* check bank width */
691         switch (surf->bankw) {
692         case 1:
693         case 2:
694         case 4:
695         case 8:
696             break;
697         default:
698             return -EINVAL;
699         }
700         /* check bank height */
701         switch (surf->bankh) {
702         case 1:
703         case 2:
704         case 4:
705         case 8:
706             break;
707         default:
708             return -EINVAL;
709         }
710         tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
711         if ((tileb * surf->bankh * surf->bankw) < surf_man->hw_info.group_bytes) {
712             return -EINVAL;
713         }
714     }
716     return 0;
719 static int eg_surface_init(struct radeon_surface_manager *surf_man,
720                            struct radeon_surface *surf)
722     unsigned mode;
723     int r;
725     /* tiling mode */
726     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
728     /* for some reason eg need to have room for stencil right after depth */
729     if (surf->flags & RADEON_SURF_ZBUFFER) {
730         surf->flags |= RADEON_SURF_SBUFFER;
731     }
733     r = eg_surface_sanity(surf_man, surf, mode);
734     if (r) {
735         return r;
736     }
738     /* check tiling mode */
739     switch (mode) {
740     case RADEON_SURF_MODE_LINEAR:
741         r = r6_surface_init_linear(surf_man, surf, 0, 0);
742         break;
743     case RADEON_SURF_MODE_LINEAR_ALIGNED:
744         r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
745         break;
746     case RADEON_SURF_MODE_1D:
747         r = eg_surface_init_1d(surf_man, surf, 0, 0);
748         break;
749     case RADEON_SURF_MODE_2D:
750         r = eg_surface_init_2d(surf_man, surf, 0, 0);
751         break;
752     default:
753         return -EINVAL;
754     }
755     return r;
758 static unsigned log2_int(unsigned x)
760     unsigned l;
762     if (x < 2) {
763         return 0;
764     }
765     for (l = 2; ; l++) {
766         if ((unsigned)(1 << l) > x) {
767             return l - 1;
768         }
769     }
770     return 0;
773 /* compute best tile_split, bankw, bankh, mtilea
774  * depending on surface
775  */
776 static int eg_surface_best(struct radeon_surface_manager *surf_man,
777                            struct radeon_surface *surf)
779     unsigned mode, tileb, h_over_w;
780     int r;
782     /* tiling mode */
783     mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
785     /* for some reason eg need to have room for stencil right after depth */
786     if (surf->flags & RADEON_SURF_ZBUFFER) {
787         surf->flags |= RADEON_SURF_SBUFFER;
788     }
790     /* set some default value to avoid sanity check choking on them */
791     surf->tile_split = 1024;
792     surf->bankw = 1;
793     surf->bankh = 1;
794     surf->mtilea = surf_man->hw_info.num_banks;
795     tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
796     for (; surf->bankh <= 8; surf->bankh *= 2) {
797         if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
798             break;
799         }
800     }
801     if (surf->mtilea > 8) {
802         surf->mtilea = 8;
803     }
805     r = eg_surface_sanity(surf_man, surf, mode);
806     if (r) {
807         return r;
808     }
810     if (mode != RADEON_SURF_MODE_2D) {
811         /* nothing to do for non 2D tiled surface */
812         return 0;
813     }
815     /* set tile split to row size, optimize latter for multi-sample surface
816      * tile split >= 256 for render buffer surface. Also depth surface want
817      * smaller value for optimal performances.
818      */
819     surf->tile_split = surf_man->hw_info.row_size;
820     surf->stencil_tile_split = surf_man->hw_info.row_size / 2;
822     /* bankw or bankh greater than 1 increase alignment requirement, not
823      * sure if it's worth using smaller bankw & bankh to stick with 2D
824      * tiling on small surface rather than falling back to 1D tiling.
825      * Use recommanded value based on tile size for now.
826      *
827      * fmask buffer has different optimal value figure them out once we
828      * use it.
829      */
830     if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
831         /* assume 1 bytes for stencil, we optimize for stencil as stencil
832          * and depth shares surface values
833          */
834         tileb = MIN2(surf->tile_split, 64 * surf->nsamples);
835     } else {
836         tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
837     }
839     /* use bankw of 1 to minimize width alignment, might be interesting to
840      * increase it for large surface
841      */
842     surf->bankw = 1;
843     switch (tileb) {
844     case 64:
845         surf->bankh = 4;
846         break;
847     case 128:
848     case 256:
849         surf->bankh = 2;
850         break;
851     default:
852         surf->bankh = 1;
853         break;
854     }
855     /* double check the constraint */
856     for (; surf->bankh <= 8; surf->bankh *= 2) {
857         if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
858             break;
859         }
860     }
862     h_over_w = (((surf->bankh * surf_man->hw_info.num_banks) << 16) /
863                 (surf->bankw * surf_man->hw_info.num_pipes)) >> 16;
864     surf->mtilea = 1 << (log2_int(h_over_w) >> 1);
866     return 0;
870 /* ===========================================================================
871  * public API
872  */
873 struct radeon_surface_manager *radeon_surface_manager_new(int fd)
875     struct radeon_surface_manager *surf_man;
877     surf_man = calloc(1, sizeof(struct radeon_surface_manager));
878     if (surf_man == NULL) {
879         return NULL;
880     }
881     surf_man->fd = fd;
882     if (radeon_get_value(fd, RADEON_INFO_DEVICE_ID, &surf_man->device_id)) {
883         goto out_err;
884     }
885     if (radeon_get_family(surf_man)) {
886         goto out_err;
887     }
889     if (surf_man->family <= CHIP_RV740) {
890         if (r6_init_hw_info(surf_man)) {
891             goto out_err;
892         }
893         surf_man->surface_init = &r6_surface_init;
894         surf_man->surface_best = &r6_surface_best;
895     } else {
896         if (eg_init_hw_info(surf_man)) {
897             goto out_err;
898         }
899         surf_man->surface_init = &eg_surface_init;
900         surf_man->surface_best = &eg_surface_best;
901     }
903     return surf_man;
904 out_err:
905     free(surf_man);
906     return NULL;
909 void radeon_surface_manager_free(struct radeon_surface_manager *surf_man)
911     free(surf_man);
914 static int radeon_surface_sanity(struct radeon_surface_manager *surf_man,
915                                  struct radeon_surface *surf,
916                                  unsigned type,
917                                  unsigned mode)
919     if (surf_man == NULL || surf_man->surface_init == NULL || surf == NULL) {
920         return -EINVAL;
921     }
923     /* all dimension must be at least 1 ! */
924     if (!surf->npix_x || !surf->npix_y || !surf->npix_z) {
925         return -EINVAL;
926     }
927     if (!surf->blk_w || !surf->blk_h || !surf->blk_d) {
928         return -EINVAL;
929     }
930     if (!surf->array_size) {
931         return -EINVAL;
932     }
933     /* array size must be a power of 2 */
934     surf->array_size = next_power_of_two(surf->array_size);
936     switch (surf->nsamples) {
937     case 1:
938     case 2:
939     case 4:
940     case 8:
941         break;
942     default:
943         return -EINVAL;
944     }
945     /* check type */
946     switch (type) {
947     case RADEON_SURF_TYPE_1D:
948         if (surf->npix_y > 1) {
949             return -EINVAL;
950         }
951     case RADEON_SURF_TYPE_2D:
952         if (surf->npix_z > 1) {
953             return -EINVAL;
954         }
955         break;
956     case RADEON_SURF_TYPE_CUBEMAP:
957         if (surf->npix_z > 1) {
958             return -EINVAL;
959         }
960         /* deal with cubemap as they were texture array */
961         if (surf_man->family >= CHIP_RV770) {
962             surf->array_size = 8;
963         } else {
964             surf->array_size = 6;
965         }
966         break;
967     case RADEON_SURF_TYPE_3D:
968         break;
969     case RADEON_SURF_TYPE_1D_ARRAY:
970         if (surf->npix_y > 1) {
971             return -EINVAL;
972         }
973     case RADEON_SURF_TYPE_2D_ARRAY:
974         break;
975     default:
976         return -EINVAL;
977     }
978     return 0;
981 int radeon_surface_init(struct radeon_surface_manager *surf_man,
982                         struct radeon_surface *surf)
984     unsigned mode, type;
985     int r;
987     type = RADEON_SURF_GET(surf->flags, TYPE);
988     mode = RADEON_SURF_GET(surf->flags, MODE);
990     r = radeon_surface_sanity(surf_man, surf, type, mode);
991     if (r) {
992         return r;
993     }
994     return surf_man->surface_init(surf_man, surf);
997 int radeon_surface_best(struct radeon_surface_manager *surf_man,
998                         struct radeon_surface *surf)
1000     unsigned mode, type;
1001     int r;
1003     type = RADEON_SURF_GET(surf->flags, TYPE);
1004     mode = RADEON_SURF_GET(surf->flags, MODE);
1006     r = radeon_surface_sanity(surf_man, surf, type, mode);
1007     if (r) {
1008         return r;
1009     }
1010     return surf_man->surface_best(surf_man, surf);