OSDN Git Service

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