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