radeon_bo_helper.c revision de2362d3
1/*
2 * Copyright 2012  Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include "radeon.h"
28
29#ifdef RADEON_PIXMAP_SHARING
30#include "radeon_bo_gem.h"
31#endif
32
33static const unsigned MicroBlockTable[5][3][2] = {
34    /*linear  tiled   square-tiled */
35    {{32, 1}, {8, 4}, {0, 0}}, /*   8 bits per pixel */
36    {{16, 1}, {8, 2}, {4, 4}}, /*  16 bits per pixel */
37    {{ 8, 1}, {4, 2}, {0, 0}}, /*  32 bits per pixel */
38    {{ 4, 1}, {0, 0}, {2, 2}}, /*  64 bits per pixel */
39    {{ 2, 1}, {0, 0}, {0, 0}}  /* 128 bits per pixel */
40};
41
42/* Return true if macrotiling can be enabled */
43static Bool RADEONMacroSwitch(int width, int height, int bpp,
44                              uint32_t flags, Bool rv350_mode)
45{
46    unsigned tilew, tileh, microtiled, logbpp;
47
48    logbpp = RADEONLog2(bpp / 8);
49    if (logbpp > 4)
50        return 0;
51
52    microtiled = !!(flags & RADEON_TILING_MICRO);
53    tilew = MicroBlockTable[logbpp][microtiled][0] * 8;
54    tileh = MicroBlockTable[logbpp][microtiled][1] * 8;
55
56    /* See TX_FILTER1_n.MACRO_SWITCH. */
57    if (rv350_mode) {
58        return width >= tilew && height >= tileh;
59    } else {
60        return width > tilew && height > tileh;
61    }
62}
63
64/* Calculate appropriate tiling and pitch for a pixmap and allocate a BO that
65 * can hold it.
66 */
67struct radeon_bo*
68radeon_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width, int height, int depth,
69		       int usage_hint, int bitsPerPixel, int *new_pitch,
70		       struct radeon_surface *new_surface, uint32_t *new_tiling)
71{
72    RADEONInfoPtr info = RADEONPTR(pScrn);
73    int pitch, base_align;
74    uint32_t size, heighta;
75    int cpp = bitsPerPixel / 8;
76    uint32_t tiling = 0;
77    struct radeon_surface surface;
78    struct radeon_bo *bo;
79    int domain = RADEON_GEM_DOMAIN_VRAM;
80    if (usage_hint) {
81	if (info->allowColorTiling) {
82	    if (usage_hint & RADEON_CREATE_PIXMAP_TILING_MACRO)
83		tiling |= RADEON_TILING_MACRO;
84	    if (usage_hint & RADEON_CREATE_PIXMAP_TILING_MICRO)
85                tiling |= RADEON_TILING_MICRO;
86	}
87	if (usage_hint & RADEON_CREATE_PIXMAP_DEPTH)
88		tiling |= RADEON_TILING_MACRO | RADEON_TILING_MICRO;
89
90#ifdef CREATE_PIXMAP_USAGE_SHARED
91	if ((usage_hint & 0xffff) == CREATE_PIXMAP_USAGE_SHARED) {
92		tiling = 0;
93		domain = RADEON_GEM_DOMAIN_GTT;
94	}
95#endif
96    }
97
98    /* Small pixmaps must not be macrotiled on R300, hw cannot sample them
99     * correctly because samplers automatically switch to macrolinear. */
100    if (info->ChipFamily >= CHIP_FAMILY_R300 &&
101        info->ChipFamily <= CHIP_FAMILY_RS740 &&
102        (tiling & RADEON_TILING_MACRO) &&
103        !RADEONMacroSwitch(width, height, bitsPerPixel, tiling,
104                           info->ChipFamily >= CHIP_FAMILY_RV350)) {
105        tiling &= ~RADEON_TILING_MACRO;
106    }
107
108    heighta = RADEON_ALIGN(height, drmmode_get_height_align(pScrn, tiling));
109    pitch = RADEON_ALIGN(width, drmmode_get_pitch_align(pScrn, cpp, tiling)) * cpp;
110    base_align = drmmode_get_base_align(pScrn, cpp, tiling);
111    size = RADEON_ALIGN(heighta * pitch, RADEON_GPU_PAGE_SIZE);
112    memset(&surface, 0, sizeof(struct radeon_surface));
113
114    if (info->ChipFamily >= CHIP_FAMILY_R600 && info->surf_man) {
115		if (width) {
116			surface.npix_x = width;
117			/* need to align height to 8 for old kernel */
118			surface.npix_y = RADEON_ALIGN(height, 8);
119			surface.npix_z = 1;
120			surface.blk_w = 1;
121			surface.blk_h = 1;
122			surface.blk_d = 1;
123			surface.array_size = 1;
124			surface.last_level = 0;
125			surface.bpe = cpp;
126			surface.nsamples = 1;
127			if (height < 128) {
128				/* disable 2d tiling for small surface to work around
129				 * the fact that ddx align height to 8 pixel for old
130				 * obscure reason i can't remember
131				 */
132				tiling &= ~RADEON_TILING_MACRO;
133			}
134			surface.flags = RADEON_SURF_SCANOUT;
135			/* we are requiring a recent enough libdrm version */
136			surface.flags |= RADEON_SURF_HAS_TILE_MODE_INDEX;
137			surface.flags |= RADEON_SURF_SET(RADEON_SURF_TYPE_2D, TYPE);
138			surface.flags |= RADEON_SURF_SET(RADEON_SURF_MODE_LINEAR, MODE);
139			if ((tiling & RADEON_TILING_MICRO)) {
140				surface.flags = RADEON_SURF_CLR(surface.flags, MODE);
141				surface.flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
142			}
143			if ((tiling & RADEON_TILING_MACRO)) {
144				surface.flags = RADEON_SURF_CLR(surface.flags, MODE);
145				surface.flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE);
146			}
147			if (usage_hint & RADEON_CREATE_PIXMAP_SZBUFFER) {
148				surface.flags |= RADEON_SURF_ZBUFFER;
149				surface.flags |= RADEON_SURF_SBUFFER;
150			}
151			if (radeon_surface_best(info->surf_man, &surface)) {
152				return NULL;
153			}
154			if (radeon_surface_init(info->surf_man, &surface)) {
155				return NULL;
156			}
157			size = surface.bo_size;
158			base_align = surface.bo_alignment;
159			pitch = surface.level[0].pitch_bytes;
160			tiling = 0;
161			switch (surface.level[0].mode) {
162			case RADEON_SURF_MODE_2D:
163				tiling |= RADEON_TILING_MACRO;
164				tiling |= surface.bankw << RADEON_TILING_EG_BANKW_SHIFT;
165				tiling |= surface.bankh << RADEON_TILING_EG_BANKH_SHIFT;
166				tiling |= surface.mtilea << RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT;
167				tiling |= eg_tile_split(surface.tile_split) << RADEON_TILING_EG_TILE_SPLIT_SHIFT;
168				tiling |= eg_tile_split(surface.stencil_tile_split) << RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT;
169				break;
170			case RADEON_SURF_MODE_1D:
171				tiling |= RADEON_TILING_MICRO;
172				break;
173			default:
174				break;
175			}
176		}
177	}
178
179    bo = radeon_bo_open(info->bufmgr, 0, size, base_align,
180			domain, 0);
181
182    if (bo && tiling && radeon_bo_set_tiling(bo, tiling, pitch) == 0)
183	*new_tiling = tiling;
184
185    *new_surface = surface;
186    *new_pitch = pitch;
187    return bo;
188}
189
190#ifdef RADEON_PIXMAP_SHARING
191
192Bool radeon_share_pixmap_backing(struct radeon_bo *bo, void **handle_p)
193{
194    int handle;
195
196    if (radeon_gem_prime_share_bo(bo, &handle) != 0)
197	return FALSE;
198
199    *handle_p = (void *)(long)handle;
200    return TRUE;
201}
202
203Bool radeon_set_shared_pixmap_backing(PixmapPtr ppix, void *fd_handle,
204				      struct radeon_surface *surface)
205{
206    ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen);
207    RADEONInfoPtr info = RADEONPTR(pScrn);
208    struct radeon_bo *bo;
209    int ihandle = (int)(long)fd_handle;
210    uint32_t size = ppix->devKind * ppix->drawable.height;
211
212    bo = radeon_gem_bo_open_prime(info->bufmgr, ihandle, size);
213    if (!bo)
214        return FALSE;
215
216    memset(surface, 0, sizeof(struct radeon_surface));
217
218    if (info->ChipFamily >= CHIP_FAMILY_R600 && info->surf_man) {
219
220	surface->npix_x = ppix->drawable.width;
221	surface->npix_y = ppix->drawable.height;
222	surface->npix_z = 1;
223	surface->blk_w = 1;
224	surface->blk_h = 1;
225	surface->blk_d = 1;
226	surface->array_size = 1;
227	surface->bpe = ppix->drawable.bitsPerPixel / 8;
228	surface->nsamples = 1;
229	/* we are requiring a recent enough libdrm version */
230	surface->flags |= RADEON_SURF_HAS_TILE_MODE_INDEX;
231	surface->flags |= RADEON_SURF_SET(RADEON_SURF_TYPE_2D, TYPE);
232	surface->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_LINEAR, MODE);
233	if (radeon_surface_best(info->surf_man, surface)) {
234	    return FALSE;
235	}
236	if (radeon_surface_init(info->surf_man, surface)) {
237	    return FALSE;
238	}
239	/* we have to post hack the surface to reflect the actual size
240	   of the shared pixmap */
241	surface->level[0].pitch_bytes = ppix->devKind;
242	surface->level[0].nblk_x = ppix->devKind / surface->bpe;
243    }
244    radeon_set_pixmap_bo(ppix, bo);
245
246    close(ihandle);
247    /* we have a reference from the alloc and one from set pixmap bo,
248       drop one */
249    radeon_bo_unref(bo);
250    return TRUE;
251}
252
253#endif /* RADEON_PIXMAP_SHARING */
254