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)
104 {
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;
114 }
116 static int radeon_get_family(struct radeon_surface_manager *surf_man)
117 {
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;
126 }
128 static unsigned next_power_of_two(unsigned x)
129 {
130 if (x <= 1)
131 return 1;
133 return (1 << ((sizeof(unsigned) * 8) - __builtin_clz(x - 1)));
134 }
136 static unsigned mip_minify(unsigned size, unsigned level)
137 {
138 unsigned val;
140 val = MAX2(1, size >> level);
141 if (level > 0)
142 val = next_power_of_two(val);
143 return val;
144 }
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)
150 {
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->nsamples == 1 && 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 * surf->nsamples;
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;
172 }
174 /* ===========================================================================
175 * r600/r700 family
176 */
177 static int r6_init_hw_info(struct radeon_surface_manager *surf_man)
178 {
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 }
194 drmFreeVersion(version);
196 switch ((tiling_config & 0xe) >> 1) {
197 case 0:
198 surf_man->hw_info.num_pipes = 1;
199 break;
200 case 1:
201 surf_man->hw_info.num_pipes = 2;
202 break;
203 case 2:
204 surf_man->hw_info.num_pipes = 4;
205 break;
206 case 3:
207 surf_man->hw_info.num_pipes = 8;
208 break;
209 default:
210 surf_man->hw_info.num_pipes = 8;
211 surf_man->hw_info.allow_2d = 0;
212 break;
213 }
215 switch ((tiling_config & 0x30) >> 4) {
216 case 0:
217 surf_man->hw_info.num_banks = 4;
218 break;
219 case 1:
220 surf_man->hw_info.num_banks = 8;
221 break;
222 default:
223 surf_man->hw_info.num_banks = 8;
224 surf_man->hw_info.allow_2d = 0;
225 break;
226 }
228 switch ((tiling_config & 0xc0) >> 6) {
229 case 0:
230 surf_man->hw_info.group_bytes = 256;
231 break;
232 case 1:
233 surf_man->hw_info.group_bytes = 512;
234 break;
235 default:
236 surf_man->hw_info.group_bytes = 256;
237 surf_man->hw_info.allow_2d = 0;
238 break;
239 }
240 return 0;
241 }
243 static int r6_surface_init_linear(struct radeon_surface_manager *surf_man,
244 struct radeon_surface *surf,
245 uint64_t offset, unsigned start_level)
246 {
247 uint32_t xalign, yalign, zalign;
248 unsigned i;
250 /* compute alignment */
251 if (!start_level) {
252 surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
253 }
254 /* the 32 alignment is for scanout, cb or db but to allow texture to be
255 * easily bound as such we force this alignment to all surface
256 */
257 xalign = MAX2(1, surf_man->hw_info.group_bytes / surf->bpe);
258 yalign = 1;
259 zalign = 1;
260 if (surf->flags & RADEON_SURF_SCANOUT) {
261 xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
262 }
264 /* build mipmap tree */
265 for (i = start_level; i <= surf->last_level; i++) {
266 surf->level[i].mode = RADEON_SURF_MODE_LINEAR;
267 surf_minify(surf, i, xalign, yalign, zalign, offset);
268 /* level0 and first mipmap need to have alignment */
269 offset = surf->bo_size;
270 if ((i == 0)) {
271 offset = ALIGN(offset, surf->bo_alignment);
272 }
273 }
274 return 0;
275 }
277 static int r6_surface_init_linear_aligned(struct radeon_surface_manager *surf_man,
278 struct radeon_surface *surf,
279 uint64_t offset, unsigned start_level)
280 {
281 uint32_t xalign, yalign, zalign;
282 unsigned i;
284 /* compute alignment */
285 if (!start_level) {
286 surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
287 }
288 xalign = MAX2(64, surf_man->hw_info.group_bytes / surf->bpe);
289 yalign = 1;
290 zalign = 1;
292 /* build mipmap tree */
293 for (i = start_level; i <= surf->last_level; i++) {
294 surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
295 surf_minify(surf, i, xalign, yalign, zalign, offset);
296 /* level0 and first mipmap need to have alignment */
297 offset = surf->bo_size;
298 if ((i == 0)) {
299 offset = ALIGN(offset, surf->bo_alignment);
300 }
301 }
302 return 0;
303 }
305 static int r6_surface_init_1d(struct radeon_surface_manager *surf_man,
306 struct radeon_surface *surf,
307 uint64_t offset, unsigned start_level)
308 {
309 uint32_t xalign, yalign, zalign, tilew;
310 unsigned i;
312 /* compute alignment */
313 tilew = 8;
314 xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
315 xalign = MAX2(tilew, xalign);
316 yalign = tilew;
317 zalign = 1;
318 if (surf->flags & RADEON_SURF_SCANOUT) {
319 xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
320 }
321 if (!start_level) {
322 surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
323 }
325 /* build mipmap tree */
326 for (i = start_level; i <= surf->last_level; i++) {
327 surf->level[i].mode = RADEON_SURF_MODE_1D;
328 surf_minify(surf, i, xalign, yalign, zalign, offset);
329 /* level0 and first mipmap need to have alignment */
330 offset = surf->bo_size;
331 if ((i == 0)) {
332 offset = ALIGN(offset, surf->bo_alignment);
333 }
334 }
335 return 0;
336 }
338 static int r6_surface_init_2d(struct radeon_surface_manager *surf_man,
339 struct radeon_surface *surf,
340 uint64_t offset, unsigned start_level)
341 {
342 uint32_t xalign, yalign, zalign, tilew;
343 unsigned i;
345 /* compute alignment */
346 tilew = 8;
347 zalign = 1;
348 xalign = (surf_man->hw_info.group_bytes * surf_man->hw_info.num_banks) /
349 (tilew * surf->bpe * surf->nsamples);
350 xalign = MAX2(tilew * surf_man->hw_info.num_banks, xalign);
351 yalign = tilew * surf_man->hw_info.num_pipes;
352 if (surf->flags & RADEON_SURF_SCANOUT) {
353 xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
354 }
355 if (!start_level) {
356 surf->bo_alignment =
357 MAX2(surf_man->hw_info.num_pipes *
358 surf_man->hw_info.num_banks *
359 surf->nsamples * surf->bpe * 64,
360 xalign * yalign * surf->nsamples * surf->bpe);
361 }
363 /* build mipmap tree */
364 for (i = start_level; i <= surf->last_level; i++) {
365 surf->level[i].mode = RADEON_SURF_MODE_2D;
366 surf_minify(surf, i, xalign, yalign, zalign, offset);
367 if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
368 return r6_surface_init_1d(surf_man, surf, offset, i);
369 }
370 /* level0 and first mipmap need to have alignment */
371 offset = surf->bo_size;
372 if ((i == 0)) {
373 offset = ALIGN(offset, surf->bo_alignment);
374 }
375 }
376 return 0;
377 }
379 static int r6_surface_init(struct radeon_surface_manager *surf_man,
380 struct radeon_surface *surf)
381 {
382 unsigned mode;
383 int r;
385 /* MSAA surfaces support the 2D mode only. */
386 if (surf->nsamples > 1) {
387 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
388 surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE);
389 }
391 /* tiling mode */
392 mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
394 if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
395 /* zbuffer only support 1D or 2D tiled surface */
396 switch (mode) {
397 case RADEON_SURF_MODE_1D:
398 case RADEON_SURF_MODE_2D:
399 break;
400 default:
401 mode = RADEON_SURF_MODE_1D;
402 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
403 surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
404 break;
405 }
406 }
408 /* force 1d on kernel that can't do 2d */
409 if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
410 if (surf->nsamples > 1) {
411 fprintf(stderr, "radeon: Cannot use 2D tiling for an MSAA surface (%i).\n", __LINE__);
412 return -EFAULT;
413 }
414 mode = RADEON_SURF_MODE_1D;
415 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
416 surf->flags |= RADEON_SURF_SET(mode, MODE);
417 }
419 /* check surface dimension */
420 if (surf->npix_x > 8192 || surf->npix_y > 8192 || surf->npix_z > 8192) {
421 return -EINVAL;
422 }
424 /* check mipmap last_level */
425 if (surf->last_level > 14) {
426 return -EINVAL;
427 }
429 /* check tiling mode */
430 switch (mode) {
431 case RADEON_SURF_MODE_LINEAR:
432 r = r6_surface_init_linear(surf_man, surf, 0, 0);
433 break;
434 case RADEON_SURF_MODE_LINEAR_ALIGNED:
435 r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
436 break;
437 case RADEON_SURF_MODE_1D:
438 r = r6_surface_init_1d(surf_man, surf, 0, 0);
439 break;
440 case RADEON_SURF_MODE_2D:
441 r = r6_surface_init_2d(surf_man, surf, 0, 0);
442 break;
443 default:
444 return -EINVAL;
445 }
446 return r;
447 }
449 static int r6_surface_best(struct radeon_surface_manager *surf_man,
450 struct radeon_surface *surf)
451 {
452 /* no value to optimize for r6xx/r7xx */
453 return 0;
454 }
457 /* ===========================================================================
458 * evergreen family
459 */
460 static int eg_init_hw_info(struct radeon_surface_manager *surf_man)
461 {
462 uint32_t tiling_config;
463 drmVersionPtr version;
464 int r;
466 r = radeon_get_value(surf_man->fd, RADEON_INFO_TILING_CONFIG,
467 &tiling_config);
468 if (r) {
469 return r;
470 }
472 surf_man->hw_info.allow_2d = 0;
473 version = drmGetVersion(surf_man->fd);
474 if (version && version->version_minor >= 16) {
475 surf_man->hw_info.allow_2d = 1;
476 }
477 drmFreeVersion(version);
479 switch (tiling_config & 0xf) {
480 case 0:
481 surf_man->hw_info.num_pipes = 1;
482 break;
483 case 1:
484 surf_man->hw_info.num_pipes = 2;
485 break;
486 case 2:
487 surf_man->hw_info.num_pipes = 4;
488 break;
489 case 3:
490 surf_man->hw_info.num_pipes = 8;
491 break;
492 default:
493 surf_man->hw_info.num_pipes = 8;
494 surf_man->hw_info.allow_2d = 0;
495 break;
496 }
498 switch ((tiling_config & 0xf0) >> 4) {
499 case 0:
500 surf_man->hw_info.num_banks = 4;
501 break;
502 case 1:
503 surf_man->hw_info.num_banks = 8;
504 break;
505 case 2:
506 surf_man->hw_info.num_banks = 16;
507 break;
508 default:
509 surf_man->hw_info.num_banks = 8;
510 surf_man->hw_info.allow_2d = 0;
511 break;
512 }
514 switch ((tiling_config & 0xf00) >> 8) {
515 case 0:
516 surf_man->hw_info.group_bytes = 256;
517 break;
518 case 1:
519 surf_man->hw_info.group_bytes = 512;
520 break;
521 default:
522 surf_man->hw_info.group_bytes = 256;
523 surf_man->hw_info.allow_2d = 0;
524 break;
525 }
527 switch ((tiling_config & 0xf000) >> 12) {
528 case 0:
529 surf_man->hw_info.row_size = 1024;
530 break;
531 case 1:
532 surf_man->hw_info.row_size = 2048;
533 break;
534 case 2:
535 surf_man->hw_info.row_size = 4096;
536 break;
537 default:
538 surf_man->hw_info.row_size = 4096;
539 surf_man->hw_info.allow_2d = 0;
540 break;
541 }
542 return 0;
543 }
545 static void eg_surf_minify(struct radeon_surface *surf,
546 unsigned level,
547 unsigned slice_pt,
548 unsigned mtilew,
549 unsigned mtileh,
550 unsigned mtileb,
551 unsigned offset)
552 {
553 unsigned mtile_pr, mtile_ps;
555 surf->level[level].npix_x = mip_minify(surf->npix_x, level);
556 surf->level[level].npix_y = mip_minify(surf->npix_y, level);
557 surf->level[level].npix_z = mip_minify(surf->npix_z, level);
558 surf->level[level].nblk_x = (surf->level[level].npix_x + surf->blk_w - 1) / surf->blk_w;
559 surf->level[level].nblk_y = (surf->level[level].npix_y + surf->blk_h - 1) / surf->blk_h;
560 surf->level[level].nblk_z = (surf->level[level].npix_z + surf->blk_d - 1) / surf->blk_d;
561 if (surf->nsamples == 1 && surf->level[level].mode == RADEON_SURF_MODE_2D) {
562 if (surf->level[level].nblk_x < mtilew || surf->level[level].nblk_y < mtileh) {
563 surf->level[level].mode = RADEON_SURF_MODE_1D;
564 return;
565 }
566 }
567 surf->level[level].nblk_x = ALIGN(surf->level[level].nblk_x, mtilew);
568 surf->level[level].nblk_y = ALIGN(surf->level[level].nblk_y, mtileh);
569 surf->level[level].nblk_z = ALIGN(surf->level[level].nblk_z, 1);
571 /* macro tile per row */
572 mtile_pr = surf->level[level].nblk_x / mtilew;
573 /* macro tile per slice */
574 mtile_ps = (mtile_pr * surf->level[level].nblk_y) / mtileh;
576 surf->level[level].offset = offset;
577 surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe * slice_pt;
578 surf->level[level].slice_size = mtile_ps * mtileb * slice_pt;
580 surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
581 }
583 static int eg_surface_init_1d(struct radeon_surface_manager *surf_man,
584 struct radeon_surface *surf,
585 uint64_t offset, unsigned start_level)
586 {
587 uint32_t xalign, yalign, zalign, tilew;
588 unsigned i;
590 /* compute alignment */
591 tilew = 8;
592 xalign = surf_man->hw_info.group_bytes / (tilew * surf->bpe * surf->nsamples);
593 if (surf->flags & RADEON_SURF_SBUFFER) {
594 xalign = surf_man->hw_info.group_bytes / (tilew * surf->nsamples);
595 }
596 xalign = MAX2(tilew, xalign);
597 yalign = tilew;
598 zalign = 1;
599 if (surf->flags & RADEON_SURF_SCANOUT) {
600 xalign = MAX2((surf->bpe == 1) ? 64 : 32, xalign);
601 }
602 if (!start_level) {
603 surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
604 }
606 /* build mipmap tree */
607 for (i = start_level; i <= surf->last_level; i++) {
608 surf->level[i].mode = RADEON_SURF_MODE_1D;
609 surf_minify(surf, i, xalign, yalign, zalign, offset);
610 /* level0 and first mipmap need to have alignment */
611 offset = surf->bo_size;
612 if ((i == 0)) {
613 offset = ALIGN(offset, surf->bo_alignment);
614 }
615 }
617 /* The depth and stencil buffers are in separate resources on evergreen.
618 * We allocate them in one buffer next to each other to simplify
619 * communication between the DDX and the Mesa driver. */
620 if ((surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) ==
621 (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
622 surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
623 surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
624 }
626 return 0;
627 }
629 static int eg_surface_init_2d(struct radeon_surface_manager *surf_man,
630 struct radeon_surface *surf,
631 uint64_t offset, unsigned start_level)
632 {
633 unsigned tilew, tileh, tileb;
634 unsigned mtilew, mtileh, mtileb;
635 unsigned slice_pt;
636 unsigned i;
638 /* compute tile values */
639 tilew = 8;
640 tileh = 8;
641 tileb = tilew * tileh * surf->bpe * surf->nsamples;
642 /* slices per tile */
643 slice_pt = 1;
644 if (tileb > surf->tile_split) {
645 slice_pt = tileb / surf->tile_split;
646 }
647 tileb = tileb / slice_pt;
649 /* macro tile width & height */
650 mtilew = (tilew * surf->bankw * surf_man->hw_info.num_pipes) * surf->mtilea;
651 mtileh = (tileh * surf->bankh * surf_man->hw_info.num_banks) / surf->mtilea;
652 /* macro tile bytes */
653 mtileb = (mtilew / tilew) * (mtileh / tileh) * tileb;
655 if (!start_level) {
656 surf->bo_alignment = MAX2(256, mtileb);
657 }
659 /* build mipmap tree */
660 for (i = start_level; i <= surf->last_level; i++) {
661 surf->level[i].mode = RADEON_SURF_MODE_2D;
662 eg_surf_minify(surf, i, slice_pt, mtilew, mtileh, mtileb, offset);
663 if (surf->level[i].mode == RADEON_SURF_MODE_1D) {
664 return eg_surface_init_1d(surf_man, surf, offset, i);
665 }
666 /* level0 and first mipmap need to have alignment */
667 offset = surf->bo_size;
668 if ((i == 0)) {
669 offset = ALIGN(offset, surf->bo_alignment);
670 }
671 }
673 if ((surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) ==
674 (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
675 surf->stencil_offset = ALIGN(surf->bo_size, surf->bo_alignment);
676 surf->bo_size = surf->stencil_offset + surf->bo_size / 4;
677 }
679 return 0;
680 }
682 static int eg_surface_sanity(struct radeon_surface_manager *surf_man,
683 struct radeon_surface *surf,
684 unsigned mode)
685 {
686 unsigned tileb;
688 /* check surface dimension */
689 if (surf->npix_x > 16384 || surf->npix_y > 16384 || surf->npix_z > 16384) {
690 return -EINVAL;
691 }
693 /* check mipmap last_level */
694 if (surf->last_level > 15) {
695 return -EINVAL;
696 }
698 /* force 1d on kernel that can't do 2d */
699 if (!surf_man->hw_info.allow_2d && mode > RADEON_SURF_MODE_1D) {
700 if (surf->nsamples > 1) {
701 fprintf(stderr, "radeon: Cannot use 2D tiling for an MSAA surface (%i).\n", __LINE__);
702 return -EFAULT;
703 }
704 mode = RADEON_SURF_MODE_1D;
705 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
706 surf->flags |= RADEON_SURF_SET(mode, MODE);
707 }
709 /* check tile split */
710 if (mode == RADEON_SURF_MODE_2D) {
711 switch (surf->tile_split) {
712 case 64:
713 case 128:
714 case 256:
715 case 512:
716 case 1024:
717 case 2048:
718 case 4096:
719 break;
720 default:
721 return -EINVAL;
722 }
723 switch (surf->mtilea) {
724 case 1:
725 case 2:
726 case 4:
727 case 8:
728 break;
729 default:
730 return -EINVAL;
731 }
732 /* check aspect ratio */
733 if (surf_man->hw_info.num_banks < surf->mtilea) {
734 return -EINVAL;
735 }
736 /* check bank width */
737 switch (surf->bankw) {
738 case 1:
739 case 2:
740 case 4:
741 case 8:
742 break;
743 default:
744 return -EINVAL;
745 }
746 /* check bank height */
747 switch (surf->bankh) {
748 case 1:
749 case 2:
750 case 4:
751 case 8:
752 break;
753 default:
754 return -EINVAL;
755 }
756 tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
757 if ((tileb * surf->bankh * surf->bankw) < surf_man->hw_info.group_bytes) {
758 return -EINVAL;
759 }
760 }
762 return 0;
763 }
765 static int eg_surface_init(struct radeon_surface_manager *surf_man,
766 struct radeon_surface *surf)
767 {
768 unsigned mode;
769 int r;
771 /* MSAA surfaces support the 2D mode only. */
772 if (surf->nsamples > 1) {
773 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
774 surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE);
775 }
777 /* tiling mode */
778 mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
780 if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
781 /* zbuffer only support 1D or 2D tiled surface */
782 switch (mode) {
783 case RADEON_SURF_MODE_1D:
784 case RADEON_SURF_MODE_2D:
785 break;
786 default:
787 mode = RADEON_SURF_MODE_1D;
788 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
789 surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
790 break;
791 }
792 }
794 r = eg_surface_sanity(surf_man, surf, mode);
795 if (r) {
796 return r;
797 }
799 surf->stencil_offset = 0;
800 surf->stencil_tile_split = 0;
802 /* check tiling mode */
803 switch (mode) {
804 case RADEON_SURF_MODE_LINEAR:
805 r = r6_surface_init_linear(surf_man, surf, 0, 0);
806 break;
807 case RADEON_SURF_MODE_LINEAR_ALIGNED:
808 r = r6_surface_init_linear_aligned(surf_man, surf, 0, 0);
809 break;
810 case RADEON_SURF_MODE_1D:
811 r = eg_surface_init_1d(surf_man, surf, 0, 0);
812 break;
813 case RADEON_SURF_MODE_2D:
814 r = eg_surface_init_2d(surf_man, surf, 0, 0);
815 break;
816 default:
817 return -EINVAL;
818 }
819 return r;
820 }
822 static unsigned log2_int(unsigned x)
823 {
824 unsigned l;
826 if (x < 2) {
827 return 0;
828 }
829 for (l = 2; ; l++) {
830 if ((unsigned)(1 << l) > x) {
831 return l - 1;
832 }
833 }
834 return 0;
835 }
837 /* compute best tile_split, bankw, bankh, mtilea
838 * depending on surface
839 */
840 static int eg_surface_best(struct radeon_surface_manager *surf_man,
841 struct radeon_surface *surf)
842 {
843 unsigned mode, tileb, h_over_w;
844 int r;
846 /* tiling mode */
847 mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
849 /* set some default value to avoid sanity check choking on them */
850 surf->tile_split = 1024;
851 surf->bankw = 1;
852 surf->bankh = 1;
853 surf->mtilea = surf_man->hw_info.num_banks;
854 tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
855 for (; surf->bankh <= 8; surf->bankh *= 2) {
856 if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
857 break;
858 }
859 }
860 if (surf->mtilea > 8) {
861 surf->mtilea = 8;
862 }
864 r = eg_surface_sanity(surf_man, surf, mode);
865 if (r) {
866 return r;
867 }
869 if (mode != RADEON_SURF_MODE_2D) {
870 /* nothing to do for non 2D tiled surface */
871 return 0;
872 }
874 /* Tweak TILE_SPLIT for performance here. */
875 if (surf->nsamples > 1) {
876 if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
877 switch (surf->nsamples) {
878 case 2:
879 surf->tile_split = 128;
880 break;
881 case 4:
882 surf->tile_split = 128;
883 break;
884 case 8:
885 surf->tile_split = 256;
886 break;
887 case 16: /* cayman only */
888 surf->tile_split = 512;
889 break;
890 default:
891 fprintf(stderr, "radeon: Wrong number of samples %i (%i)\n",
892 surf->nsamples, __LINE__);
893 return -EINVAL;
894 }
895 surf->stencil_tile_split = 64;
896 } else {
897 /* tile split must be >= 256 for colorbuffer surfaces */
898 surf->tile_split = MAX2(surf->nsamples * surf->bpe * 64, 256);
899 }
900 } else {
901 /* set tile split to row size */
902 surf->tile_split = surf_man->hw_info.row_size;
903 surf->stencil_tile_split = surf_man->hw_info.row_size / 2;
904 }
906 /* bankw or bankh greater than 1 increase alignment requirement, not
907 * sure if it's worth using smaller bankw & bankh to stick with 2D
908 * tiling on small surface rather than falling back to 1D tiling.
909 * Use recommanded value based on tile size for now.
910 *
911 * fmask buffer has different optimal value figure them out once we
912 * use it.
913 */
914 if (surf->flags & RADEON_SURF_SBUFFER) {
915 /* assume 1 bytes for stencil, we optimize for stencil as stencil
916 * and depth shares surface values
917 */
918 tileb = MIN2(surf->tile_split, 64 * surf->nsamples);
919 } else {
920 tileb = MIN2(surf->tile_split, 64 * surf->bpe * surf->nsamples);
921 }
923 /* use bankw of 1 to minimize width alignment, might be interesting to
924 * increase it for large surface
925 */
926 surf->bankw = 1;
927 switch (tileb) {
928 case 64:
929 surf->bankh = 4;
930 break;
931 case 128:
932 case 256:
933 surf->bankh = 2;
934 break;
935 default:
936 surf->bankh = 1;
937 break;
938 }
939 /* double check the constraint */
940 for (; surf->bankh <= 8; surf->bankh *= 2) {
941 if ((tileb * surf->bankh * surf->bankw) >= surf_man->hw_info.group_bytes) {
942 break;
943 }
944 }
946 h_over_w = (((surf->bankh * surf_man->hw_info.num_banks) << 16) /
947 (surf->bankw * surf_man->hw_info.num_pipes)) >> 16;
948 surf->mtilea = 1 << (log2_int(h_over_w) >> 1);
950 return 0;
951 }
954 /* ===========================================================================
955 * Southern Islands family
956 */
958 static void si_surf_minify_linear_aligned(struct radeon_surface *surf,
959 unsigned level,
960 uint32_t xalign, uint32_t yalign, uint32_t zalign, uint32_t slice_align,
961 unsigned offset)
962 {
963 surf->level[level].npix_x = mip_minify(surf->npix_x, level);
964 surf->level[level].npix_y = mip_minify(surf->npix_y, level);
965 surf->level[level].npix_z = mip_minify(surf->npix_z, level);
967 if (level == 0 && surf->last_level > 0) {
968 surf->level[level].nblk_x = (next_power_of_two(surf->level[level].npix_x) + surf->blk_w - 1) / surf->blk_w;
969 surf->level[level].nblk_y = (next_power_of_two(surf->level[level].npix_y) + surf->blk_h - 1) / surf->blk_h;
970 surf->level[level].nblk_z = (next_power_of_two(surf->level[level].npix_z) + surf->blk_d - 1) / surf->blk_d;
971 } else {
972 surf->level[level].nblk_x = (surf->level[level].npix_x + surf->blk_w - 1) / surf->blk_w;
973 surf->level[level].nblk_y = (surf->level[level].npix_y + surf->blk_h - 1) / surf->blk_h;
974 surf->level[level].nblk_z = (surf->level[level].npix_z + surf->blk_d - 1) / surf->blk_d;
975 }
977 /* XXX: Texture sampling uses unexpectedly large pitches in some cases,
978 * these are just guesses for the rules behind those
979 */
980 if (level == 0 && surf->last_level == 0)
981 /* Non-mipmap pitch padded to slice alignment */
982 xalign = MAX2(xalign, slice_align / surf->bpe);
983 else
984 /* Small rows evenly distributed across slice */
985 xalign = MAX2(xalign, slice_align / surf->bpe / surf->level[level].npix_y);
987 surf->level[level].nblk_x = ALIGN(surf->level[level].nblk_x, xalign);
988 surf->level[level].nblk_y = ALIGN(surf->level[level].nblk_y, yalign);
989 surf->level[level].nblk_z = ALIGN(surf->level[level].nblk_z, zalign);
991 surf->level[level].offset = offset;
992 surf->level[level].pitch_bytes = surf->level[level].nblk_x * surf->bpe * surf->nsamples;
993 surf->level[level].slice_size = ALIGN(surf->level[level].pitch_bytes * surf->level[level].nblk_y, slice_align);
995 surf->bo_size = offset + surf->level[level].slice_size * surf->level[level].nblk_z * surf->array_size;
996 }
998 static int si_surface_init_linear_aligned(struct radeon_surface_manager *surf_man,
999 struct radeon_surface *surf,
1000 uint64_t offset, unsigned start_level)
1001 {
1002 uint32_t xalign, yalign, zalign, slice_align;
1003 unsigned i;
1005 /* compute alignment */
1006 if (!start_level) {
1007 surf->bo_alignment = MAX2(256, surf_man->hw_info.group_bytes);
1008 }
1009 xalign = MAX2(8, 64 / surf->bpe);
1010 yalign = 1;
1011 zalign = 1;
1012 slice_align = MAX2(64 * surf->bpe, surf_man->hw_info.group_bytes);
1014 /* build mipmap tree */
1015 for (i = start_level; i <= surf->last_level; i++) {
1016 surf->level[i].mode = RADEON_SURF_MODE_LINEAR_ALIGNED;
1017 si_surf_minify_linear_aligned(surf, i, xalign, yalign, zalign, slice_align, offset);
1018 /* level0 and first mipmap need to have alignment */
1019 offset = surf->bo_size;
1020 if ((i == 0)) {
1021 offset = ALIGN(offset, surf->bo_alignment);
1022 }
1023 }
1024 return 0;
1025 }
1027 static int si_surface_init(struct radeon_surface_manager *surf_man,
1028 struct radeon_surface *surf)
1029 {
1030 unsigned mode;
1031 int r;
1033 /* MSAA surfaces support the 2D mode only. */
1034 if (surf->nsamples > 1) {
1035 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
1036 surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE);
1037 }
1039 /* tiling mode */
1040 mode = (surf->flags >> RADEON_SURF_MODE_SHIFT) & RADEON_SURF_MODE_MASK;
1042 if (surf->flags & (RADEON_SURF_ZBUFFER | RADEON_SURF_SBUFFER)) {
1043 /* zbuffer only support 1D or 2D tiled surface */
1044 switch (mode) {
1045 case RADEON_SURF_MODE_1D:
1046 case RADEON_SURF_MODE_2D:
1047 break;
1048 default:
1049 mode = RADEON_SURF_MODE_1D;
1050 surf->flags = RADEON_SURF_CLR(surf->flags, MODE);
1051 surf->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
1052 break;
1053 }
1054 }
1056 r = eg_surface_sanity(surf_man, surf, mode);
1057 if (r) {
1058 return r;
1059 }
1061 surf->stencil_offset = 0;
1062 surf->stencil_tile_split = 0;
1064 /* check tiling mode */
1065 switch (mode) {
1066 case RADEON_SURF_MODE_LINEAR:
1067 r = r6_surface_init_linear(surf_man, surf, 0, 0);
1068 break;
1069 case RADEON_SURF_MODE_LINEAR_ALIGNED:
1070 r = si_surface_init_linear_aligned(surf_man, surf, 0, 0);
1071 break;
1072 case RADEON_SURF_MODE_1D:
1073 r = eg_surface_init_1d(surf_man, surf, 0, 0);
1074 break;
1075 case RADEON_SURF_MODE_2D:
1076 r = eg_surface_init_2d(surf_man, surf, 0, 0);
1077 break;
1078 default:
1079 return -EINVAL;
1080 }
1081 return r;
1082 }
1084 /* ===========================================================================
1085 * public API
1086 */
1087 struct radeon_surface_manager *radeon_surface_manager_new(int fd)
1088 {
1089 struct radeon_surface_manager *surf_man;
1091 surf_man = calloc(1, sizeof(struct radeon_surface_manager));
1092 if (surf_man == NULL) {
1093 return NULL;
1094 }
1095 surf_man->fd = fd;
1096 if (radeon_get_value(fd, RADEON_INFO_DEVICE_ID, &surf_man->device_id)) {
1097 goto out_err;
1098 }
1099 if (radeon_get_family(surf_man)) {
1100 goto out_err;
1101 }
1103 if (surf_man->family <= CHIP_RV740) {
1104 if (r6_init_hw_info(surf_man)) {
1105 goto out_err;
1106 }
1107 surf_man->surface_init = &r6_surface_init;
1108 surf_man->surface_best = &r6_surface_best;
1109 } else {
1110 if (eg_init_hw_info(surf_man)) {
1111 goto out_err;
1112 }
1113 if (surf_man->family <= CHIP_ARUBA) {
1114 surf_man->surface_init = &eg_surface_init;
1115 } else {
1116 surf_man->surface_init = &si_surface_init;
1117 }
1118 surf_man->surface_best = &eg_surface_best;
1119 }
1121 return surf_man;
1122 out_err:
1123 free(surf_man);
1124 return NULL;
1125 }
1127 void radeon_surface_manager_free(struct radeon_surface_manager *surf_man)
1128 {
1129 free(surf_man);
1130 }
1132 static int radeon_surface_sanity(struct radeon_surface_manager *surf_man,
1133 struct radeon_surface *surf,
1134 unsigned type,
1135 unsigned mode)
1136 {
1137 if (surf_man == NULL || surf_man->surface_init == NULL || surf == NULL) {
1138 return -EINVAL;
1139 }
1141 /* all dimension must be at least 1 ! */
1142 if (!surf->npix_x || !surf->npix_y || !surf->npix_z) {
1143 return -EINVAL;
1144 }
1145 if (!surf->blk_w || !surf->blk_h || !surf->blk_d) {
1146 return -EINVAL;
1147 }
1148 if (!surf->array_size) {
1149 return -EINVAL;
1150 }
1151 /* array size must be a power of 2 */
1152 surf->array_size = next_power_of_two(surf->array_size);
1154 switch (surf->nsamples) {
1155 case 1:
1156 case 2:
1157 case 4:
1158 case 8:
1159 break;
1160 default:
1161 return -EINVAL;
1162 }
1163 /* check type */
1164 switch (type) {
1165 case RADEON_SURF_TYPE_1D:
1166 if (surf->npix_y > 1) {
1167 return -EINVAL;
1168 }
1169 case RADEON_SURF_TYPE_2D:
1170 if (surf->npix_z > 1) {
1171 return -EINVAL;
1172 }
1173 break;
1174 case RADEON_SURF_TYPE_CUBEMAP:
1175 if (surf->npix_z > 1) {
1176 return -EINVAL;
1177 }
1178 /* deal with cubemap as they were texture array */
1179 if (surf_man->family >= CHIP_RV770) {
1180 surf->array_size = 8;
1181 } else {
1182 surf->array_size = 6;
1183 }
1184 break;
1185 case RADEON_SURF_TYPE_3D:
1186 break;
1187 case RADEON_SURF_TYPE_1D_ARRAY:
1188 if (surf->npix_y > 1) {
1189 return -EINVAL;
1190 }
1191 case RADEON_SURF_TYPE_2D_ARRAY:
1192 break;
1193 default:
1194 return -EINVAL;
1195 }
1196 return 0;
1197 }
1199 int radeon_surface_init(struct radeon_surface_manager *surf_man,
1200 struct radeon_surface *surf)
1201 {
1202 unsigned mode, type;
1203 int r;
1205 type = RADEON_SURF_GET(surf->flags, TYPE);
1206 mode = RADEON_SURF_GET(surf->flags, MODE);
1208 r = radeon_surface_sanity(surf_man, surf, type, mode);
1209 if (r) {
1210 return r;
1211 }
1212 return surf_man->surface_init(surf_man, surf);
1213 }
1215 int radeon_surface_best(struct radeon_surface_manager *surf_man,
1216 struct radeon_surface *surf)
1217 {
1218 unsigned mode, type;
1219 int r;
1221 type = RADEON_SURF_GET(surf->flags, TYPE);
1222 mode = RADEON_SURF_GET(surf->flags, MODE);
1224 r = radeon_surface_sanity(surf_man, surf, type, mode);
1225 if (r) {
1226 return r;
1227 }
1228 return surf_man->surface_best(surf_man, surf);
1229 }