nouveau_exa.c revision fda9279d
1/*
2 * Copyright 2009 Nouveau Project
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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#include "nv_include.h"
24#include "exa.h"
25
26#include "hwdefs/nv_m2mf.xml.h"
27
28static inline Bool
29NVAccelMemcpyRect(char *dst, const char *src, int height, int dst_pitch,
30		  int src_pitch, int line_len)
31{
32	if ((src_pitch == line_len) && (src_pitch == dst_pitch)) {
33		memcpy(dst, src, line_len*height);
34	} else {
35		while (height--) {
36			memcpy(dst, src, line_len);
37			src += src_pitch;
38			dst += dst_pitch;
39		}
40	}
41
42	return TRUE;
43}
44
45Bool
46NVAccelM2MF(NVPtr pNv, int w, int h, int cpp, uint32_t srcoff, uint32_t dstoff,
47	    struct nouveau_bo *src, int sd, int sp, int sh, int sx, int sy,
48	    struct nouveau_bo *dst, int dd, int dp, int dh, int dx, int dy)
49{
50	if (pNv->ce_rect && pNv->ce_enabled)
51		return pNv->ce_rect(pNv->ce_pushbuf, pNv->NvCopy, w, h, cpp,
52				    src, srcoff, sd, sp, sh, sx, sy,
53				    dst, dstoff, dd, dp, dh, dx, dy);
54	else
55	if (pNv->Architecture >= NV_KEPLER)
56		return NVE0EXARectCopy(pNv, w, h, cpp,
57				       src, srcoff, sd, sp, sh, sx, sy,
58				       dst, dstoff, dd, dp, dh, dx, dy);
59	else
60	if (pNv->Architecture >= NV_FERMI)
61		return NVC0EXARectM2MF(pNv, w, h, cpp,
62				       src, srcoff, sd, sp, sh, sx, sy,
63				       dst, dstoff, dd, dp, dh, dx, dy);
64	else
65	if (pNv->Architecture >= NV_TESLA)
66		return NV50EXARectM2MF(pNv, w, h, cpp,
67				       src, srcoff, sd, sp, sh, sx, sy,
68				       dst, dstoff, dd, dp, dh, dx, dy);
69	else
70		return NV04EXARectM2MF(pNv, w, h, cpp,
71				       src, srcoff, sd, sp, sh, sx, sy,
72				       dst, dstoff, dd, dp, dh, dx, dy);
73	return FALSE;
74}
75
76static int
77nouveau_exa_mark_sync(ScreenPtr pScreen)
78{
79	return 0;
80}
81
82static void
83nouveau_exa_wait_marker(ScreenPtr pScreen, int marker)
84{
85}
86
87static Bool
88nouveau_exa_prepare_access(PixmapPtr ppix, int index)
89{
90	struct nouveau_bo *bo = nouveau_pixmap_bo(ppix);
91	NVPtr pNv = NVPTR(xf86ScreenToScrn(ppix->drawable.pScreen));
92
93	if (nv50_style_tiled_pixmap(ppix) && !pNv->wfb_enabled)
94		return FALSE;
95	if (nouveau_bo_map(bo, NOUVEAU_BO_RDWR, pNv->client))
96		return FALSE;
97	ppix->devPrivate.ptr = bo->map;
98	return TRUE;
99}
100
101static void
102nouveau_exa_finish_access(PixmapPtr ppix, int index)
103{
104}
105
106static Bool
107nouveau_exa_pixmap_is_offscreen(PixmapPtr ppix)
108{
109	return nouveau_pixmap_bo(ppix) != NULL;
110}
111
112static void *
113nouveau_exa_create_pixmap(ScreenPtr pScreen, int width, int height, int depth,
114			  int usage_hint, int bitsPerPixel, int *new_pitch)
115{
116	ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen);
117	NVPtr pNv = NVPTR(scrn);
118	struct nouveau_pixmap *nvpix;
119	int ret;
120
121	if (!width || !height)
122		return calloc(1, sizeof(*nvpix));
123
124	if (!pNv->exa_force_cp && pNv->dev->vram_size <= 32 * 1024 * 1024)
125		return NULL;
126
127	nvpix = calloc(1, sizeof(*nvpix));
128	if (!nvpix)
129		return NULL;
130
131	ret = nouveau_allocate_surface(scrn, width, height, bitsPerPixel,
132				       usage_hint, new_pitch, &nvpix->bo);
133	if (!ret) {
134		free(nvpix);
135		return NULL;
136	}
137
138#ifdef NOUVEAU_PIXMAP_SHARING
139	if ((usage_hint & 0xffff) == CREATE_PIXMAP_USAGE_SHARED)
140		nvpix->shared = TRUE;
141#endif
142
143	return nvpix;
144}
145
146static void
147nouveau_exa_destroy_pixmap(ScreenPtr pScreen, void *priv)
148{
149	struct nouveau_pixmap *nvpix = priv;
150
151	if (!nvpix)
152		return;
153
154	nouveau_bo_ref(NULL, &nvpix->bo);
155	free(nvpix);
156}
157
158#ifdef NOUVEAU_PIXMAP_SHARING
159static Bool
160nouveau_exa_share_pixmap_backing(PixmapPtr ppix, ScreenPtr slave, void **handle_p)
161{
162	struct nouveau_bo *bo = nouveau_pixmap_bo(ppix);
163	struct nouveau_pixmap *nvpix = nouveau_pixmap(ppix);
164	int ret;
165	int handle;
166
167	ret = nouveau_bo_set_prime(bo, &handle);
168	if (ret != 0) {
169		ErrorF("%s: ret is %d errno is %d\n", __func__, ret, errno);
170		return FALSE;
171	}
172	nvpix->shared = TRUE;
173	*handle_p = (void *)(long)handle;
174	return TRUE;
175}
176
177static Bool
178nouveau_exa_set_shared_pixmap_backing(PixmapPtr ppix, void *handle)
179{
180	ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen);
181	NVPtr pNv = NVPTR(pScrn);
182	struct nouveau_bo *bo = nouveau_pixmap_bo(ppix);
183	struct nouveau_pixmap *nvpix = nouveau_pixmap(ppix);
184	int ret;
185	int ihandle = (int)(long)(handle);
186
187	ret = nouveau_bo_prime_handle_ref(pNv->dev, ihandle, &bo);
188	if (ret) {
189		ErrorF("failed to get BO with handle %d\n", ihandle);
190		return FALSE;
191	}
192	nvpix->bo = bo;
193	nvpix->shared = TRUE;
194	close(ihandle);
195	return TRUE;
196}
197#endif
198
199bool
200nv50_style_tiled_pixmap(PixmapPtr ppix)
201{
202	ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen);
203	NVPtr pNv = NVPTR(pScrn);
204
205	return pNv->Architecture >= NV_TESLA &&
206	       nouveau_pixmap_bo(ppix)->config.nv50.memtype;
207}
208
209static int
210nouveau_exa_scratch(NVPtr pNv, int size, struct nouveau_bo **pbo, int *off)
211{
212	struct nouveau_bo *bo;
213	int ret;
214
215	if (!pNv->transfer ||
216	     pNv->transfer->size <= pNv->transfer_offset + size) {
217		ret = nouveau_bo_new(pNv->dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP,
218				     0, NOUVEAU_ALIGN(size, 1 * 1024 * 1024),
219				     NULL, &bo);
220		if (ret != 0)
221			return ret;
222
223		ret = nouveau_bo_map(bo, NOUVEAU_BO_RDWR, pNv->client);
224		if (ret != 0) {
225			nouveau_bo_ref(NULL, &bo);
226			return ret;
227		}
228
229		nouveau_bo_ref(bo, &pNv->transfer);
230		nouveau_bo_ref(NULL, &bo);
231		pNv->transfer_offset = 0;
232	}
233
234	*off = pNv->transfer_offset;
235	*pbo = pNv->transfer;
236
237	pNv->transfer_offset += size;
238	return 0;
239}
240
241static Bool
242nouveau_exa_download_from_screen(PixmapPtr pspix, int x, int y, int w, int h,
243				 char *dst, int dst_pitch)
244{
245	ScrnInfoPtr pScrn = xf86ScreenToScrn(pspix->drawable.pScreen);
246	NVPtr pNv = NVPTR(pScrn);
247	struct nouveau_bo *bo;
248	int src_pitch, tmp_pitch, cpp, i;
249	const char *src;
250	Bool ret;
251
252	cpp = pspix->drawable.bitsPerPixel >> 3;
253	src_pitch  = exaGetPixmapPitch(pspix);
254	tmp_pitch = w * cpp;
255
256	while (h) {
257		const int lines = (h > 2047) ? 2047 : h;
258		struct nouveau_bo *tmp;
259		int tmp_offset;
260
261		if (nouveau_exa_scratch(pNv, lines * tmp_pitch,
262					&tmp, &tmp_offset))
263			goto memcpy;
264
265		if (!NVAccelM2MF(pNv, w, lines, cpp, 0, tmp_offset,
266				 nouveau_pixmap_bo(pspix), NOUVEAU_BO_VRAM,
267				 src_pitch, pspix->drawable.height, x, y,
268				 tmp, NOUVEAU_BO_GART, tmp_pitch,
269				 lines, 0, 0))
270			goto memcpy;
271
272		nouveau_bo_wait(tmp, NOUVEAU_BO_RD, pNv->client);
273		if (dst_pitch == tmp_pitch) {
274			memcpy(dst, tmp->map + tmp_offset, dst_pitch * lines);
275			dst += dst_pitch * lines;
276		} else {
277			src = tmp->map + tmp_offset;
278			for (i = 0; i < lines; i++) {
279				memcpy(dst, src, tmp_pitch);
280				src += tmp_pitch;
281				dst += dst_pitch;
282			}
283		}
284
285		/* next! */
286		h -= lines;
287		y += lines;
288	}
289	return TRUE;
290
291memcpy:
292	bo = nouveau_pixmap_bo(pspix);
293	if (nv50_style_tiled_pixmap(pspix))
294		ErrorF("%s:%d - falling back to memcpy ignores tiling\n",
295		       __func__, __LINE__);
296
297	if (nouveau_bo_map(bo, NOUVEAU_BO_RD, pNv->client))
298		return FALSE;
299	src = (char *)bo->map + (y * src_pitch) + (x * cpp);
300	ret = NVAccelMemcpyRect(dst, src, h, dst_pitch, src_pitch, w*cpp);
301	return ret;
302}
303
304static Bool
305nouveau_exa_upload_to_screen(PixmapPtr pdpix, int x, int y, int w, int h,
306			     char *src, int src_pitch)
307{
308	ScrnInfoPtr pScrn = xf86ScreenToScrn(pdpix->drawable.pScreen);
309	NVPtr pNv = NVPTR(pScrn);
310	int dst_pitch, tmp_pitch, cpp, i;
311	struct nouveau_bo *bo;
312	char *dst;
313	Bool ret;
314
315	cpp = pdpix->drawable.bitsPerPixel >> 3;
316	dst_pitch  = exaGetPixmapPitch(pdpix);
317	tmp_pitch = w * cpp;
318
319	/* try hostdata transfer */
320	if (w * h * cpp < 16*1024) /* heuristic */
321	{
322		if (pNv->Architecture < NV_TESLA) {
323			if (NV04EXAUploadIFC(pScrn, src, src_pitch, pdpix,
324					     x, y, w, h, cpp)) {
325				return TRUE;
326			}
327		} else
328		if (pNv->Architecture < NV_FERMI) {
329			if (NV50EXAUploadSIFC(src, src_pitch, pdpix,
330					      x, y, w, h, cpp)) {
331				return TRUE;
332			}
333		} else {
334			if (NVC0EXAUploadSIFC(src, src_pitch, pdpix,
335					      x, y, w, h, cpp)) {
336				return TRUE;
337			}
338		}
339	}
340
341	while (h) {
342		const int lines = (h > 2047) ? 2047 : h;
343		struct nouveau_bo *tmp;
344		int tmp_offset;
345
346		if (nouveau_exa_scratch(pNv, lines * tmp_pitch,
347					&tmp, &tmp_offset))
348			goto memcpy;
349
350		if (src_pitch == tmp_pitch) {
351			memcpy(tmp->map + tmp_offset, src, src_pitch * lines);
352			src += src_pitch * lines;
353		} else {
354			dst = tmp->map + tmp_offset;
355			for (i = 0; i < lines; i++) {
356				memcpy(dst, src, tmp_pitch);
357				src += src_pitch;
358				dst += tmp_pitch;
359			}
360		}
361
362		if (!NVAccelM2MF(pNv, w, lines, cpp, tmp_offset, 0, tmp,
363				 NOUVEAU_BO_GART, tmp_pitch, lines, 0, 0,
364				 nouveau_pixmap_bo(pdpix), NOUVEAU_BO_VRAM,
365				 dst_pitch, pdpix->drawable.height, x, y))
366			goto memcpy;
367
368		/* next! */
369		h -= lines;
370		y += lines;
371	}
372
373	return TRUE;
374
375	/* fallback to memcpy-based transfer */
376memcpy:
377	bo = nouveau_pixmap_bo(pdpix);
378	if (nv50_style_tiled_pixmap(pdpix))
379		ErrorF("%s:%d - falling back to memcpy ignores tiling\n",
380		       __func__, __LINE__);
381
382	if (nouveau_bo_map(bo, NOUVEAU_BO_WR, pNv->client))
383		return FALSE;
384	dst = (char *)bo->map + (y * dst_pitch) + (x * cpp);
385	ret = NVAccelMemcpyRect(dst, src, h, dst_pitch, src_pitch, w*cpp);
386	return ret;
387}
388
389Bool
390nouveau_exa_pixmap_is_onscreen(PixmapPtr ppix)
391{
392	ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen);
393
394	if (pScrn->pScreen->GetScreenPixmap(pScrn->pScreen) == ppix)
395		return TRUE;
396
397	return FALSE;
398}
399
400static void
401nouveau_exa_flush(ScrnInfoPtr pScrn)
402{
403	NVPtr pNv = NVPTR(pScrn);
404	nouveau_pushbuf_kick(pNv->pushbuf, pNv->pushbuf->channel);
405}
406
407Bool
408nouveau_exa_init(ScreenPtr pScreen)
409{
410	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
411	NVPtr pNv = NVPTR(pScrn);
412	ExaDriverPtr exa;
413
414	if (!xf86LoadSubModule(pScrn, "exa"))
415		return FALSE;
416
417	exa = exaDriverAlloc();
418	if (!exa)
419		return FALSE;
420
421	exa->exa_major = EXA_VERSION_MAJOR;
422	exa->exa_minor = EXA_VERSION_MINOR;
423	exa->flags = EXA_OFFSCREEN_PIXMAPS;
424
425#ifdef EXA_SUPPORTS_PREPARE_AUX
426	exa->flags |= EXA_SUPPORTS_PREPARE_AUX;
427#endif
428
429	exa->PixmapIsOffscreen = nouveau_exa_pixmap_is_offscreen;
430	exa->PrepareAccess = nouveau_exa_prepare_access;
431	exa->FinishAccess = nouveau_exa_finish_access;
432
433	exa->flags |= (EXA_HANDLES_PIXMAPS | EXA_MIXED_PIXMAPS);
434	exa->pixmapOffsetAlign = 256;
435	exa->pixmapPitchAlign = 64;
436
437	exa->CreatePixmap2 = nouveau_exa_create_pixmap;
438	exa->DestroyPixmap = nouveau_exa_destroy_pixmap;
439#ifdef NOUVEAU_PIXMAP_SHARING
440	exa->SharePixmapBacking = nouveau_exa_share_pixmap_backing;
441	exa->SetSharedPixmapBacking = nouveau_exa_set_shared_pixmap_backing;
442#endif
443
444	if (pNv->Architecture >= NV_TESLA) {
445		exa->maxX = 8192;
446		exa->maxY = 8192;
447	} else
448	if (pNv->Architecture >= NV_ARCH_10) {
449		exa->maxX = 4096;
450		exa->maxY = 4096;
451	} else {
452		exa->maxX = 2048;
453		exa->maxY = 2048;
454	}
455
456	exa->MarkSync = nouveau_exa_mark_sync;
457	exa->WaitMarker = nouveau_exa_wait_marker;
458
459	exa->DownloadFromScreen = nouveau_exa_download_from_screen;
460	exa->UploadToScreen = nouveau_exa_upload_to_screen;
461
462	if (pNv->Architecture < NV_TESLA) {
463		exa->PrepareCopy = NV04EXAPrepareCopy;
464		exa->Copy = NV04EXACopy;
465		exa->DoneCopy = NV04EXADoneCopy;
466
467		exa->PrepareSolid = NV04EXAPrepareSolid;
468		exa->Solid = NV04EXASolid;
469		exa->DoneSolid = NV04EXADoneSolid;
470	} else
471	if (pNv->Architecture < NV_FERMI) {
472		exa->PrepareCopy = NV50EXAPrepareCopy;
473		exa->Copy = NV50EXACopy;
474		exa->DoneCopy = NV50EXADoneCopy;
475
476		exa->PrepareSolid = NV50EXAPrepareSolid;
477		exa->Solid = NV50EXASolid;
478		exa->DoneSolid = NV50EXADoneSolid;
479	} else {
480		exa->PrepareCopy = NVC0EXAPrepareCopy;
481		exa->Copy        = NVC0EXACopy;
482		exa->DoneCopy    = NVC0EXADoneCopy;
483
484		exa->PrepareSolid = NVC0EXAPrepareSolid;
485		exa->Solid        = NVC0EXASolid;
486		exa->DoneSolid    = NVC0EXADoneSolid;
487	}
488
489	switch (pNv->Architecture) {
490	case NV_ARCH_10:
491	case NV_ARCH_20:
492 		exa->CheckComposite   = NV10EXACheckComposite;
493 		exa->PrepareComposite = NV10EXAPrepareComposite;
494 		exa->Composite        = NV10EXAComposite;
495 		exa->DoneComposite    = NV10EXADoneComposite;
496		break;
497	case NV_ARCH_30:
498		exa->CheckComposite   = NV30EXACheckComposite;
499		exa->PrepareComposite = NV30EXAPrepareComposite;
500		exa->Composite        = NV30EXAComposite;
501		exa->DoneComposite    = NV30EXADoneComposite;
502		break;
503	case NV_ARCH_40:
504		exa->CheckComposite   = NV40EXACheckComposite;
505		exa->PrepareComposite = NV40EXAPrepareComposite;
506		exa->Composite        = NV40EXAComposite;
507		exa->DoneComposite    = NV40EXADoneComposite;
508		break;
509	case NV_TESLA:
510		exa->CheckComposite   = NV50EXACheckComposite;
511		exa->PrepareComposite = NV50EXAPrepareComposite;
512		exa->Composite        = NV50EXAComposite;
513		exa->DoneComposite    = NV50EXADoneComposite;
514		break;
515	case NV_FERMI:
516	case NV_KEPLER:
517		exa->CheckComposite   = NVC0EXACheckComposite;
518		exa->PrepareComposite = NVC0EXAPrepareComposite;
519		exa->Composite        = NVC0EXAComposite;
520		exa->DoneComposite    = NVC0EXADoneComposite;
521		break;
522	case NV_MAXWELL:
523	default:
524		break;
525	}
526
527	if (!exaDriverInit(pScreen, exa))
528		return FALSE;
529
530	pNv->EXADriverPtr = exa;
531	pNv->Flush = nouveau_exa_flush;
532	return TRUE;
533}
534