1235783Skib/*-
2235783Skib * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
3235783Skib * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
4235783Skib * All Rights Reserved.
5235783Skib *
6235783Skib * Permission is hereby granted, free of charge, to any person obtaining a
7235783Skib * copy of this software and associated documentation files (the "Software"),
8235783Skib * to deal in the Software without restriction, including without limitation
9235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10235783Skib * and/or sell copies of the Software, and to permit persons to whom the
11235783Skib * Software is furnished to do so, subject to the following conditions:
12235783Skib *
13235783Skib * The above copyright notice and this permission notice (including the next
14235783Skib * paragraph) shall be included in all copies or substantial portions of the
15235783Skib * Software.
16235783Skib *
17235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20235783Skib * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21235783Skib * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22235783Skib * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23235783Skib * OTHER DEALINGS IN THE SOFTWARE.
24235783Skib *
25235783Skib * Author:
26235783Skib *    Rickard E. (Rik) Faith <faith@valinux.com>
27235783Skib *    Gareth Hughes <gareth@valinux.com>
28235783Skib *
29235783Skib */
30235783Skib
31235783Skib#include <sys/cdefs.h>
32235783Skib__FBSDID("$FreeBSD$");
33235783Skib
34235783Skib/** @file drm_agpsupport.c
35235783Skib * Support code for tying the kernel AGP support to DRM drivers and
36235783Skib * the DRM's AGP ioctls.
37235783Skib */
38235783Skib
39235783Skib#include <dev/drm2/drmP.h>
40235783Skib
41235783Skib#include <dev/agp/agpreg.h>
42235783Skib#include <dev/pci/pcireg.h>
43235783Skib
44235783Skib/* Returns 1 if AGP or 0 if not. */
45235783Skibstatic int
46235783Skibdrm_device_find_capability(struct drm_device *dev, int cap)
47235783Skib{
48235783Skib
49235783Skib	return (pci_find_cap(dev->device, cap, NULL) == 0);
50235783Skib}
51235783Skib
52235783Skibint drm_device_is_agp(struct drm_device *dev)
53235783Skib{
54235783Skib	if (dev->driver->device_is_agp != NULL) {
55235783Skib		int ret;
56235783Skib
57235783Skib		/* device_is_agp returns a tristate, 0 = not AGP, 1 = definitely
58235783Skib		 * AGP, 2 = fall back to PCI capability
59235783Skib		 */
60235783Skib		ret = (*dev->driver->device_is_agp)(dev);
61235783Skib		if (ret != DRM_MIGHT_BE_AGP)
62235783Skib			return ret;
63235783Skib	}
64235783Skib
65235783Skib	return (drm_device_find_capability(dev, PCIY_AGP));
66235783Skib}
67235783Skib
68235783Skibint drm_device_is_pcie(struct drm_device *dev)
69235783Skib{
70235783Skib	return (drm_device_find_capability(dev, PCIY_EXPRESS));
71235783Skib}
72235783Skib
73235783Skibint drm_agp_info(struct drm_device * dev, struct drm_agp_info *info)
74235783Skib{
75235783Skib	struct agp_info *kern;
76235783Skib
77235783Skib	if (!dev->agp || !dev->agp->acquired)
78235783Skib		return EINVAL;
79235783Skib
80235783Skib	kern                   = &dev->agp->info;
81235783Skib	agp_get_info(dev->agp->agpdev, kern);
82235783Skib	info->agp_version_major = 1;
83235783Skib	info->agp_version_minor = 0;
84235783Skib	info->mode              = kern->ai_mode;
85235783Skib	info->aperture_base     = kern->ai_aperture_base;
86235783Skib	info->aperture_size     = kern->ai_aperture_size;
87235783Skib	info->memory_allowed    = kern->ai_memory_allowed;
88235783Skib	info->memory_used       = kern->ai_memory_used;
89235783Skib	info->id_vendor         = kern->ai_devid & 0xffff;
90235783Skib	info->id_device         = kern->ai_devid >> 16;
91235783Skib
92235783Skib	return 0;
93235783Skib}
94235783Skib
95235783Skibint drm_agp_info_ioctl(struct drm_device *dev, void *data,
96235783Skib		       struct drm_file *file_priv)
97235783Skib{
98235783Skib	int err;
99235783Skib	struct drm_agp_info info;
100235783Skib
101235783Skib	err = drm_agp_info(dev, &info);
102235783Skib	if (err != 0)
103235783Skib		return err;
104235783Skib
105235783Skib	*(struct drm_agp_info *) data = info;
106235783Skib	return 0;
107235783Skib}
108235783Skib
109235783Skibint drm_agp_acquire_ioctl(struct drm_device *dev, void *data,
110235783Skib			  struct drm_file *file_priv)
111235783Skib{
112235783Skib
113235783Skib	return drm_agp_acquire(dev);
114235783Skib}
115235783Skib
116235783Skibint drm_agp_acquire(struct drm_device *dev)
117235783Skib{
118235783Skib	int retcode;
119235783Skib
120235783Skib	if (!dev->agp || dev->agp->acquired)
121235783Skib		return EINVAL;
122235783Skib
123235783Skib	retcode = agp_acquire(dev->agp->agpdev);
124235783Skib	if (retcode)
125235783Skib		return retcode;
126235783Skib
127235783Skib	dev->agp->acquired = 1;
128235783Skib	return 0;
129235783Skib}
130235783Skib
131235783Skibint drm_agp_release_ioctl(struct drm_device *dev, void *data,
132235783Skib			  struct drm_file *file_priv)
133235783Skib{
134235783Skib
135235783Skib	return drm_agp_release(dev);
136235783Skib}
137235783Skib
138235783Skibint drm_agp_release(struct drm_device * dev)
139235783Skib{
140235783Skib	if (!dev->agp || !dev->agp->acquired)
141235783Skib		return EINVAL;
142235783Skib	agp_release(dev->agp->agpdev);
143235783Skib	dev->agp->acquired = 0;
144235783Skib	return 0;
145235783Skib}
146235783Skib
147235783Skibint drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode)
148235783Skib{
149235783Skib
150235783Skib	if (!dev->agp || !dev->agp->acquired)
151235783Skib		return EINVAL;
152235783Skib
153235783Skib	dev->agp->mode    = mode.mode;
154235783Skib	agp_enable(dev->agp->agpdev, mode.mode);
155235783Skib	dev->agp->enabled = 1;
156235783Skib	return 0;
157235783Skib}
158235783Skib
159235783Skibint drm_agp_enable_ioctl(struct drm_device *dev, void *data,
160235783Skib			 struct drm_file *file_priv)
161235783Skib{
162235783Skib	struct drm_agp_mode mode;
163235783Skib
164235783Skib	mode = *(struct drm_agp_mode *) data;
165235783Skib
166235783Skib	return drm_agp_enable(dev, mode);
167235783Skib}
168235783Skib
169235783Skibint drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
170235783Skib{
171235783Skib	drm_agp_mem_t    *entry;
172235783Skib	void	         *handle;
173235783Skib	unsigned long    pages;
174235783Skib	u_int32_t	 type;
175235783Skib	struct agp_memory_info info;
176235783Skib
177235783Skib	if (!dev->agp || !dev->agp->acquired)
178235783Skib		return EINVAL;
179235783Skib
180235783Skib	entry = malloc(sizeof(*entry), DRM_MEM_AGPLISTS, M_NOWAIT | M_ZERO);
181235783Skib	if (entry == NULL)
182235783Skib		return ENOMEM;
183235783Skib
184235783Skib	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
185235783Skib	type = (u_int32_t) request->type;
186235783Skib
187235783Skib	DRM_UNLOCK(dev);
188235783Skib	handle = drm_agp_allocate_memory(pages, type);
189235783Skib	DRM_LOCK(dev);
190235783Skib	if (handle == NULL) {
191235783Skib		free(entry, DRM_MEM_AGPLISTS);
192235783Skib		return ENOMEM;
193235783Skib	}
194235783Skib
195235783Skib	entry->handle    = handle;
196235783Skib	entry->bound     = 0;
197235783Skib	entry->pages     = pages;
198235783Skib	entry->prev      = NULL;
199235783Skib	entry->next      = dev->agp->memory;
200235783Skib	if (dev->agp->memory)
201235783Skib		dev->agp->memory->prev = entry;
202235783Skib	dev->agp->memory = entry;
203235783Skib
204235783Skib	agp_memory_info(dev->agp->agpdev, entry->handle, &info);
205235783Skib
206235783Skib	request->handle   = (unsigned long) entry->handle;
207235783Skib        request->physical = info.ami_physical;
208235783Skib
209235783Skib	return 0;
210235783Skib}
211235783Skib
212235783Skibint drm_agp_alloc_ioctl(struct drm_device *dev, void *data,
213235783Skib			struct drm_file *file_priv)
214235783Skib{
215235783Skib	struct drm_agp_buffer request;
216235783Skib	int retcode;
217235783Skib
218235783Skib	request = *(struct drm_agp_buffer *) data;
219235783Skib
220235783Skib	DRM_LOCK(dev);
221235783Skib	retcode = drm_agp_alloc(dev, &request);
222235783Skib	DRM_UNLOCK(dev);
223235783Skib
224235783Skib	*(struct drm_agp_buffer *) data = request;
225235783Skib
226235783Skib	return retcode;
227235783Skib}
228235783Skib
229235783Skibstatic drm_agp_mem_t * drm_agp_lookup_entry(struct drm_device *dev,
230235783Skib					    void *handle)
231235783Skib{
232235783Skib	drm_agp_mem_t *entry;
233235783Skib
234235783Skib	for (entry = dev->agp->memory; entry; entry = entry->next) {
235235783Skib		if (entry->handle == handle) return entry;
236235783Skib	}
237235783Skib	return NULL;
238235783Skib}
239235783Skib
240235783Skibint drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request)
241235783Skib{
242235783Skib	drm_agp_mem_t     *entry;
243235783Skib	int retcode;
244235783Skib
245235783Skib	if (!dev->agp || !dev->agp->acquired)
246235783Skib		return EINVAL;
247235783Skib
248235783Skib	entry = drm_agp_lookup_entry(dev, (void *)request->handle);
249235783Skib	if (entry == NULL || !entry->bound)
250235783Skib		return EINVAL;
251235783Skib
252235783Skib	DRM_UNLOCK(dev);
253235783Skib	retcode = drm_agp_unbind_memory(entry->handle);
254235783Skib	DRM_LOCK(dev);
255235783Skib
256235783Skib	if (retcode == 0)
257235783Skib		entry->bound = 0;
258235783Skib
259235783Skib	return retcode;
260235783Skib}
261235783Skib
262235783Skibint drm_agp_unbind_ioctl(struct drm_device *dev, void *data,
263235783Skib			 struct drm_file *file_priv)
264235783Skib{
265235783Skib	struct drm_agp_binding request;
266235783Skib	int retcode;
267235783Skib
268235783Skib	request = *(struct drm_agp_binding *) data;
269235783Skib
270235783Skib	DRM_LOCK(dev);
271235783Skib	retcode = drm_agp_unbind(dev, &request);
272235783Skib	DRM_UNLOCK(dev);
273235783Skib
274235783Skib	return retcode;
275235783Skib}
276235783Skib
277235783Skibint drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request)
278235783Skib{
279235783Skib	drm_agp_mem_t     *entry;
280235783Skib	int               retcode;
281235783Skib	int               page;
282235783Skib
283235783Skib	if (!dev->agp || !dev->agp->acquired)
284235783Skib		return EINVAL;
285235783Skib
286235783Skib	DRM_DEBUG("agp_bind, page_size=%x\n", (int)PAGE_SIZE);
287235783Skib
288235783Skib	entry = drm_agp_lookup_entry(dev, (void *)request->handle);
289235783Skib	if (entry == NULL || entry->bound)
290235783Skib		return EINVAL;
291235783Skib
292235783Skib	page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE;
293235783Skib
294235783Skib	DRM_UNLOCK(dev);
295235783Skib	retcode = drm_agp_bind_memory(entry->handle, page);
296235783Skib	DRM_LOCK(dev);
297235783Skib	if (retcode == 0)
298235783Skib		entry->bound = dev->agp->base + (page << PAGE_SHIFT);
299235783Skib
300235783Skib	return retcode;
301235783Skib}
302235783Skib
303235783Skibint drm_agp_bind_ioctl(struct drm_device *dev, void *data,
304235783Skib		       struct drm_file *file_priv)
305235783Skib{
306235783Skib	struct drm_agp_binding request;
307235783Skib	int retcode;
308235783Skib
309235783Skib	request = *(struct drm_agp_binding *) data;
310235783Skib
311235783Skib	DRM_LOCK(dev);
312235783Skib	retcode = drm_agp_bind(dev, &request);
313235783Skib	DRM_UNLOCK(dev);
314235783Skib
315235783Skib	return retcode;
316235783Skib}
317235783Skib
318235783Skibint drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request)
319235783Skib{
320235783Skib	drm_agp_mem_t    *entry;
321235783Skib
322235783Skib	if (!dev->agp || !dev->agp->acquired)
323235783Skib		return EINVAL;
324235783Skib
325235783Skib	entry = drm_agp_lookup_entry(dev, (void*)request->handle);
326235783Skib	if (entry == NULL)
327235783Skib		return EINVAL;
328235783Skib
329235783Skib	if (entry->prev)
330235783Skib		entry->prev->next = entry->next;
331235783Skib	else
332235783Skib		dev->agp->memory  = entry->next;
333235783Skib	if (entry->next)
334235783Skib		entry->next->prev = entry->prev;
335235783Skib
336235783Skib	DRM_UNLOCK(dev);
337235783Skib	if (entry->bound)
338235783Skib		drm_agp_unbind_memory(entry->handle);
339235783Skib	drm_agp_free_memory(entry->handle);
340235783Skib	DRM_LOCK(dev);
341235783Skib
342235783Skib	free(entry, DRM_MEM_AGPLISTS);
343235783Skib
344235783Skib	return 0;
345235783Skib
346235783Skib}
347235783Skib
348235783Skibint drm_agp_free_ioctl(struct drm_device *dev, void *data,
349235783Skib		       struct drm_file *file_priv)
350235783Skib{
351235783Skib	struct drm_agp_buffer request;
352235783Skib	int retcode;
353235783Skib
354235783Skib	request = *(struct drm_agp_buffer *) data;
355235783Skib
356235783Skib	DRM_LOCK(dev);
357235783Skib	retcode = drm_agp_free(dev, &request);
358235783Skib	DRM_UNLOCK(dev);
359235783Skib
360235783Skib	return retcode;
361235783Skib}
362235783Skib
363235783Skibdrm_agp_head_t *drm_agp_init(void)
364235783Skib{
365235783Skib	device_t agpdev;
366235783Skib	drm_agp_head_t *head   = NULL;
367235783Skib	int      agp_available = 1;
368235783Skib
369235783Skib	agpdev = DRM_AGP_FIND_DEVICE();
370235783Skib	if (!agpdev)
371235783Skib		agp_available = 0;
372235783Skib
373235783Skib	DRM_DEBUG("agp_available = %d\n", agp_available);
374235783Skib
375235783Skib	if (agp_available) {
376235783Skib		head = malloc(sizeof(*head), DRM_MEM_AGPLISTS,
377235783Skib		    M_NOWAIT | M_ZERO);
378235783Skib		if (head == NULL)
379235783Skib			return NULL;
380235783Skib		head->agpdev = agpdev;
381235783Skib		agp_get_info(agpdev, &head->info);
382235783Skib		head->base = head->info.ai_aperture_base;
383235783Skib		head->memory = NULL;
384235783Skib		DRM_INFO("AGP at 0x%08lx %dMB\n",
385235783Skib			 (long)head->info.ai_aperture_base,
386235783Skib			 (int)(head->info.ai_aperture_size >> 20));
387235783Skib	}
388235783Skib	return head;
389235783Skib}
390235783Skib
391235783Skibvoid *drm_agp_allocate_memory(size_t pages, u32 type)
392235783Skib{
393235783Skib	device_t agpdev;
394235783Skib
395235783Skib	agpdev = DRM_AGP_FIND_DEVICE();
396235783Skib	if (!agpdev)
397235783Skib		return NULL;
398235783Skib
399235783Skib	return agp_alloc_memory(agpdev, type, pages << AGP_PAGE_SHIFT);
400235783Skib}
401235783Skib
402235783Skibint drm_agp_free_memory(void *handle)
403235783Skib{
404235783Skib	device_t agpdev;
405235783Skib
406235783Skib	agpdev = DRM_AGP_FIND_DEVICE();
407235783Skib	if (!agpdev || !handle)
408235783Skib		return 0;
409235783Skib
410235783Skib	agp_free_memory(agpdev, handle);
411235783Skib	return 1;
412235783Skib}
413235783Skib
414235783Skibint drm_agp_bind_memory(void *handle, off_t start)
415235783Skib{
416235783Skib	device_t agpdev;
417235783Skib
418235783Skib	agpdev = DRM_AGP_FIND_DEVICE();
419235783Skib	if (!agpdev || !handle)
420235783Skib		return EINVAL;
421235783Skib
422235783Skib	return agp_bind_memory(agpdev, handle, start * PAGE_SIZE);
423235783Skib}
424235783Skib
425235783Skibint drm_agp_unbind_memory(void *handle)
426235783Skib{
427235783Skib	device_t agpdev;
428235783Skib
429235783Skib	agpdev = DRM_AGP_FIND_DEVICE();
430235783Skib	if (!agpdev || !handle)
431235783Skib		return EINVAL;
432235783Skib
433235783Skib	return agp_unbind_memory(agpdev, handle);
434235783Skib}
435