1280183Sdumbbell/**
2280183Sdumbbell * \file drm_agpsupport.c
3280183Sdumbbell * DRM support for AGP/GART backend
4280183Sdumbbell *
5280183Sdumbbell * \author Rickard E. (Rik) Faith <faith@valinux.com>
6280183Sdumbbell * \author Gareth Hughes <gareth@valinux.com>
7280183Sdumbbell */
8280183Sdumbbell
9280183Sdumbbell/*
10235783Skib * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
11235783Skib * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
12235783Skib * All Rights Reserved.
13235783Skib *
14235783Skib * Permission is hereby granted, free of charge, to any person obtaining a
15235783Skib * copy of this software and associated documentation files (the "Software"),
16235783Skib * to deal in the Software without restriction, including without limitation
17235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18235783Skib * and/or sell copies of the Software, and to permit persons to whom the
19235783Skib * Software is furnished to do so, subject to the following conditions:
20235783Skib *
21235783Skib * The above copyright notice and this permission notice (including the next
22235783Skib * paragraph) shall be included in all copies or substantial portions of the
23235783Skib * Software.
24235783Skib *
25235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28235783Skib * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29235783Skib * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30235783Skib * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
31235783Skib * OTHER DEALINGS IN THE SOFTWARE.
32235783Skib */
33235783Skib
34235783Skib#include <sys/cdefs.h>
35235783Skib__FBSDID("$FreeBSD$");
36235783Skib
37235783Skib#include <dev/drm2/drmP.h>
38235783Skib
39280183Sdumbbell#if __OS_HAS_AGP
40235783Skib
41280183Sdumbbell/**
42280183Sdumbbell * Get AGP information.
43280183Sdumbbell *
44280183Sdumbbell * \param inode device inode.
45280183Sdumbbell * \param file_priv DRM file private.
46280183Sdumbbell * \param cmd command.
47280183Sdumbbell * \param arg pointer to a (output) drm_agp_info structure.
48280183Sdumbbell * \return zero on success or a negative number on failure.
49280183Sdumbbell *
50280183Sdumbbell * Verifies the AGP device has been initialized and acquired and fills in the
51280183Sdumbbell * drm_agp_info structure with the information in drm_agp_head::agp_info.
52280183Sdumbbell */
53280183Sdumbbellint drm_agp_info(struct drm_device *dev, struct drm_agp_info *info)
54235783Skib{
55280183Sdumbbell	DRM_AGP_KERN *kern;
56235783Skib
57235783Skib	if (!dev->agp || !dev->agp->acquired)
58280183Sdumbbell		return -EINVAL;
59235783Skib
60280183Sdumbbell	kern = &dev->agp->agp_info;
61280183Sdumbbell	agp_get_info(dev->agp->bridge, kern);
62235783Skib	info->agp_version_major = 1;
63235783Skib	info->agp_version_minor = 0;
64235783Skib	info->mode              = kern->ai_mode;
65235783Skib	info->aperture_base     = kern->ai_aperture_base;
66235783Skib	info->aperture_size     = kern->ai_aperture_size;
67235783Skib	info->memory_allowed    = kern->ai_memory_allowed;
68235783Skib	info->memory_used       = kern->ai_memory_used;
69235783Skib	info->id_vendor         = kern->ai_devid & 0xffff;
70235783Skib	info->id_device         = kern->ai_devid >> 16;
71235783Skib
72235783Skib	return 0;
73235783Skib}
74235783Skib
75280183SdumbbellEXPORT_SYMBOL(drm_agp_info);
76280183Sdumbbell
77235783Skibint drm_agp_info_ioctl(struct drm_device *dev, void *data,
78235783Skib		       struct drm_file *file_priv)
79235783Skib{
80280183Sdumbbell	struct drm_agp_info *info = data;
81235783Skib	int err;
82235783Skib
83280183Sdumbbell	err = drm_agp_info(dev, info);
84280183Sdumbbell	if (err)
85235783Skib		return err;
86235783Skib
87235783Skib	return 0;
88235783Skib}
89235783Skib
90280183Sdumbbell/**
91280183Sdumbbell * Acquire the AGP device.
92280183Sdumbbell *
93280183Sdumbbell * \param dev DRM device that is to acquire AGP.
94280183Sdumbbell * \return zero on success or a negative number on failure.
95280183Sdumbbell *
96280183Sdumbbell * Verifies the AGP device hasn't been acquired before and calls
97280183Sdumbbell * \c agp_backend_acquire.
98280183Sdumbbell */
99280183Sdumbbellint drm_agp_acquire(struct drm_device * dev)
100235783Skib{
101235783Skib	int retcode;
102235783Skib
103280183Sdumbbell	if (!dev->agp)
104280183Sdumbbell		return -ENODEV;
105280183Sdumbbell	if (dev->agp->acquired)
106280183Sdumbbell		return -EBUSY;
107280183Sdumbbell	retcode = agp_acquire(dev->agp->bridge);
108235783Skib	if (retcode)
109280183Sdumbbell		return -retcode;
110235783Skib	dev->agp->acquired = 1;
111235783Skib	return 0;
112235783Skib}
113235783Skib
114280183SdumbbellEXPORT_SYMBOL(drm_agp_acquire);
115280183Sdumbbell
116280183Sdumbbell/**
117280183Sdumbbell * Acquire the AGP device (ioctl).
118280183Sdumbbell *
119280183Sdumbbell * \param inode device inode.
120280183Sdumbbell * \param file_priv DRM file private.
121280183Sdumbbell * \param cmd command.
122280183Sdumbbell * \param arg user argument.
123280183Sdumbbell * \return zero on success or a negative number on failure.
124280183Sdumbbell *
125280183Sdumbbell * Verifies the AGP device hasn't been acquired before and calls
126280183Sdumbbell * \c agp_backend_acquire.
127280183Sdumbbell */
128280183Sdumbbellint drm_agp_acquire_ioctl(struct drm_device *dev, void *data,
129235783Skib			  struct drm_file *file_priv)
130235783Skib{
131280183Sdumbbell	return drm_agp_acquire((struct drm_device *) file_priv->minor->dev);
132235783Skib}
133235783Skib
134280183Sdumbbell/**
135280183Sdumbbell * Release the AGP device.
136280183Sdumbbell *
137280183Sdumbbell * \param dev DRM device that is to release AGP.
138280183Sdumbbell * \return zero on success or a negative number on failure.
139280183Sdumbbell *
140280183Sdumbbell * Verifies the AGP device has been acquired and calls \c agp_backend_release.
141280183Sdumbbell */
142235783Skibint drm_agp_release(struct drm_device * dev)
143235783Skib{
144235783Skib	if (!dev->agp || !dev->agp->acquired)
145280183Sdumbbell		return -EINVAL;
146280183Sdumbbell	agp_release(dev->agp->bridge);
147235783Skib	dev->agp->acquired = 0;
148235783Skib	return 0;
149235783Skib}
150280183SdumbbellEXPORT_SYMBOL(drm_agp_release);
151235783Skib
152280183Sdumbbellint drm_agp_release_ioctl(struct drm_device *dev, void *data,
153280183Sdumbbell			  struct drm_file *file_priv)
154235783Skib{
155280183Sdumbbell	return drm_agp_release(dev);
156280183Sdumbbell}
157235783Skib
158280183Sdumbbell/**
159280183Sdumbbell * Enable the AGP bus.
160280183Sdumbbell *
161280183Sdumbbell * \param dev DRM device that has previously acquired AGP.
162280183Sdumbbell * \param mode Requested AGP mode.
163280183Sdumbbell * \return zero on success or a negative number on failure.
164280183Sdumbbell *
165280183Sdumbbell * Verifies the AGP device has been acquired but not enabled, and calls
166280183Sdumbbell * \c agp_enable.
167280183Sdumbbell */
168280183Sdumbbellint drm_agp_enable(struct drm_device * dev, struct drm_agp_mode mode)
169280183Sdumbbell{
170235783Skib	if (!dev->agp || !dev->agp->acquired)
171280183Sdumbbell		return -EINVAL;
172280183Sdumbbell
173280183Sdumbbell	dev->agp->mode = mode.mode;
174280183Sdumbbell	agp_enable(dev->agp->bridge, mode.mode);
175235783Skib	dev->agp->enabled = 1;
176235783Skib	return 0;
177235783Skib}
178235783Skib
179280183SdumbbellEXPORT_SYMBOL(drm_agp_enable);
180280183Sdumbbell
181235783Skibint drm_agp_enable_ioctl(struct drm_device *dev, void *data,
182235783Skib			 struct drm_file *file_priv)
183235783Skib{
184280183Sdumbbell	struct drm_agp_mode *mode = data;
185235783Skib
186280183Sdumbbell	return drm_agp_enable(dev, *mode);
187235783Skib}
188235783Skib
189280183Sdumbbell/**
190280183Sdumbbell * Allocate AGP memory.
191280183Sdumbbell *
192280183Sdumbbell * \param inode device inode.
193280183Sdumbbell * \param file_priv file private pointer.
194280183Sdumbbell * \param cmd command.
195280183Sdumbbell * \param arg pointer to a drm_agp_buffer structure.
196280183Sdumbbell * \return zero on success or a negative number on failure.
197280183Sdumbbell *
198280183Sdumbbell * Verifies the AGP device is present and has been acquired, allocates the
199280183Sdumbbell * memory via agp_allocate_memory() and creates a drm_agp_mem entry for it.
200280183Sdumbbell */
201235783Skibint drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
202235783Skib{
203280183Sdumbbell	struct drm_agp_mem *entry;
204280183Sdumbbell	DRM_AGP_MEM *memory;
205280183Sdumbbell	unsigned long pages;
206280183Sdumbbell	u32 type;
207235783Skib	struct agp_memory_info info;
208235783Skib
209235783Skib	if (!dev->agp || !dev->agp->acquired)
210280183Sdumbbell		return -EINVAL;
211280183Sdumbbell	if (!(entry = malloc(sizeof(*entry), DRM_MEM_AGPLISTS, M_NOWAIT)))
212280183Sdumbbell		return -ENOMEM;
213235783Skib
214280183Sdumbbell	memset(entry, 0, sizeof(*entry));
215235783Skib
216235783Skib	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
217280183Sdumbbell	type = (u32) request->type;
218280183Sdumbbell	if (!(memory = agp_alloc_memory(dev->agp->bridge, type, pages << PAGE_SHIFT))) {
219235783Skib		free(entry, DRM_MEM_AGPLISTS);
220280183Sdumbbell		return -ENOMEM;
221235783Skib	}
222235783Skib
223280183Sdumbbell	entry->handle = (unsigned long)memory;
224280183Sdumbbell	entry->memory = memory;
225280183Sdumbbell	entry->bound = 0;
226280183Sdumbbell	entry->pages = pages;
227280183Sdumbbell	list_add(&entry->head, &dev->agp->memory);
228235783Skib
229280183Sdumbbell	agp_memory_info(dev->agp->bridge, entry->memory, &info);
230235783Skib
231280183Sdumbbell	request->handle = entry->handle;
232280183Sdumbbell	request->physical = info.ami_physical;
233280183Sdumbbell
234235783Skib	return 0;
235235783Skib}
236280183SdumbbellEXPORT_SYMBOL(drm_agp_alloc);
237235783Skib
238280183Sdumbbell
239235783Skibint drm_agp_alloc_ioctl(struct drm_device *dev, void *data,
240235783Skib			struct drm_file *file_priv)
241235783Skib{
242280183Sdumbbell	struct drm_agp_buffer *request = data;
243235783Skib
244280183Sdumbbell	return drm_agp_alloc(dev, request);
245235783Skib}
246235783Skib
247280183Sdumbbell/**
248280183Sdumbbell * Search for the AGP memory entry associated with a handle.
249280183Sdumbbell *
250280183Sdumbbell * \param dev DRM device structure.
251280183Sdumbbell * \param handle AGP memory handle.
252280183Sdumbbell * \return pointer to the drm_agp_mem structure associated with \p handle.
253280183Sdumbbell *
254280183Sdumbbell * Walks through drm_agp_head::memory until finding a matching handle.
255280183Sdumbbell */
256280183Sdumbbellstatic struct drm_agp_mem *drm_agp_lookup_entry(struct drm_device * dev,
257280183Sdumbbell					   unsigned long handle)
258235783Skib{
259280183Sdumbbell	struct drm_agp_mem *entry;
260235783Skib
261280183Sdumbbell	list_for_each_entry(entry, &dev->agp->memory, head) {
262280183Sdumbbell		if (entry->handle == handle)
263280183Sdumbbell			return entry;
264235783Skib	}
265235783Skib	return NULL;
266235783Skib}
267235783Skib
268280183Sdumbbell/**
269280183Sdumbbell * Unbind AGP memory from the GATT (ioctl).
270280183Sdumbbell *
271280183Sdumbbell * \param inode device inode.
272280183Sdumbbell * \param file_priv DRM file private.
273280183Sdumbbell * \param cmd command.
274280183Sdumbbell * \param arg pointer to a drm_agp_binding structure.
275280183Sdumbbell * \return zero on success or a negative number on failure.
276280183Sdumbbell *
277280183Sdumbbell * Verifies the AGP device is present and acquired, looks-up the AGP memory
278280183Sdumbbell * entry and passes it to the unbind_agp() function.
279280183Sdumbbell */
280235783Skibint drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request)
281235783Skib{
282280183Sdumbbell	struct drm_agp_mem *entry;
283280183Sdumbbell	int ret;
284235783Skib
285235783Skib	if (!dev->agp || !dev->agp->acquired)
286280183Sdumbbell		return -EINVAL;
287280183Sdumbbell	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
288280183Sdumbbell		return -EINVAL;
289280183Sdumbbell	if (!entry->bound)
290280183Sdumbbell		return -EINVAL;
291280183Sdumbbell	ret = drm_unbind_agp(entry->memory);
292280183Sdumbbell	if (ret == 0)
293235783Skib		entry->bound = 0;
294280183Sdumbbell	return ret;
295235783Skib}
296280183SdumbbellEXPORT_SYMBOL(drm_agp_unbind);
297235783Skib
298280183Sdumbbell
299235783Skibint drm_agp_unbind_ioctl(struct drm_device *dev, void *data,
300235783Skib			 struct drm_file *file_priv)
301235783Skib{
302280183Sdumbbell	struct drm_agp_binding *request = data;
303235783Skib
304280183Sdumbbell	return drm_agp_unbind(dev, request);
305235783Skib}
306235783Skib
307280183Sdumbbell/**
308280183Sdumbbell * Bind AGP memory into the GATT (ioctl)
309280183Sdumbbell *
310280183Sdumbbell * \param inode device inode.
311280183Sdumbbell * \param file_priv DRM file private.
312280183Sdumbbell * \param cmd command.
313280183Sdumbbell * \param arg pointer to a drm_agp_binding structure.
314280183Sdumbbell * \return zero on success or a negative number on failure.
315280183Sdumbbell *
316280183Sdumbbell * Verifies the AGP device is present and has been acquired and that no memory
317280183Sdumbbell * is currently bound into the GATT. Looks-up the AGP memory entry and passes
318280183Sdumbbell * it to bind_agp() function.
319280183Sdumbbell */
320235783Skibint drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request)
321235783Skib{
322280183Sdumbbell	struct drm_agp_mem *entry;
323280183Sdumbbell	int retcode;
324280183Sdumbbell	int page;
325280183Sdumbbell
326235783Skib	if (!dev->agp || !dev->agp->acquired)
327280183Sdumbbell		return -EINVAL;
328280183Sdumbbell	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
329280183Sdumbbell		return -EINVAL;
330280183Sdumbbell	if (entry->bound)
331280183Sdumbbell		return -EINVAL;
332235783Skib	page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE;
333280183Sdumbbell	if ((retcode = drm_bind_agp(entry->memory, page)))
334280183Sdumbbell		return retcode;
335280183Sdumbbell	entry->bound = dev->agp->base + (page << PAGE_SHIFT);
336280183Sdumbbell	DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n",
337280183Sdumbbell		  dev->agp->base, entry->bound);
338280183Sdumbbell	return 0;
339280183Sdumbbell}
340280183SdumbbellEXPORT_SYMBOL(drm_agp_bind);
341235783Skib
342235783Skib
343235783Skibint drm_agp_bind_ioctl(struct drm_device *dev, void *data,
344235783Skib		       struct drm_file *file_priv)
345235783Skib{
346280183Sdumbbell	struct drm_agp_binding *request = data;
347235783Skib
348280183Sdumbbell	return drm_agp_bind(dev, request);
349235783Skib}
350235783Skib
351280183Sdumbbell/**
352280183Sdumbbell * Free AGP memory (ioctl).
353280183Sdumbbell *
354280183Sdumbbell * \param inode device inode.
355280183Sdumbbell * \param file_priv DRM file private.
356280183Sdumbbell * \param cmd command.
357280183Sdumbbell * \param arg pointer to a drm_agp_buffer structure.
358280183Sdumbbell * \return zero on success or a negative number on failure.
359280183Sdumbbell *
360280183Sdumbbell * Verifies the AGP device is present and has been acquired and looks up the
361280183Sdumbbell * AGP memory entry. If the memory it's currently bound, unbind it via
362280183Sdumbbell * unbind_agp(). Frees it via free_agp() as well as the entry itself
363280183Sdumbbell * and unlinks from the doubly linked list it's inserted in.
364280183Sdumbbell */
365235783Skibint drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request)
366235783Skib{
367280183Sdumbbell	struct drm_agp_mem *entry;
368280183Sdumbbell
369235783Skib	if (!dev->agp || !dev->agp->acquired)
370280183Sdumbbell		return -EINVAL;
371280183Sdumbbell	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
372280183Sdumbbell		return -EINVAL;
373280183Sdumbbell	if (entry->bound)
374280183Sdumbbell		drm_unbind_agp(entry->memory);
375235783Skib
376280183Sdumbbell	list_del(&entry->head);
377235783Skib
378280183Sdumbbell	drm_free_agp(entry->memory, entry->pages);
379235783Skib	free(entry, DRM_MEM_AGPLISTS);
380235783Skib	return 0;
381235783Skib}
382280183SdumbbellEXPORT_SYMBOL(drm_agp_free);
383235783Skib
384280183Sdumbbell
385280183Sdumbbell
386235783Skibint drm_agp_free_ioctl(struct drm_device *dev, void *data,
387235783Skib		       struct drm_file *file_priv)
388235783Skib{
389280183Sdumbbell	struct drm_agp_buffer *request = data;
390235783Skib
391280183Sdumbbell	return drm_agp_free(dev, request);
392235783Skib}
393235783Skib
394280183Sdumbbell/**
395280183Sdumbbell * Initialize the AGP resources.
396280183Sdumbbell *
397280183Sdumbbell * \return pointer to a drm_agp_head structure.
398280183Sdumbbell *
399280183Sdumbbell * Gets the drm_agp_t structure which is made available by the agpgart module
400280183Sdumbbell * via the inter_module_* functions. Creates and initializes a drm_agp_head
401280183Sdumbbell * structure.
402280183Sdumbbell */
403280183Sdumbbellstruct drm_agp_head *drm_agp_init(struct drm_device *dev)
404235783Skib{
405280183Sdumbbell	struct drm_agp_head *head = NULL;
406235783Skib
407280183Sdumbbell	if (!(head = malloc(sizeof(*head), DRM_MEM_AGPLISTS, M_NOWAIT)))
408280183Sdumbbell		return NULL;
409280183Sdumbbell	memset((void *)head, 0, sizeof(*head));
410280183Sdumbbell	head->bridge = agp_find_device();
411280183Sdumbbell	if (!head->bridge) {
412280183Sdumbbell		free(head, DRM_MEM_AGPLISTS);
413280183Sdumbbell		return NULL;
414280183Sdumbbell	} else {
415280183Sdumbbell		agp_get_info(head->bridge, &head->agp_info);
416235783Skib	}
417280183Sdumbbell	INIT_LIST_HEAD(&head->memory);
418280183Sdumbbell	head->cant_use_aperture = 0;
419280183Sdumbbell	head->base = head->agp_info.ai_aperture_base;
420235783Skib	return head;
421235783Skib}
422235783Skib
423280183Sdumbbell#ifdef FREEBSD_NOTYET
424280183Sdumbbell/**
425280183Sdumbbell * Binds a collection of pages into AGP memory at the given offset, returning
426280183Sdumbbell * the AGP memory structure containing them.
427280183Sdumbbell *
428280183Sdumbbell * No reference is held on the pages during this time -- it is up to the
429280183Sdumbbell * caller to handle that.
430280183Sdumbbell */
431280183SdumbbellDRM_AGP_MEM *
432280183Sdumbbelldrm_agp_bind_pages(struct drm_device *dev,
433280183Sdumbbell		   struct page **pages,
434280183Sdumbbell		   unsigned long num_pages,
435280183Sdumbbell		   uint32_t gtt_offset,
436280183Sdumbbell		   u32 type)
437235783Skib{
438280183Sdumbbell	DRM_AGP_MEM *mem;
439280183Sdumbbell	int ret, i;
440235783Skib
441280183Sdumbbell	DRM_DEBUG("\n");
442280183Sdumbbell
443280183Sdumbbell	mem = agp_allocate_memory(dev->agp->bridge, num_pages,
444280183Sdumbbell				      type);
445280183Sdumbbell	if (mem == NULL) {
446280183Sdumbbell		DRM_ERROR("Failed to allocate memory for %ld pages\n",
447280183Sdumbbell			  num_pages);
448235783Skib		return NULL;
449280183Sdumbbell	}
450235783Skib
451280183Sdumbbell	for (i = 0; i < num_pages; i++)
452280183Sdumbbell		mem->pages[i] = pages[i];
453280183Sdumbbell	mem->page_count = num_pages;
454235783Skib
455280183Sdumbbell	mem->is_flushed = true;
456280183Sdumbbell	ret = agp_bind_memory(mem, gtt_offset / PAGE_SIZE);
457280183Sdumbbell	if (ret != 0) {
458280183Sdumbbell		DRM_ERROR("Failed to bind AGP memory: %d\n", ret);
459280183Sdumbbell		agp_free_memory(mem);
460280183Sdumbbell		return NULL;
461280183Sdumbbell	}
462235783Skib
463280183Sdumbbell	return mem;
464235783Skib}
465280183SdumbbellEXPORT_SYMBOL(drm_agp_bind_pages);
466280183Sdumbbell#endif /* FREEBSD_NOTYET */
467235783Skib
468280183Sdumbbell#endif /* __OS_HAS_AGP */
469