1/*
2 * crude EXA support for geforce chips
3 *
4 * Copyright (C) 2018 Michael Lorenz
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * MICHAEL LORENZ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24/* $NetBSD: nv_exa.c,v 1.7 2022/08/15 09:44:19 macallan Exp $ */
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include "nv_include.h"
31#include "miline.h"
32#include "nv_dma.h"
33#include "exa.h"
34
35//#define DEBUG
36
37#ifdef DEBUG
38#define ENTER xf86Msg(X_ERROR, "%s\n", __func__)
39#define LEAVE xf86Msg(X_ERROR, "%s done\n", __func__)
40#else
41#define ENTER
42#define LEAVE
43#endif
44
45static void
46NvWaitMarker(ScreenPtr pScreen, int Marker)
47{
48	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
49
50	ENTER;
51	NVSync(pScrn);
52	LEAVE;
53}
54
55static Bool
56NvPrepareCopy
57(
58    PixmapPtr pSrcPixmap,
59    PixmapPtr pDstPixmap,
60    int       xdir,
61    int       ydir,
62    int       rop,
63    Pixel     planemask
64)
65{
66	ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum];
67	NVPtr pNv = NVPTR(pScrn);
68	uint32_t dstpitch, dstoff, srcpitch, srcoff;
69
70	ENTER;
71	if (pSrcPixmap->drawable.bitsPerPixel != 32)
72		xf86Msg(X_ERROR, "%s %d bpp\n", __func__, pSrcPixmap->drawable.bitsPerPixel);
73	planemask |= ~0 << pNv->CurrentLayout.depth;
74	NVSetRopSolid(pScrn, rop, planemask);
75
76	dstpitch = exaGetPixmapPitch(pDstPixmap);
77	dstoff = exaGetPixmapOffset(pDstPixmap);
78	srcpitch = exaGetPixmapPitch(pSrcPixmap);
79	srcoff = exaGetPixmapOffset(pSrcPixmap);
80
81
82	NVDmaStart(pNv, SURFACE_FORMAT, 4);
83	NVDmaNext (pNv, pNv->surfaceFormat);
84	NVDmaNext (pNv, srcpitch | (dstpitch << 16));
85	NVDmaNext (pNv, srcoff);
86	NVDmaNext (pNv, dstoff);
87
88	pNv->DMAKickoffCallback = NVDMAKickoffCallback;
89
90	LEAVE;
91	return TRUE;
92}
93
94static void
95NvCopy
96(
97    PixmapPtr pDstPixmap,
98    int       srcX,
99    int       srcY,
100    int       dstX,
101    int       dstY,
102    int       w,
103    int       h
104)
105{
106	ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum];
107	NVPtr pNv = NVPTR(pScrn);
108
109	ENTER;
110
111	NVDmaStart(pNv, BLIT_POINT_SRC, 3);
112	NVDmaNext (pNv, (srcY << 16) | srcX);
113	NVDmaNext (pNv, (dstY << 16) | dstX);
114	NVDmaNext (pNv, (h  << 16) | w);
115
116	if((w * h) >= 512)
117		NVDmaKickoff(pNv);
118
119	LEAVE;
120}
121
122static void
123NvDoneCopy(PixmapPtr pDstPixmap)
124{
125    ENTER;
126    LEAVE;
127}
128
129static Bool
130NvPrepareSolid(
131    PixmapPtr pPixmap,
132    int rop,
133    Pixel planemask,
134    Pixel color)
135{
136	ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
137	NVPtr pNv = NVPTR(pScrn);
138	uint32_t pitch, off;
139
140	ENTER;
141
142	if (pPixmap->drawable.bitsPerPixel != 32) {
143#ifdef DEBUG
144		xf86Msg(X_ERROR, "%s %d bpp\n", __func__, pPixmap->drawable.bitsPerPixel);
145#endif
146		return FALSE;
147	}
148	planemask |= ~0 << pNv->CurrentLayout.depth;
149	off = exaGetPixmapOffset(pPixmap);
150
151	/*
152	 * XXX
153	 * on my 6800 Ultra the drawing engine stalls when drawing at least
154	 * some rectangles into off-screen memory. Draw them by software until
155	 * I figure out what's going on
156	 */
157	if (pNv->Architecture >= NV_ARCH_40) {
158		if (off != 0) return FALSE;
159	}
160
161	NVSetRopSolid(pScrn, rop, planemask);
162
163	pitch = exaGetPixmapPitch(pPixmap);
164
165	NVDmaStart(pNv, SURFACE_FORMAT, 4);
166	NVDmaNext (pNv, pNv->surfaceFormat);
167	NVDmaNext (pNv, pitch | (pitch << 16));
168	NVDmaNext (pNv, off);
169	NVDmaNext (pNv, off);
170
171	NVDmaStart(pNv, RECT_FORMAT, 1);
172	NVDmaNext (pNv, pNv->rectFormat);
173
174	NVDmaStart(pNv, RECT_SOLID_COLOR, 1);
175	NVDmaNext (pNv, color);
176
177	pNv->DMAKickoffCallback = NVDMAKickoffCallback;
178
179	LEAVE;
180	return TRUE;
181}
182
183static void
184NvSolid(
185    PixmapPtr pPixmap,
186    int x1,
187    int y1,
188    int x2,
189    int y2)
190{
191	ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
192	NVPtr pNv = NVPTR(pScrn);
193	int w = x2 - x1, h = y2 - y1;
194
195	ENTER;
196
197	NVDmaStart(pNv, RECT_SOLID_RECTS(0), 2);
198	NVDmaNext (pNv, (x1 << 16) | y1);
199	NVDmaNext (pNv, (w << 16) | h);
200
201	if((w * h) >= 512)
202		NVDmaKickoff(pNv);
203
204	LEAVE;
205}
206
207/*
208 * Memcpy-based UTS.
209 */
210static Bool
211NvUploadToScreen(PixmapPtr pDst, int x, int y, int w, int h,
212    char *src, int src_pitch)
213{
214	ScrnInfoPtr pScrn = xf86Screens[pDst->drawable.pScreen->myNum];
215	NVPtr pNv = NVPTR(pScrn);
216	unsigned char *dst = pNv->FbStart + exaGetPixmapOffset(pDst);
217	int dst_pitch = exaGetPixmapPitch(pDst);
218
219	int bpp    = pDst->drawable.bitsPerPixel;
220	int cpp    = (bpp + 7) >> 3;
221	int wBytes = w * cpp;
222
223	ENTER;
224	dst += (x * cpp) + (y * dst_pitch);
225
226	NVSync(pScrn);
227
228	while (h--) {
229		memcpy(dst, src, wBytes);
230		src += src_pitch;
231		dst += dst_pitch;
232	}
233
234	LEAVE;
235	return TRUE;
236}
237
238/*
239 * Memcpy-based DFS.
240 */
241static Bool
242NvDownloadFromScreen(PixmapPtr pSrc, int x, int y, int w, int h,
243    char *dst, int dst_pitch)
244{
245	ScrnInfoPtr pScrn = xf86Screens[pSrc->drawable.pScreen->myNum];
246	NVPtr pNv = NVPTR(pScrn);
247	unsigned char *src = pNv->FbStart + exaGetPixmapOffset(pSrc);
248	int src_pitch = exaGetPixmapPitch(pSrc);
249
250	int bpp    = pSrc->drawable.bitsPerPixel;
251	int cpp    = (bpp + 7) >> 3;
252	int wBytes = w * cpp;
253
254	ENTER;
255	src += (x * cpp) + (y * src_pitch);
256
257	NVSync(pScrn);
258
259	while (h--) {
260		memcpy(dst, src, wBytes);
261		src += src_pitch;
262		dst += dst_pitch;
263	}
264	LEAVE;
265	return TRUE;
266}
267
268static Bool
269NvPrepareAccess(PixmapPtr pPix, int index)
270{
271	ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
272
273	NVSync(pScrn);
274	return TRUE;
275}
276
277static void
278NvFinishAccess(PixmapPtr pPix, int index)
279{
280}
281
282Bool
283NvInitExa(ScreenPtr pScreen)
284{
285	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
286	NVPtr pNv = NVPTR(pScrn);
287	ExaDriverPtr pExa;
288
289	pExa = exaDriverAlloc();
290	if (!pExa)
291		return FALSE;
292
293	pNv->pExa = pExa;
294
295	NVResetGraphics(pScrn);
296
297	pExa->exa_major = EXA_VERSION_MAJOR;
298	pExa->exa_minor = EXA_VERSION_MINOR;
299
300	pExa->memoryBase = pNv->FbStart;
301	pExa->memorySize = pNv->ScratchBufferStart & (~255);
302	pExa->offScreenBase = (((pScrn->virtualY * pScrn->displayWidth *
303			       pScrn->bitsPerPixel >> 3) + 255) & (~255));
304	pExa->pixmapOffsetAlign = 256;
305	pExa->pixmapPitchAlign = 256;
306
307	pExa->flags = EXA_OFFSCREEN_PIXMAPS |
308		      EXA_MIXED_PIXMAPS;
309
310	pExa->maxX = 4096;
311	pExa->maxY = 4096;
312
313	pExa->WaitMarker = NvWaitMarker;
314	pExa->PrepareSolid = NvPrepareSolid;
315	pExa->Solid = NvSolid;
316	pExa->DoneSolid = NvDoneCopy;
317	pExa->PrepareCopy = NvPrepareCopy;
318	pExa->Copy = NvCopy;
319	pExa->DoneCopy = NvDoneCopy;
320
321	switch(pNv->CurrentLayout.depth) {
322	case 24:
323		pNv->surfaceFormat = SURFACE_FORMAT_DEPTH24;
324		pNv->rectFormat = RECT_FORMAT_DEPTH24;
325		break;
326	case 16:
327	case 15:
328		pNv->surfaceFormat = SURFACE_FORMAT_DEPTH16;
329		pNv->rectFormat = RECT_FORMAT_DEPTH16;
330		break;
331	default:
332		pNv->surfaceFormat = SURFACE_FORMAT_DEPTH8;
333		pNv->rectFormat = RECT_FORMAT_DEPTH8;
334		break;
335	}
336	NVDmaStart(pNv, SURFACE_FORMAT, 1);
337	NVDmaNext (pNv, pNv->surfaceFormat);
338	NVDmaStart(pNv, RECT_FORMAT, 1);
339	NVDmaNext (pNv, pNv->rectFormat);
340
341	NVDmaStart(pNv, PATTERN_COLOR_0, 4);
342	NVDmaNext (pNv, 0xffffffff);
343	NVDmaNext (pNv, 0xffffffff);
344	NVDmaNext (pNv, 0xffffffff);
345	NVDmaNext (pNv, 0xffffffff);
346
347	pNv->currentRop = ~0;  /* set to something invalid */
348	NVSetRopSolid(pScrn, GXcopy, ~0);
349
350	NVDmaKickoff(pNv);
351
352	/* EXA hits more optimized paths when it does not have to fallback
353	 * because of missing UTS/DFS, hook memcpy-based UTS/DFS.
354	 */
355	pExa->UploadToScreen = NvUploadToScreen;
356	pExa->DownloadFromScreen = NvDownloadFromScreen;
357	pExa->PrepareAccess = NvPrepareAccess;
358	pExa->FinishAccess = NvFinishAccess;
359
360	return exaDriverInit(pScreen, pExa);
361}
362