xref: /dragonfly/sys/dev/drm/drm_agpsupport.c (revision 62f7f702)
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  * $DragonFly: src/sys/dev/drm/drm_agpsupport.c,v 1.1 2008/04/05 18:12:29 hasso Exp $
30  */
31 
32 /** @file drm_agpsupport.c
33  * Support code for tying the kernel AGP support to DRM drivers and
34  * the DRM's AGP ioctls.
35  */
36 
37 #include "drmP.h"
38 
39 #ifdef __FreeBSD__
40 #if __FreeBSD_version >= 800004
41 #include <dev/agp/agpreg.h>
42 #else /* __FreeBSD_version >= 800004 */
43 #include <pci/agpreg.h>
44 #endif /* __FreeBSD_version >= 800004 */
45 #include <dev/pci/pcireg.h>
46 #elif defined(__DragonFly__)
47 #include <dev/agp/agpreg.h>
48 #include <bus/pci/pcireg.h>
49 #endif
50 
51 /* Returns 1 if AGP or 0 if not. */
52 static int
53 drm_device_find_capability(drm_device_t *dev, int cap)
54 {
55 #if defined(__FreeBSD__) || defined(__DragonFly__)
56 #if __FreeBSD_version >= 602102
57 
58 	return (pci_find_extcap(dev->device, cap, NULL) == 0);
59 #else
60 	/* Code taken from agp.c.  IWBNI that was a public interface. */
61 	u_int32_t status;
62 	u_int8_t ptr, next;
63 
64 	/*
65 	 * Check the CAP_LIST bit of the PCI status register first.
66 	 */
67 	status = pci_read_config(dev->device, PCIR_STATUS, 2);
68 	if (!(status & 0x10))
69 		return 0;
70 
71 	/*
72 	 * Traverse the capabilities list.
73 	 */
74 	for (ptr = pci_read_config(dev->device, AGP_CAPPTR, 1);
75 	     ptr != 0;
76 	     ptr = next) {
77 		u_int32_t capid = pci_read_config(dev->device, ptr, 4);
78 		next = AGP_CAPID_GET_NEXT_PTR(capid);
79 
80 		/*
81 		 * If this capability entry ID is cap, then we are done.
82 		 */
83 		if (AGP_CAPID_GET_CAP_ID(capid) == cap)
84 			return 1;
85 	}
86 
87 	return 0;
88 #endif
89 #else
90 	/* XXX: fill me in for non-FreeBSD */
91 	return 1;
92 #endif
93 }
94 
95 int drm_device_is_agp(drm_device_t *dev)
96 {
97 	if (dev->driver.device_is_agp != NULL) {
98 		int ret;
99 
100 		/* device_is_agp returns a tristate, 0 = not AGP, 1 = definitely
101 		 * AGP, 2 = fall back to PCI capability
102 		 */
103 		ret = (*dev->driver.device_is_agp)(dev);
104 		if (ret != DRM_MIGHT_BE_AGP)
105 			return ret;
106 	}
107 
108 	return (drm_device_find_capability(dev, PCIY_AGP));
109 }
110 
111 int drm_device_is_pcie(drm_device_t *dev)
112 {
113 	return (drm_device_find_capability(dev, PCIY_EXPRESS));
114 }
115 
116 int drm_agp_info(drm_device_t * dev, drm_agp_info_t *info)
117 {
118 	struct agp_info *kern;
119 
120 	if (!dev->agp || !dev->agp->acquired)
121 		return EINVAL;
122 
123 	kern                   = &dev->agp->info;
124 	agp_get_info(dev->agp->agpdev, kern);
125 	info->agp_version_major = 1;
126 	info->agp_version_minor = 0;
127 	info->mode              = kern->ai_mode;
128 	info->aperture_base     = kern->ai_aperture_base;
129 	info->aperture_size     = kern->ai_aperture_size;
130 	info->memory_allowed    = kern->ai_memory_allowed;
131 	info->memory_used       = kern->ai_memory_used;
132 	info->id_vendor         = kern->ai_devid & 0xffff;
133 	info->id_device         = kern->ai_devid >> 16;
134 
135 	return 0;
136 }
137 
138 int drm_agp_info_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
139 {
140 	int err;
141 	drm_agp_info_t info;
142 
143 	err = drm_agp_info(dev, &info);
144 	if (err != 0)
145 		return err;
146 
147 	*(drm_agp_info_t *) data = info;
148 	return 0;
149 }
150 
151 int drm_agp_acquire_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
152 {
153 
154 	return drm_agp_acquire(dev);
155 }
156 
157 int drm_agp_acquire(drm_device_t *dev)
158 {
159 	int retcode;
160 
161 	if (!dev->agp || dev->agp->acquired)
162 		return EINVAL;
163 
164 	retcode = agp_acquire(dev->agp->agpdev);
165 	if (retcode)
166 		return retcode;
167 
168 	dev->agp->acquired = 1;
169 	return 0;
170 }
171 
172 int drm_agp_release_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
173 {
174 
175 	return drm_agp_release(dev);
176 }
177 
178 int drm_agp_release(drm_device_t * dev)
179 {
180 	if (!dev->agp || !dev->agp->acquired)
181 		return EINVAL;
182 	agp_release(dev->agp->agpdev);
183 	dev->agp->acquired = 0;
184 	return 0;
185 }
186 
187 int drm_agp_enable(drm_device_t *dev, drm_agp_mode_t mode)
188 {
189 
190 	if (!dev->agp || !dev->agp->acquired)
191 		return EINVAL;
192 
193 	dev->agp->mode    = mode.mode;
194 	agp_enable(dev->agp->agpdev, mode.mode);
195 	dev->agp->enabled = 1;
196 	return 0;
197 }
198 
199 int drm_agp_enable_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
200 {
201 	drm_agp_mode_t mode;
202 
203 	mode = *(drm_agp_mode_t *) data;
204 
205 	return drm_agp_enable(dev, mode);
206 }
207 
208 int drm_agp_alloc(drm_device_t *dev, drm_agp_buffer_t *request)
209 {
210 	drm_agp_mem_t    *entry;
211 	void	         *handle;
212 	unsigned long    pages;
213 	u_int32_t	 type;
214 	struct agp_memory_info info;
215 
216 	if (!dev->agp || !dev->agp->acquired)
217 		return EINVAL;
218 
219 	entry = malloc(sizeof(*entry), M_DRM, M_NOWAIT | M_ZERO);
220 	if (entry == NULL)
221 		return ENOMEM;
222 
223 	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
224 	type = (u_int32_t) request->type;
225 
226 	DRM_UNLOCK();
227 	handle = drm_agp_allocate_memory(pages, type);
228 	DRM_LOCK();
229 	if (handle == NULL) {
230 		free(entry, M_DRM);
231 		return ENOMEM;
232 	}
233 
234 	entry->handle    = handle;
235 	entry->bound     = 0;
236 	entry->pages     = pages;
237 	entry->prev      = NULL;
238 	entry->next      = dev->agp->memory;
239 	if (dev->agp->memory)
240 		dev->agp->memory->prev = entry;
241 	dev->agp->memory = entry;
242 
243 	agp_memory_info(dev->agp->agpdev, entry->handle, &info);
244 
245 	request->handle   = (unsigned long) entry->handle;
246         request->physical = info.ami_physical;
247 
248 	return 0;
249 }
250 
251 int drm_agp_alloc_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
252 {
253 	drm_agp_buffer_t request;
254 	int retcode;
255 
256 	request = *(drm_agp_buffer_t *) data;
257 
258 	DRM_LOCK();
259 	retcode = drm_agp_alloc(dev, &request);
260 	DRM_UNLOCK();
261 
262 	*(drm_agp_buffer_t *) data = request;
263 
264 	return retcode;
265 }
266 
267 static drm_agp_mem_t * drm_agp_lookup_entry(drm_device_t *dev, void *handle)
268 {
269 	drm_agp_mem_t *entry;
270 
271 	for (entry = dev->agp->memory; entry; entry = entry->next) {
272 		if (entry->handle == handle) return entry;
273 	}
274 	return NULL;
275 }
276 
277 int drm_agp_unbind(drm_device_t *dev, drm_agp_binding_t *request)
278 {
279 	drm_agp_mem_t     *entry;
280 	int retcode;
281 
282 	if (!dev->agp || !dev->agp->acquired)
283 		return EINVAL;
284 
285 	entry = drm_agp_lookup_entry(dev, (void *)request->handle);
286 	if (entry == NULL || !entry->bound)
287 		return EINVAL;
288 
289 	DRM_UNLOCK();
290 	retcode = drm_agp_unbind_memory(entry->handle);
291 	DRM_LOCK();
292 
293 	if (retcode == 0)
294 		entry->bound = 0;
295 
296 	return retcode;
297 }
298 
299 int drm_agp_unbind_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
300 {
301 	drm_agp_binding_t request;
302 	int retcode;
303 
304 	request = *(drm_agp_binding_t *) data;
305 
306 	DRM_LOCK();
307 	retcode = drm_agp_unbind(dev, &request);
308 	DRM_UNLOCK();
309 
310 	return retcode;
311 }
312 
313 int drm_agp_bind(drm_device_t *dev, drm_agp_binding_t *request)
314 {
315 	drm_agp_mem_t     *entry;
316 	int               retcode;
317 	int               page;
318 
319 	if (!dev->agp || !dev->agp->acquired)
320 		return EINVAL;
321 
322 	DRM_DEBUG("agp_bind, page_size=%x\n", PAGE_SIZE);
323 
324 	entry = drm_agp_lookup_entry(dev, (void *)request->handle);
325 	if (entry == NULL || entry->bound)
326 		return EINVAL;
327 
328 	page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE;
329 
330 	DRM_UNLOCK();
331 	retcode = drm_agp_bind_memory(entry->handle, page);
332 	DRM_LOCK();
333 	if (retcode == 0)
334 		entry->bound = dev->agp->base + (page << PAGE_SHIFT);
335 
336 	return retcode;
337 }
338 
339 int drm_agp_bind_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
340 {
341 	drm_agp_binding_t request;
342 	int retcode;
343 
344 	request = *(drm_agp_binding_t *) data;
345 
346 	DRM_LOCK();
347 	retcode = drm_agp_bind(dev, &request);
348 	DRM_UNLOCK();
349 
350 	return retcode;
351 }
352 
353 int drm_agp_free(drm_device_t *dev, drm_agp_buffer_t *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, M_DRM);
378 
379 	return 0;
380 
381 }
382 
383 int drm_agp_free_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv)
384 {
385 	drm_agp_buffer_t request;
386 	int retcode;
387 
388 	request = *(drm_agp_buffer_t *) data;
389 
390 	DRM_LOCK();
391 	retcode = drm_agp_free(dev, &request);
392 	DRM_UNLOCK();
393 
394 	return retcode;
395 }
396 
397 drm_agp_head_t *drm_agp_init(void)
398 {
399 	device_t agpdev;
400 	drm_agp_head_t *head   = NULL;
401 	int      agp_available = 1;
402 
403 	agpdev = DRM_AGP_FIND_DEVICE();
404 	if (!agpdev)
405 		agp_available = 0;
406 
407 	DRM_DEBUG("agp_available = %d\n", agp_available);
408 
409 	if (agp_available) {
410 		head = malloc(sizeof(*head), M_DRM, M_NOWAIT | M_ZERO);
411 		if (head == NULL)
412 			return NULL;
413 		head->agpdev = agpdev;
414 		agp_get_info(agpdev, &head->info);
415 		head->base = head->info.ai_aperture_base;
416 		head->memory = NULL;
417 		DRM_INFO("AGP at 0x%08lx %dMB\n",
418 			 (long)head->info.ai_aperture_base,
419 			 (int)(head->info.ai_aperture_size >> 20));
420 	}
421 	return head;
422 }
423 
424 void *drm_agp_allocate_memory(size_t pages, u32 type)
425 {
426 	device_t agpdev;
427 
428 	agpdev = DRM_AGP_FIND_DEVICE();
429 	if (!agpdev)
430 		return NULL;
431 
432 	return agp_alloc_memory(agpdev, type, pages << AGP_PAGE_SHIFT);
433 }
434 
435 int drm_agp_free_memory(void *handle)
436 {
437 	device_t agpdev;
438 
439 	agpdev = DRM_AGP_FIND_DEVICE();
440 	if (!agpdev || !handle)
441 		return 0;
442 
443 	agp_free_memory(agpdev, handle);
444 	return 1;
445 }
446 
447 int drm_agp_bind_memory(void *handle, off_t start)
448 {
449 	device_t agpdev;
450 
451 	agpdev = DRM_AGP_FIND_DEVICE();
452 	if (!agpdev || !handle)
453 		return EINVAL;
454 
455 	return agp_bind_memory(agpdev, handle, start * PAGE_SIZE);
456 }
457 
458 int drm_agp_unbind_memory(void *handle)
459 {
460 	device_t agpdev;
461 
462 	agpdev = DRM_AGP_FIND_DEVICE();
463 	if (!agpdev || !handle)
464 		return EINVAL;
465 
466 	return agp_unbind_memory(agpdev, handle);
467 }
468