1/*
2 * Abstraction of the AGP GART interface.
3 *
4 * This version is for Solaris.
5 *
6 * Copyright � 2000 VA Linux Systems, Inc.
7 * Copyright � 2001 The XFree86 Project, Inc.
8 */
9/* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice (including the next
19 * paragraph) shall be included in all copies or substantial portions of the
20 * Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 */
30
31#ifdef HAVE_XORG_CONFIG_H
32#include <xorg-config.h>
33#endif
34
35#include <X11/X.h>
36#include "xf86.h"
37#include "xf86Priv.h"
38#include "xf86_OSlib.h"
39#include "xf86_OSproc.h"
40#include <unistd.h>
41#include <sys/ioccom.h>
42#include <sys/types.h>
43#include <fcntl.h>
44#include <sys/agpgart.h>
45
46/* AGP page size is independent of the host page size. */
47#ifndef	AGP_PAGE_SIZE
48#define	AGP_PAGE_SIZE		4096
49#endif
50
51static int gartFd = -1;
52static int acquiredScreen = -1;
53static Bool initDone = FALSE;
54/*
55 * Close /dev/agpgart.  This frees all associated memory allocated during
56 * this server generation.
57 */
58Bool
59xf86GARTCloseScreen(int screenNum)
60{
61	if (gartFd != -1) {
62		close(gartFd);
63		acquiredScreen = -1;
64		gartFd = -1;
65		initDone = FALSE;
66
67		xf86DrvMsg(screenNum, X_INFO,
68		    "xf86GARTCloseScreen: device closed successfully\n");
69
70	}
71	return TRUE;
72}
73
74/*
75 * Open /dev/agpgart.  Keep it open until xf86GARTCloseScreen is called.
76 */
77static Bool
78GARTInit(int screenNum)
79{
80	if (initDone)
81		return gartFd != -1;
82
83	if (gartFd == -1)
84		gartFd = open(AGP_DEVICE, O_RDWR);
85	else
86		return FALSE;
87
88	if (gartFd == -1) {
89		xf86DrvMsg(screenNum, X_ERROR,
90		    "GARTInit: Unable to open " AGP_DEVICE " (%s)\n",
91		    strerror(errno));
92		return FALSE;
93	}
94
95	initDone = TRUE;
96	xf86DrvMsg(screenNum, X_INFO,
97	    "GARTInit: " AGP_DEVICE " opened successfully\n");
98
99	return TRUE;
100}
101
102Bool
103xf86AgpGARTSupported(void)
104{
105	return (GARTInit(-1));
106
107}
108
109AgpInfoPtr
110xf86GetAGPInfo(int screenNum)
111{
112	agp_info_t agpinf;
113	AgpInfoPtr info;
114
115	if (!GARTInit(screenNum))
116		return NULL;
117
118	if (ioctl(gartFd, AGPIOC_INFO, &agpinf) != 0) {
119		xf86DrvMsg(screenNum, X_ERROR,
120		    "xf86GetAGPInfo: AGPIOC_INFO failed (%s)\n",
121		    strerror(errno));
122		return NULL;
123	}
124
125	if ((info = calloc(sizeof(AgpInfo), 1)) == NULL) {
126		xf86DrvMsg(screenNum, X_ERROR,
127		    "xf86GetAGPInfo: Failed to allocate AgpInfo\n");
128		return NULL;
129	}
130
131	info->bridgeId = agpinf.agpi_devid;
132	info->agpMode = agpinf.agpi_mode;
133	info->base = agpinf.agpi_aperbase;
134	info->size = agpinf.agpi_apersize;
135	info->totalPages = (unsigned long)agpinf.agpi_pgtotal;
136	info->systemPages = (unsigned long)agpinf.agpi_pgsystem;
137	info->usedPages = (unsigned long)agpinf.agpi_pgused;
138
139	return info;
140}
141
142Bool
143xf86AcquireGART(int screenNum)
144{
145
146	if (!GARTInit(screenNum))
147		return FALSE;
148
149	if (acquiredScreen != screenNum) {
150		if (ioctl(gartFd, AGPIOC_ACQUIRE, 0) != 0) {
151			xf86DrvMsg(screenNum, X_WARNING,
152			    "xf86AcquireGART: AGPIOC_ACQUIRE failed (%s)\n",
153			    strerror(errno));
154			return FALSE;
155		}
156		acquiredScreen = screenNum;
157		xf86DrvMsg(screenNum, X_INFO,
158		    "xf86AcquireGART: AGPIOC_ACQUIRE succeeded\n");
159	}
160	return TRUE;
161}
162
163Bool
164xf86ReleaseGART(int screenNum)
165{
166
167	if (!GARTInit(screenNum))
168		return FALSE;
169
170	if (acquiredScreen == screenNum) {
171		/*
172		 * The FreeBSD agp driver removes allocations on release.
173		 * The Solaris driver doesn't.  xf86ReleaseGART() is expected
174		 * to give up access to the GART, but not to remove any
175		 * allocations.
176		 */
177
178	 	if (ioctl(gartFd, AGPIOC_RELEASE, 0) != 0) {
179			xf86DrvMsg(screenNum, X_WARNING,
180				"xf86ReleaseGART: AGPIOC_RELEASE failed (%s)\n",
181				strerror(errno));
182			return FALSE;
183		}
184		acquiredScreen = -1;
185		xf86DrvMsg(screenNum, X_INFO,
186			"xf86ReleaseGART: AGPIOC_RELEASE succeeded\n");
187	 	return TRUE;
188	}
189	return FALSE;
190}
191
192int
193xf86AllocateGARTMemory(int screenNum, unsigned long size, int type,
194			unsigned long *physical)
195{
196	agp_allocate_t alloc;
197	int pages;
198
199	/*
200	 * Allocates "size" bytes of GART memory (rounds up to the next
201	 * page multiple) or type "type".  A handle (key) for the allocated
202	 * memory is returned.  On error, the return value is -1.
203	 * "size" should be larger than 0, or AGPIOC_ALLOCATE ioctl will
204	 * return error.
205	 */
206
207	if (!GARTInit(screenNum) || (acquiredScreen != screenNum))
208		return -1;
209
210	pages = (size / AGP_PAGE_SIZE);
211	if (size % AGP_PAGE_SIZE != 0)
212		pages++;
213
214	alloc.agpa_pgcount = pages;
215	alloc.agpa_type = type;
216
217	if (ioctl(gartFd, AGPIOC_ALLOCATE, &alloc) != 0) {
218		xf86DrvMsg(screenNum, X_WARNING, "xf86AllocateGARTMemory: "
219		    "allocation of %d pages failed\n\t(%s)\n", pages,
220		    strerror(errno));
221		return -1;
222	}
223
224	if (physical)
225		*physical = (unsigned long)alloc.agpa_physical;
226
227	return alloc.agpa_key;
228}
229
230Bool
231xf86DeallocateGARTMemory(int screenNum, int key)
232{
233	if (!GARTInit(screenNum) || (acquiredScreen != screenNum))
234		return FALSE;
235
236 	if (ioctl(gartFd, AGPIOC_DEALLOCATE, (int *)key) != 0) {
237		xf86DrvMsg(screenNum, X_WARNING, "xf86DeAllocateGARTMemory: "
238			   "deallocation of gart memory with key %d failed\n"
239			   "\t(%s)\n", key, strerror(errno));
240		return FALSE;
241	}
242
243	return TRUE;
244}
245
246/* Bind GART memory with "key" at "offset" */
247Bool
248xf86BindGARTMemory(int screenNum, int key, unsigned long offset)
249{
250	agp_bind_t bind;
251	int pageOffset;
252
253	if (!GARTInit(screenNum) || (acquiredScreen != screenNum))
254		return FALSE;
255
256	if (offset % AGP_PAGE_SIZE != 0) {
257		xf86DrvMsg(screenNum, X_WARNING, "xf86BindGARTMemory: "
258		    "offset (0x%lx) is not page-aligned (%d)\n",
259		    offset, AGP_PAGE_SIZE);
260		return FALSE;
261	}
262	pageOffset = offset / AGP_PAGE_SIZE;
263
264	xf86DrvMsgVerb(screenNum, X_INFO, 3,
265	    "xf86BindGARTMemory: bind key %d at 0x%08lx "
266	    "(pgoffset %d)\n", key, offset, pageOffset);
267
268	bind.agpb_pgstart = pageOffset;
269	bind.agpb_key = key;
270
271	if (ioctl(gartFd, AGPIOC_BIND, &bind) != 0) {
272		xf86DrvMsg(screenNum, X_WARNING, "xf86BindGARTMemory: "
273		    "binding of gart memory with key %d\n"
274		    "\tat offset 0x%lx failed (%s)\n",
275		    key, offset, strerror(errno));
276		return FALSE;
277	}
278
279	return TRUE;
280}
281
282/* Unbind GART memory with "key" */
283Bool
284xf86UnbindGARTMemory(int screenNum, int key)
285{
286	agp_unbind_t unbind;
287
288	if (!GARTInit(screenNum) || (acquiredScreen != screenNum))
289		return FALSE;
290
291	unbind.agpu_pri = 0;
292	unbind.agpu_key = key;
293
294	if (ioctl(gartFd, AGPIOC_UNBIND, &unbind) != 0) {
295		xf86DrvMsg(screenNum, X_WARNING, "xf86UnbindGARTMemory: "
296		    "unbinding of gart memory with key %d "
297		    "failed (%s)\n", key, strerror(errno));
298		return FALSE;
299	}
300
301	xf86DrvMsgVerb(screenNum, X_INFO, 3,
302	    "xf86UnbindGARTMemory: unbind key %d\n", key);
303
304	return TRUE;
305}
306
307
308/* XXX Interface may change. */
309Bool
310xf86EnableAGP(int screenNum, CARD32 mode)
311{
312	agp_setup_t setup;
313
314	if (!GARTInit(screenNum) || (acquiredScreen != screenNum))
315		return FALSE;
316
317	setup.agps_mode = mode;
318	if (ioctl(gartFd, AGPIOC_SETUP, &setup) != 0) {
319		xf86DrvMsg(screenNum, X_WARNING, "xf86EnableAGP: "
320		    "AGPIOC_SETUP with mode %x failed (%s)\n",
321		    (unsigned int) mode, strerror(errno));
322		return FALSE;
323	}
324
325	return TRUE;
326}
327
328