1/*-
2 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
3 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * Author:
26 *    Rickard E. (Rik) Faith <faith@valinux.com>
27 *    Gareth Hughes <gareth@valinux.com>
28 *
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34/** @file drm_agpsupport.c
35 * Support code for tying the kernel AGP support to DRM drivers and
36 * the DRM's AGP ioctls.
37 */
38
39#include "dev/drm/drmP.h"
40
41#if __FreeBSD_version >= 800004
42#include <dev/agp/agpreg.h>
43#else /* __FreeBSD_version >= 800004 */
44#include <pci/agpreg.h>
45#endif /* __FreeBSD_version >= 800004 */
46#include <dev/pci/pcireg.h>
47
48/* Returns 1 if AGP or 0 if not. */
49static int
50drm_device_find_capability(struct drm_device *dev, int cap)
51{
52#if __FreeBSD_version >= 602102
53
54	return (pci_find_cap(dev->device, cap, NULL) == 0);
55#else
56	/* Code taken from agp.c.  IWBNI that was a public interface. */
57	u_int32_t status;
58	u_int8_t ptr, next;
59
60	/*
61	 * Check the CAP_LIST bit of the PCI status register first.
62	 */
63	status = pci_read_config(dev->device, PCIR_STATUS, 2);
64	if (!(status & 0x10))
65		return 0;
66
67	/*
68	 * Traverse the capabilities list.
69	 */
70	for (ptr = pci_read_config(dev->device, AGP_CAPPTR, 1);
71	     ptr != 0;
72	     ptr = next) {
73		u_int32_t capid = pci_read_config(dev->device, ptr, 4);
74		next = AGP_CAPID_GET_NEXT_PTR(capid);
75
76		/*
77		 * If this capability entry ID is cap, then we are done.
78		 */
79		if (AGP_CAPID_GET_CAP_ID(capid) == cap)
80			return 1;
81	}
82
83	return 0;
84#endif
85}
86
87int drm_device_is_agp(struct drm_device *dev)
88{
89	if (dev->driver->device_is_agp != NULL) {
90		int ret;
91
92		/* device_is_agp returns a tristate, 0 = not AGP, 1 = definitely
93		 * AGP, 2 = fall back to PCI capability
94		 */
95		ret = (*dev->driver->device_is_agp)(dev);
96		if (ret != DRM_MIGHT_BE_AGP)
97			return ret;
98	}
99
100	return (drm_device_find_capability(dev, PCIY_AGP));
101}
102
103int drm_device_is_pcie(struct drm_device *dev)
104{
105	return (drm_device_find_capability(dev, PCIY_EXPRESS));
106}
107
108int drm_agp_info(struct drm_device * dev, struct drm_agp_info *info)
109{
110	struct agp_info *kern;
111
112	if (!dev->agp || !dev->agp->acquired)
113		return EINVAL;
114
115	kern                   = &dev->agp->info;
116	agp_get_info(dev->agp->agpdev, kern);
117	info->agp_version_major = 1;
118	info->agp_version_minor = 0;
119	info->mode              = kern->ai_mode;
120	info->aperture_base     = kern->ai_aperture_base;
121	info->aperture_size     = kern->ai_aperture_size;
122	info->memory_allowed    = kern->ai_memory_allowed;
123	info->memory_used       = kern->ai_memory_used;
124	info->id_vendor         = kern->ai_devid & 0xffff;
125	info->id_device         = kern->ai_devid >> 16;
126
127	return 0;
128}
129
130int drm_agp_info_ioctl(struct drm_device *dev, void *data,
131		       struct drm_file *file_priv)
132{
133	int err;
134	struct drm_agp_info info;
135
136	err = drm_agp_info(dev, &info);
137	if (err != 0)
138		return err;
139
140	*(struct drm_agp_info *) data = info;
141	return 0;
142}
143
144int drm_agp_acquire_ioctl(struct drm_device *dev, void *data,
145			  struct drm_file *file_priv)
146{
147
148	return drm_agp_acquire(dev);
149}
150
151int drm_agp_acquire(struct drm_device *dev)
152{
153	int retcode;
154
155	if (!dev->agp || dev->agp->acquired)
156		return EINVAL;
157
158	retcode = agp_acquire(dev->agp->agpdev);
159	if (retcode)
160		return retcode;
161
162	dev->agp->acquired = 1;
163	return 0;
164}
165
166int drm_agp_release_ioctl(struct drm_device *dev, void *data,
167			  struct drm_file *file_priv)
168{
169
170	return drm_agp_release(dev);
171}
172
173int drm_agp_release(struct drm_device * dev)
174{
175	if (!dev->agp || !dev->agp->acquired)
176		return EINVAL;
177	agp_release(dev->agp->agpdev);
178	dev->agp->acquired = 0;
179	return 0;
180}
181
182int drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode)
183{
184
185	if (!dev->agp || !dev->agp->acquired)
186		return EINVAL;
187
188	dev->agp->mode    = mode.mode;
189	agp_enable(dev->agp->agpdev, mode.mode);
190	dev->agp->enabled = 1;
191	return 0;
192}
193
194int drm_agp_enable_ioctl(struct drm_device *dev, void *data,
195			 struct drm_file *file_priv)
196{
197	struct drm_agp_mode mode;
198
199	mode = *(struct drm_agp_mode *) data;
200
201	return drm_agp_enable(dev, mode);
202}
203
204int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
205{
206	drm_agp_mem_t    *entry;
207	void	         *handle;
208	unsigned long    pages;
209	u_int32_t	 type;
210	struct agp_memory_info info;
211
212	if (!dev->agp || !dev->agp->acquired)
213		return EINVAL;
214
215	entry = malloc(sizeof(*entry), DRM_MEM_AGPLISTS, M_NOWAIT | M_ZERO);
216	if (entry == NULL)
217		return ENOMEM;
218
219	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
220	type = (u_int32_t) request->type;
221
222	DRM_UNLOCK();
223	handle = drm_agp_allocate_memory(pages, type);
224	DRM_LOCK();
225	if (handle == NULL) {
226		free(entry, DRM_MEM_AGPLISTS);
227		return ENOMEM;
228	}
229
230	entry->handle    = handle;
231	entry->bound     = 0;
232	entry->pages     = pages;
233	entry->prev      = NULL;
234	entry->next      = dev->agp->memory;
235	if (dev->agp->memory)
236		dev->agp->memory->prev = entry;
237	dev->agp->memory = entry;
238
239	agp_memory_info(dev->agp->agpdev, entry->handle, &info);
240
241	request->handle   = (unsigned long) entry->handle;
242        request->physical = info.ami_physical;
243
244	return 0;
245}
246
247int drm_agp_alloc_ioctl(struct drm_device *dev, void *data,
248			struct drm_file *file_priv)
249{
250	struct drm_agp_buffer request;
251	int retcode;
252
253	request = *(struct drm_agp_buffer *) data;
254
255	DRM_LOCK();
256	retcode = drm_agp_alloc(dev, &request);
257	DRM_UNLOCK();
258
259	*(struct drm_agp_buffer *) data = request;
260
261	return retcode;
262}
263
264static drm_agp_mem_t * drm_agp_lookup_entry(struct drm_device *dev,
265					    void *handle)
266{
267	drm_agp_mem_t *entry;
268
269	for (entry = dev->agp->memory; entry; entry = entry->next) {
270		if (entry->handle == handle) return entry;
271	}
272	return NULL;
273}
274
275int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request)
276{
277	drm_agp_mem_t     *entry;
278	int retcode;
279
280	if (!dev->agp || !dev->agp->acquired)
281		return EINVAL;
282
283	entry = drm_agp_lookup_entry(dev, (void *)request->handle);
284	if (entry == NULL || !entry->bound)
285		return EINVAL;
286
287	DRM_UNLOCK();
288	retcode = drm_agp_unbind_memory(entry->handle);
289	DRM_LOCK();
290
291	if (retcode == 0)
292		entry->bound = 0;
293
294	return retcode;
295}
296
297int drm_agp_unbind_ioctl(struct drm_device *dev, void *data,
298			 struct drm_file *file_priv)
299{
300	struct drm_agp_binding request;
301	int retcode;
302
303	request = *(struct drm_agp_binding *) data;
304
305	DRM_LOCK();
306	retcode = drm_agp_unbind(dev, &request);
307	DRM_UNLOCK();
308
309	return retcode;
310}
311
312int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request)
313{
314	drm_agp_mem_t     *entry;
315	int               retcode;
316	int               page;
317
318	if (!dev->agp || !dev->agp->acquired)
319		return EINVAL;
320
321	DRM_DEBUG("agp_bind, page_size=%x\n", (int)PAGE_SIZE);
322
323	entry = drm_agp_lookup_entry(dev, (void *)request->handle);
324	if (entry == NULL || entry->bound)
325		return EINVAL;
326
327	page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE;
328
329	DRM_UNLOCK();
330	retcode = drm_agp_bind_memory(entry->handle, page);
331	DRM_LOCK();
332	if (retcode == 0)
333		entry->bound = dev->agp->base + (page << PAGE_SHIFT);
334
335	return retcode;
336}
337
338int drm_agp_bind_ioctl(struct drm_device *dev, void *data,
339		       struct drm_file *file_priv)
340{
341	struct drm_agp_binding request;
342	int retcode;
343
344	request = *(struct drm_agp_binding *) data;
345
346	DRM_LOCK();
347	retcode = drm_agp_bind(dev, &request);
348	DRM_UNLOCK();
349
350	return retcode;
351}
352
353int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request)
354{
355	drm_agp_mem_t    *entry;
356
357	if (!dev->agp || !dev->agp->acquired)
358		return EINVAL;
359
360	entry = drm_agp_lookup_entry(dev, (void*)request->handle);
361	if (entry == NULL)
362		return EINVAL;
363
364	if (entry->prev)
365		entry->prev->next = entry->next;
366	else
367		dev->agp->memory  = entry->next;
368	if (entry->next)
369		entry->next->prev = entry->prev;
370
371	DRM_UNLOCK();
372	if (entry->bound)
373		drm_agp_unbind_memory(entry->handle);
374	drm_agp_free_memory(entry->handle);
375	DRM_LOCK();
376
377	free(entry, DRM_MEM_AGPLISTS);
378
379	return 0;
380
381}
382
383int drm_agp_free_ioctl(struct drm_device *dev, void *data,
384		       struct drm_file *file_priv)
385{
386	struct drm_agp_buffer request;
387	int retcode;
388
389	request = *(struct drm_agp_buffer *) data;
390
391	DRM_LOCK();
392	retcode = drm_agp_free(dev, &request);
393	DRM_UNLOCK();
394
395	return retcode;
396}
397
398drm_agp_head_t *drm_agp_init(void)
399{
400	device_t agpdev;
401	drm_agp_head_t *head   = NULL;
402	int      agp_available = 1;
403
404	agpdev = DRM_AGP_FIND_DEVICE();
405	if (!agpdev)
406		agp_available = 0;
407
408	DRM_DEBUG("agp_available = %d\n", agp_available);
409
410	if (agp_available) {
411		head = malloc(sizeof(*head), DRM_MEM_AGPLISTS,
412		    M_NOWAIT | M_ZERO);
413		if (head == NULL)
414			return NULL;
415		head->agpdev = agpdev;
416		agp_get_info(agpdev, &head->info);
417		head->base = head->info.ai_aperture_base;
418		head->memory = NULL;
419		DRM_INFO("AGP at 0x%08lx %dMB\n",
420			 (long)head->info.ai_aperture_base,
421			 (int)(head->info.ai_aperture_size >> 20));
422	}
423	return head;
424}
425
426void *drm_agp_allocate_memory(size_t pages, u32 type)
427{
428	device_t agpdev;
429
430	agpdev = DRM_AGP_FIND_DEVICE();
431	if (!agpdev)
432		return NULL;
433
434	return agp_alloc_memory(agpdev, type, pages << AGP_PAGE_SHIFT);
435}
436
437int drm_agp_free_memory(void *handle)
438{
439	device_t agpdev;
440
441	agpdev = DRM_AGP_FIND_DEVICE();
442	if (!agpdev || !handle)
443		return 0;
444
445	agp_free_memory(agpdev, handle);
446	return 1;
447}
448
449int drm_agp_bind_memory(void *handle, off_t start)
450{
451	device_t agpdev;
452
453	agpdev = DRM_AGP_FIND_DEVICE();
454	if (!agpdev || !handle)
455		return EINVAL;
456
457	return agp_bind_memory(agpdev, handle, start * PAGE_SIZE);
458}
459
460int drm_agp_unbind_memory(void *handle)
461{
462	device_t agpdev;
463
464	agpdev = DRM_AGP_FIND_DEVICE();
465	if (!agpdev || !handle)
466		return EINVAL;
467
468	return agp_unbind_memory(agpdev, handle);
469}
470