radeon_bo_helper.c revision 0d16fef4
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#include "radeon_glamor.h"
29
30#ifdef RADEON_PIXMAP_SHARING
31#include "radeon_bo_gem.h"
32#endif
33
34static const unsigned MicroBlockTable[5][3][2] = {
35    /*linear  tiled   square-tiled */
36    {{32, 1}, {8, 4}, {0, 0}}, /*   8 bits per pixel */
37    {{16, 1}, {8, 2}, {4, 4}}, /*  16 bits per pixel */
38    {{ 8, 1}, {4, 2}, {0, 0}}, /*  32 bits per pixel */
39    {{ 4, 1}, {0, 0}, {2, 2}}, /*  64 bits per pixel */
40    {{ 2, 1}, {0, 0}, {0, 0}}  /* 128 bits per pixel */
41};
42
43/* Return true if macrotiling can be enabled */
44static Bool RADEONMacroSwitch(int width, int height, int bpp,
45                              uint32_t flags, Bool rv350_mode)
46{
47    unsigned tilew, tileh, microtiled, logbpp;
48
49    logbpp = RADEONLog2(bpp / 8);
50    if (logbpp > 4)
51        return 0;
52
53    microtiled = !!(flags & RADEON_TILING_MICRO);
54    tilew = MicroBlockTable[logbpp][microtiled][0] * 8;
55    tileh = MicroBlockTable[logbpp][microtiled][1] * 8;
56
57    /* See TX_FILTER1_n.MACRO_SWITCH. */
58    if (rv350_mode) {
59        return width >= tilew && height >= tileh;
60    } else {
61        return width > tilew && height > tileh;
62    }
63}
64
65/* Calculate appropriate tiling and pitch for a pixmap and allocate a BO that
66 * can hold it.
67 */
68struct radeon_bo*
69radeon_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width, int height, int depth,
70		       int usage_hint, int bitsPerPixel, int *new_pitch,
71		       struct radeon_surface *new_surface, uint32_t *new_tiling)
72{
73    RADEONInfoPtr info = RADEONPTR(pScrn);
74    int pitch, base_align;
75    uint32_t size, heighta;
76    int cpp = bitsPerPixel / 8;
77    uint32_t tiling = 0, flags = 0;
78    struct radeon_surface surface;
79    struct radeon_bo *bo;
80    int domain = RADEON_GEM_DOMAIN_VRAM;
81    if (usage_hint) {
82	if (info->allowColorTiling) {
83	    if (usage_hint & RADEON_CREATE_PIXMAP_TILING_MACRO)
84		tiling |= RADEON_TILING_MACRO;
85	    if (usage_hint & RADEON_CREATE_PIXMAP_TILING_MICRO)
86                tiling |= RADEON_TILING_MICRO;
87	}
88	if (usage_hint & RADEON_CREATE_PIXMAP_DEPTH)
89		tiling |= RADEON_TILING_MACRO | RADEON_TILING_MICRO;
90
91	if ((usage_hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP &&
92	     info->shadow_primary)
93#ifdef CREATE_PIXMAP_USAGE_SHARED
94	    || (usage_hint & 0xffff) == CREATE_PIXMAP_USAGE_SHARED
95#endif
96	    ) {
97		tiling = 0;
98		domain = RADEON_GEM_DOMAIN_GTT;
99	}
100    }
101
102    /* Small pixmaps must not be macrotiled on R300, hw cannot sample them
103     * correctly because samplers automatically switch to macrolinear. */
104    if (info->ChipFamily >= CHIP_FAMILY_R300 &&
105        info->ChipFamily <= CHIP_FAMILY_RS740 &&
106        (tiling & RADEON_TILING_MACRO) &&
107        !RADEONMacroSwitch(width, height, bitsPerPixel, tiling,
108                           info->ChipFamily >= CHIP_FAMILY_RV350)) {
109        tiling &= ~RADEON_TILING_MACRO;
110    }
111
112    heighta = RADEON_ALIGN(height, drmmode_get_height_align(pScrn, tiling));
113    pitch = RADEON_ALIGN(width, drmmode_get_pitch_align(pScrn, cpp, tiling)) * cpp;
114    base_align = drmmode_get_base_align(pScrn, cpp, tiling);
115    size = RADEON_ALIGN(heighta * pitch, RADEON_GPU_PAGE_SIZE);
116    memset(&surface, 0, sizeof(struct radeon_surface));
117
118    if (info->ChipFamily >= CHIP_FAMILY_R600 && info->surf_man) {
119		if (width) {
120			surface.npix_x = width;
121			/* need to align height to 8 for old kernel */
122			surface.npix_y = RADEON_ALIGN(height, 8);
123			surface.npix_z = 1;
124			surface.blk_w = 1;
125			surface.blk_h = 1;
126			surface.blk_d = 1;
127			surface.array_size = 1;
128			surface.last_level = 0;
129			surface.bpe = cpp;
130			surface.nsamples = 1;
131			if (height < 128) {
132				/* disable 2d tiling for small surface to work around
133				 * the fact that ddx align height to 8 pixel for old
134				 * obscure reason i can't remember
135				 */
136				tiling &= ~RADEON_TILING_MACRO;
137			}
138			surface.flags = RADEON_SURF_SCANOUT;
139			/* we are requiring a recent enough libdrm version */
140			surface.flags |= RADEON_SURF_HAS_TILE_MODE_INDEX;
141			surface.flags |= RADEON_SURF_SET(RADEON_SURF_TYPE_2D, TYPE);
142			surface.flags |= RADEON_SURF_SET(RADEON_SURF_MODE_LINEAR, MODE);
143			if ((tiling & RADEON_TILING_MICRO)) {
144				surface.flags = RADEON_SURF_CLR(surface.flags, MODE);
145				surface.flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
146			}
147			if ((tiling & RADEON_TILING_MACRO)) {
148				surface.flags = RADEON_SURF_CLR(surface.flags, MODE);
149				surface.flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE);
150			}
151			if (usage_hint & RADEON_CREATE_PIXMAP_SZBUFFER) {
152				surface.flags |= RADEON_SURF_ZBUFFER;
153				surface.flags |= RADEON_SURF_SBUFFER;
154			}
155			if (radeon_surface_best(info->surf_man, &surface)) {
156				return NULL;
157			}
158			if (radeon_surface_init(info->surf_man, &surface)) {
159				return NULL;
160			}
161			size = surface.bo_size;
162			base_align = surface.bo_alignment;
163			pitch = surface.level[0].pitch_bytes;
164			tiling = 0;
165			switch (surface.level[0].mode) {
166			case RADEON_SURF_MODE_2D:
167				tiling |= RADEON_TILING_MACRO;
168				tiling |= surface.bankw << RADEON_TILING_EG_BANKW_SHIFT;
169				tiling |= surface.bankh << RADEON_TILING_EG_BANKH_SHIFT;
170				tiling |= surface.mtilea << RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT;
171				if (surface.tile_split)
172					tiling |= eg_tile_split(surface.tile_split) << RADEON_TILING_EG_TILE_SPLIT_SHIFT;
173				tiling |= eg_tile_split(surface.stencil_tile_split) << RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT;
174				break;
175			case RADEON_SURF_MODE_1D:
176				tiling |= RADEON_TILING_MICRO;
177				break;
178			default:
179				break;
180			}
181		}
182	}
183
184    if (tiling)
185	flags |= RADEON_GEM_NO_CPU_ACCESS;
186
187    bo = radeon_bo_open(info->bufmgr, 0, size, base_align,
188			domain, flags);
189
190    if (bo && tiling && radeon_bo_set_tiling(bo, tiling, pitch) == 0)
191	*new_tiling = tiling;
192
193    *new_surface = surface;
194    *new_pitch = pitch;
195    return bo;
196}
197
198/* Get GEM handle for the pixmap */
199Bool radeon_get_pixmap_handle(PixmapPtr pixmap, uint32_t *handle)
200{
201    struct radeon_bo *bo = radeon_get_pixmap_bo(pixmap);
202#ifdef USE_GLAMOR
203    ScreenPtr screen = pixmap->drawable.pScreen;
204    RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(screen));
205#endif
206
207    if (bo) {
208	*handle = bo->handle;
209	return TRUE;
210    }
211
212#ifdef USE_GLAMOR
213    if (info->use_glamor) {
214	struct radeon_pixmap *priv = radeon_get_pixmap_private(pixmap);
215	CARD16 stride;
216	CARD32 size;
217	int fd, r;
218
219	if (!priv) {
220	    priv = calloc(1, sizeof(*priv));
221	    radeon_set_pixmap_private(pixmap, priv);
222	}
223
224	if (priv->handle_valid) {
225	    *handle = priv->handle;
226	    return TRUE;
227	}
228
229	fd = glamor_fd_from_pixmap(screen, pixmap, &stride, &size);
230	if (fd < 0)
231	    return FALSE;
232
233	r = drmPrimeFDToHandle(info->dri2.drm_fd, fd, &priv->handle);
234	close(fd);
235	if (r == 0) {
236	    struct drm_radeon_gem_set_tiling args = { .handle = priv->handle };
237
238	    priv->handle_valid = TRUE;
239	    *handle = priv->handle;
240
241	    if (drmCommandWriteRead(info->dri2.drm_fd,
242				    DRM_RADEON_GEM_GET_TILING, &args,
243				    sizeof(args)) == 0)
244		priv->tiling_flags = args.tiling_flags;
245
246	    return TRUE;
247	}
248    }
249#endif
250
251    return FALSE;
252}
253
254uint32_t radeon_get_pixmap_tiling_flags(PixmapPtr pPix)
255{
256#ifdef USE_GLAMOR
257    RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(pPix->drawable.pScreen));
258
259    if (info->use_glamor) {
260	struct radeon_pixmap *priv = radeon_get_pixmap_private(pPix);
261
262	if (!priv || (!priv->bo && !priv->handle_valid)) {
263	    uint32_t handle;
264
265	    radeon_get_pixmap_handle(pPix, &handle);
266	    priv = radeon_get_pixmap_private(pPix);
267	}
268
269	return priv ? priv->tiling_flags : 0;
270    } else
271#endif
272    {
273	struct radeon_exa_pixmap_priv *driver_priv;
274	driver_priv = exaGetPixmapDriverPrivate(pPix);
275	return driver_priv ? driver_priv->tiling_flags : 0;
276    }
277}
278
279#ifdef RADEON_PIXMAP_SHARING
280
281Bool radeon_share_pixmap_backing(struct radeon_bo *bo, void **handle_p)
282{
283    int handle;
284
285    if (radeon_gem_prime_share_bo(bo, &handle) != 0)
286	return FALSE;
287
288    *handle_p = (void *)(long)handle;
289    return TRUE;
290}
291
292static unsigned eg_tile_split_opp(unsigned tile_split)
293{
294    switch (tile_split) {
295        case 0:     tile_split = 64;    break;
296        case 1:     tile_split = 128;   break;
297        case 2:     tile_split = 256;   break;
298        case 3:     tile_split = 512;   break;
299        default:
300        case 4:     tile_split = 1024;  break;
301        case 5:     tile_split = 2048;  break;
302        case 6:     tile_split = 4096;  break;
303    }
304    return tile_split;
305}
306
307Bool radeon_set_shared_pixmap_backing(PixmapPtr ppix, void *fd_handle,
308				      struct radeon_surface *surface)
309{
310    ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen);
311    RADEONInfoPtr info = RADEONPTR(pScrn);
312    struct radeon_bo *bo;
313    int ihandle = (int)(long)fd_handle;
314    uint32_t size = ppix->devKind * ppix->drawable.height;
315
316    bo = radeon_gem_bo_open_prime(info->bufmgr, ihandle, size);
317    if (!bo)
318        return FALSE;
319
320    memset(surface, 0, sizeof(struct radeon_surface));
321
322    radeon_set_pixmap_bo(ppix, bo);
323
324    if (info->ChipFamily >= CHIP_FAMILY_R600 && info->surf_man) {
325	uint32_t tiling_flags;
326
327#ifdef USE_GLAMOR
328	if (info->use_glamor) {
329	    tiling_flags = radeon_get_pixmap_private(ppix)->tiling_flags;
330	} else
331#endif
332	{
333	    struct radeon_exa_pixmap_priv *driver_priv;
334
335	    driver_priv = exaGetPixmapDriverPrivate(ppix);
336	    tiling_flags = driver_priv->tiling_flags;
337	}
338
339	surface->npix_x = ppix->drawable.width;
340	surface->npix_y = ppix->drawable.height;
341	surface->npix_z = 1;
342	surface->blk_w = 1;
343	surface->blk_h = 1;
344	surface->blk_d = 1;
345	surface->array_size = 1;
346	surface->bpe = ppix->drawable.bitsPerPixel / 8;
347	surface->nsamples = 1;
348	/* we are requiring a recent enough libdrm version */
349	surface->flags |= RADEON_SURF_HAS_TILE_MODE_INDEX;
350	surface->flags |= RADEON_SURF_SET(RADEON_SURF_TYPE_2D, TYPE);
351	if (tiling_flags & RADEON_TILING_MACRO)
352	    surface->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_2D, MODE);
353	else if (tiling_flags & RADEON_TILING_MICRO)
354	    surface->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_1D, MODE);
355	else
356	    surface->flags |= RADEON_SURF_SET(RADEON_SURF_MODE_LINEAR_ALIGNED, MODE);
357	surface->bankw = (tiling_flags >> RADEON_TILING_EG_BANKW_SHIFT) & RADEON_TILING_EG_BANKW_MASK;
358	surface->bankh = (tiling_flags >> RADEON_TILING_EG_BANKH_SHIFT) & RADEON_TILING_EG_BANKH_MASK;
359	surface->tile_split = eg_tile_split_opp((tiling_flags >> RADEON_TILING_EG_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_TILE_SPLIT_MASK);
360	surface->stencil_tile_split = (tiling_flags >> RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK;
361	surface->mtilea = (tiling_flags >> RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK;
362	if (radeon_surface_best(info->surf_man, surface)) {
363	    return FALSE;
364	}
365	if (radeon_surface_init(info->surf_man, surface)) {
366	    return FALSE;
367	}
368	/* we have to post hack the surface to reflect the actual size
369	   of the shared pixmap */
370	surface->level[0].pitch_bytes = ppix->devKind;
371	surface->level[0].nblk_x = ppix->devKind / surface->bpe;
372    }
373
374    close(ihandle);
375    /* we have a reference from the alloc and one from set pixmap bo,
376       drop one */
377    radeon_bo_unref(bo);
378    return TRUE;
379}
380
381#endif /* RADEON_PIXMAP_SHARING */
382