xref: /gfx-drm/usr/src/uts/common/io/drm/drm_agpsupport.c (revision 34389f68)
1829150b0SGordon Ross /*
247dc10d7SGordon Ross  * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
3829150b0SGordon Ross  */
4829150b0SGordon Ross 
547dc10d7SGordon Ross /**
647dc10d7SGordon Ross  * \file drm_agpsupport.c
747dc10d7SGordon Ross  * DRM support for AGP/GART backend
847dc10d7SGordon Ross  *
947dc10d7SGordon Ross  * \author Rickard E. (Rik) Faith <faith@valinux.com>
1047dc10d7SGordon Ross  * \author Gareth Hughes <gareth@valinux.com>
11829150b0SGordon Ross  */
1247dc10d7SGordon Ross 
13829150b0SGordon Ross /*
14829150b0SGordon Ross  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
15829150b0SGordon Ross  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
1647dc10d7SGordon Ross  * Copyright (c) 2009, 2013, Intel Corporation.
17829150b0SGordon Ross  * All Rights Reserved.
18829150b0SGordon Ross  *
19829150b0SGordon Ross  * Permission is hereby granted, free of charge, to any person obtaining a
20829150b0SGordon Ross  * copy of this software and associated documentation files (the "Software"),
21829150b0SGordon Ross  * to deal in the Software without restriction, including without limitation
22829150b0SGordon Ross  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
23829150b0SGordon Ross  * and/or sell copies of the Software, and to permit persons to whom the
24829150b0SGordon Ross  * Software is furnished to do so, subject to the following conditions:
25829150b0SGordon Ross  *
26829150b0SGordon Ross  * The above copyright notice and this permission notice (including the next
27829150b0SGordon Ross  * paragraph) shall be included in all copies or substantial portions of the
28829150b0SGordon Ross  * Software.
29829150b0SGordon Ross  *
30829150b0SGordon Ross  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31829150b0SGordon Ross  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32829150b0SGordon Ross  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
33829150b0SGordon Ross  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
34829150b0SGordon Ross  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
35829150b0SGordon Ross  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
36829150b0SGordon Ross  * OTHER DEALINGS IN THE SOFTWARE.
37829150b0SGordon Ross  */
38829150b0SGordon Ross 
39829150b0SGordon Ross #include "drm.h"
40829150b0SGordon Ross #include "drmP.h"
41829150b0SGordon Ross 
42829150b0SGordon Ross #ifndef	AGP_PAGE_SIZE
43829150b0SGordon Ross #define	AGP_PAGE_SIZE 4096
44829150b0SGordon Ross #define	AGP_PAGE_SHIFT 12
45829150b0SGordon Ross #endif
46829150b0SGordon Ross 
47829150b0SGordon Ross /*
48829150b0SGordon Ross  * The agpa_key field of struct agp_allocate_t actually is
49829150b0SGordon Ross  * an index to an array. It can be zero. But we will use
50829150b0SGordon Ross  * this agpa_key as a handle returned to userland. Generally,
51829150b0SGordon Ross  * 0 is not a valid value for a handle, so we add an offset
52829150b0SGordon Ross  * to the key to get a handle.
53829150b0SGordon Ross  */
54829150b0SGordon Ross #define	DRM_AGP_KEY_OFFSET	8
55829150b0SGordon Ross 
drm_agp_cleanup(struct drm_device * dev)5647dc10d7SGordon Ross void drm_agp_cleanup(struct drm_device *dev)
57829150b0SGordon Ross {
5847dc10d7SGordon Ross 	struct drm_agp_head *agp = dev->agp;
59829150b0SGordon Ross 
6047dc10d7SGordon Ross 	(void) ldi_close(agp->agpgart_lh, FEXCL, kcred);
6147dc10d7SGordon Ross 	ldi_ident_release(agp->agpgart_li);
62829150b0SGordon Ross }
63829150b0SGordon Ross 
6447dc10d7SGordon Ross /**
6547dc10d7SGordon Ross  * Get AGP information.
6647dc10d7SGordon Ross  *
6747dc10d7SGordon Ross  * \param inode device inode.
6847dc10d7SGordon Ross  * \param file_priv DRM file private.
6947dc10d7SGordon Ross  * \param cmd command.
7047dc10d7SGordon Ross  * \param arg pointer to a (output) drm_agp_info structure.
7147dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
7247dc10d7SGordon Ross  *
7347dc10d7SGordon Ross  * Verifies the AGP device has been initialized and acquired and fills in the
7447dc10d7SGordon Ross  * drm_agp_info structure with the information in drm_agp_head::agp_info.
7547dc10d7SGordon Ross  */
drm_agp_info(struct drm_device * dev,struct drm_agp_info * info)7647dc10d7SGordon Ross int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info)
77829150b0SGordon Ross {
7847dc10d7SGordon Ross 	agp_info_t *agpinfo;
79829150b0SGordon Ross 
80829150b0SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
8147dc10d7SGordon Ross 		return -EINVAL;
82829150b0SGordon Ross 
83829150b0SGordon Ross 	agpinfo = &dev->agp->agp_info;
8447dc10d7SGordon Ross 	info->agp_version_major = agpinfo->agpi_version.agpv_major;
8547dc10d7SGordon Ross 	info->agp_version_minor = agpinfo->agpi_version.agpv_minor;
8647dc10d7SGordon Ross 	info->mode = agpinfo->agpi_mode;
8747dc10d7SGordon Ross 	info->aperture_base = agpinfo->agpi_aperbase;
8847dc10d7SGordon Ross 	info->aperture_size = agpinfo->agpi_apersize * 1024 * 1024;
8947dc10d7SGordon Ross 	info->memory_allowed = agpinfo->agpi_pgtotal << PAGE_SHIFT;
9047dc10d7SGordon Ross 	info->memory_used = agpinfo->agpi_pgused << PAGE_SHIFT;
9147dc10d7SGordon Ross 	info->id_vendor = agpinfo->agpi_devid & 0xffff;
9247dc10d7SGordon Ross 	info->id_device = agpinfo->agpi_devid >> 16;
9347dc10d7SGordon Ross 
9447dc10d7SGordon Ross 	return 0;
95829150b0SGordon Ross }
96829150b0SGordon Ross 
9747dc10d7SGordon Ross /* LINTED */
drm_agp_info_ioctl(DRM_IOCTL_ARGS)9847dc10d7SGordon Ross int drm_agp_info_ioctl(DRM_IOCTL_ARGS)
99829150b0SGordon Ross {
10047dc10d7SGordon Ross 	struct drm_agp_info *info = data;
10147dc10d7SGordon Ross 	int err;
102829150b0SGordon Ross 
10347dc10d7SGordon Ross 	err = drm_agp_info(dev, info);
10447dc10d7SGordon Ross 	if (err)
10547dc10d7SGordon Ross 		return err;
106829150b0SGordon Ross 
10747dc10d7SGordon Ross 	return 0;
108829150b0SGordon Ross }
109829150b0SGordon Ross 
11047dc10d7SGordon Ross /**
11147dc10d7SGordon Ross  * Acquire the AGP device.
11247dc10d7SGordon Ross  *
11347dc10d7SGordon Ross  * \param dev DRM device that is to acquire AGP.
11447dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
11547dc10d7SGordon Ross  *
11647dc10d7SGordon Ross  * Verifies the AGP device hasn't been acquired before and calls
11747dc10d7SGordon Ross  * \c agp_backend_acquire.
11847dc10d7SGordon Ross  */
drm_agp_acquire(struct drm_device * dev)11947dc10d7SGordon Ross int drm_agp_acquire(struct drm_device * dev)
120829150b0SGordon Ross {
121829150b0SGordon Ross 	if (!dev->agp)
12247dc10d7SGordon Ross 		return -ENODEV;
12347dc10d7SGordon Ross 	if (dev->agp->acquired)
12447dc10d7SGordon Ross 		return -EBUSY;
12547dc10d7SGordon Ross 	{
12647dc10d7SGordon Ross 		int ret, rval;
12747dc10d7SGordon Ross 		if (ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ACQUIRE,
12847dc10d7SGordon Ross 		    (uintptr_t)0, FKIOCTL, kcred, &rval)) {
12947dc10d7SGordon Ross 			DRM_ERROR("AGPIOC_ACQUIRE failed");
13047dc10d7SGordon Ross 			return -ret;
13147dc10d7SGordon Ross 		}
132829150b0SGordon Ross 	}
13347dc10d7SGordon Ross 	dev->agp->acquired = 1;
13447dc10d7SGordon Ross 	return 0;
135829150b0SGordon Ross }
136829150b0SGordon Ross 
13747dc10d7SGordon Ross /**
13847dc10d7SGordon Ross  * Acquire the AGP device (ioctl).
13947dc10d7SGordon Ross  *
14047dc10d7SGordon Ross  * \param inode device inode.
14147dc10d7SGordon Ross  * \param file_priv DRM file private.
14247dc10d7SGordon Ross  * \param cmd command.
14347dc10d7SGordon Ross  * \param arg user argument.
14447dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
14547dc10d7SGordon Ross  *
14647dc10d7SGordon Ross  * Verifies the AGP device hasn't been acquired before and calls
14747dc10d7SGordon Ross  * \c agp_backend_acquire.
14847dc10d7SGordon Ross  */
14947dc10d7SGordon Ross /* LINTED */
drm_agp_acquire_ioctl(DRM_IOCTL_ARGS)15047dc10d7SGordon Ross int drm_agp_acquire_ioctl(DRM_IOCTL_ARGS)
151829150b0SGordon Ross {
15247dc10d7SGordon Ross 	return drm_agp_acquire((struct drm_device *) file->minor->dev);
153829150b0SGordon Ross }
154829150b0SGordon Ross 
15547dc10d7SGordon Ross /**
15647dc10d7SGordon Ross  * Release the AGP device.
15747dc10d7SGordon Ross  *
15847dc10d7SGordon Ross  * \param dev DRM device that is to release AGP.
15947dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
16047dc10d7SGordon Ross  *
16147dc10d7SGordon Ross  * Verifies the AGP device has been acquired and calls \c agp_backend_release.
16247dc10d7SGordon Ross  */
drm_agp_release(struct drm_device * dev)16347dc10d7SGordon Ross int drm_agp_release(struct drm_device * dev)
164829150b0SGordon Ross {
16547dc10d7SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
16647dc10d7SGordon Ross 		return -EINVAL;
16747dc10d7SGordon Ross 	{
16847dc10d7SGordon Ross 		int ret, rval;
16947dc10d7SGordon Ross 		if (ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RELEASE,
17047dc10d7SGordon Ross 		    (intptr_t)0, FKIOCTL, kcred, &rval)) {
17147dc10d7SGordon Ross 			DRM_ERROR("AGPIOC_RELEASE failed");
17247dc10d7SGordon Ross 			return -ret;
17347dc10d7SGordon Ross 		}
17447dc10d7SGordon Ross 	}
17547dc10d7SGordon Ross 	dev->agp->acquired = 0;
17647dc10d7SGordon Ross 	return 0;
17747dc10d7SGordon Ross }
178829150b0SGordon Ross 
17947dc10d7SGordon Ross /* LINTED */
drm_agp_release_ioctl(DRM_IOCTL_ARGS)18047dc10d7SGordon Ross int drm_agp_release_ioctl(DRM_IOCTL_ARGS)
18147dc10d7SGordon Ross {
18247dc10d7SGordon Ross 	return drm_agp_release(dev);
18347dc10d7SGordon Ross }
184829150b0SGordon Ross 
18547dc10d7SGordon Ross /**
18647dc10d7SGordon Ross  * Enable the AGP bus.
18747dc10d7SGordon Ross  *
18847dc10d7SGordon Ross  * \param dev DRM device that has previously acquired AGP.
18947dc10d7SGordon Ross  * \param mode Requested AGP mode.
19047dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
19147dc10d7SGordon Ross  *
19247dc10d7SGordon Ross  * Verifies the AGP device has been acquired but not enabled, and calls
19347dc10d7SGordon Ross  * \c agp_enable.
19447dc10d7SGordon Ross  */
drm_agp_enable(struct drm_device * dev,struct drm_agp_mode mode)19547dc10d7SGordon Ross int drm_agp_enable(struct drm_device * dev, struct drm_agp_mode mode)
19647dc10d7SGordon Ross {
19747dc10d7SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
19847dc10d7SGordon Ross 		return -EINVAL;
19947dc10d7SGordon Ross 
20047dc10d7SGordon Ross 	dev->agp->mode = mode.mode;
20147dc10d7SGordon Ross 	{
20247dc10d7SGordon Ross 		agp_setup_t setup;
20347dc10d7SGordon Ross 		int ret, rval;
20447dc10d7SGordon Ross 		setup.agps_mode = (uint32_t)mode.mode;
20547dc10d7SGordon Ross 		if (ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_SETUP,
20647dc10d7SGordon Ross 		    (intptr_t)&setup, FKIOCTL, kcred, &rval)) {
20747dc10d7SGordon Ross 			DRM_ERROR("AGPIOC_SETUP failed");
20847dc10d7SGordon Ross 			return -ret;
20947dc10d7SGordon Ross 		}
210829150b0SGordon Ross 	}
211829150b0SGordon Ross 	dev->agp->enabled = 1;
21247dc10d7SGordon Ross 	return 0;
21347dc10d7SGordon Ross }
214829150b0SGordon Ross 
21547dc10d7SGordon Ross /* LINTED */
drm_agp_enable_ioctl(DRM_IOCTL_ARGS)21647dc10d7SGordon Ross int drm_agp_enable_ioctl(DRM_IOCTL_ARGS)
21747dc10d7SGordon Ross {
21847dc10d7SGordon Ross 	struct drm_agp_mode *mode = data;
21947dc10d7SGordon Ross 
22047dc10d7SGordon Ross 	return drm_agp_enable(dev, *mode);
221829150b0SGordon Ross }
222829150b0SGordon Ross 
22347dc10d7SGordon Ross /**
22447dc10d7SGordon Ross  * Allocate AGP memory.
22547dc10d7SGordon Ross  *
22647dc10d7SGordon Ross  * \param inode device inode.
22747dc10d7SGordon Ross  * \param file_priv file private pointer.
22847dc10d7SGordon Ross  * \param cmd command.
22947dc10d7SGordon Ross  * \param arg pointer to a drm_agp_buffer structure.
23047dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
23147dc10d7SGordon Ross  *
23247dc10d7SGordon Ross  * Verifies the AGP device is present and has been acquired, allocates the
23347dc10d7SGordon Ross  * memory via alloc_agp() and creates a drm_agp_mem entry for it.
23447dc10d7SGordon Ross  */
drm_agp_alloc(struct drm_device * dev,struct drm_agp_buffer * request)23547dc10d7SGordon Ross int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
236829150b0SGordon Ross {
23747dc10d7SGordon Ross 	struct drm_agp_mem *entry;
23847dc10d7SGordon Ross 	agp_allocate_t alloc;
23947dc10d7SGordon Ross 	unsigned long pages;
240829150b0SGordon Ross 	int ret, rval;
241829150b0SGordon Ross 
242829150b0SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
24347dc10d7SGordon Ross 		return -EINVAL;
24447dc10d7SGordon Ross 	if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
24547dc10d7SGordon Ross 		return -ENOMEM;
246829150b0SGordon Ross 
24747dc10d7SGordon Ross 	(void) memset(entry, 0, sizeof(*entry));
248829150b0SGordon Ross 
24947dc10d7SGordon Ross 	pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
250829150b0SGordon Ross 
25147dc10d7SGordon Ross 	alloc.agpa_pgcount = (uint32_t) pages;
252829150b0SGordon Ross 	alloc.agpa_type = AGP_NORMAL;
253829150b0SGordon Ross 	ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ALLOCATE,
254829150b0SGordon Ross 	    (intptr_t)&alloc, FKIOCTL, kcred, &rval);
255829150b0SGordon Ross 	if (ret) {
25647dc10d7SGordon Ross 		DRM_ERROR("AGPIOC_ALLOCATE failed");
25747dc10d7SGordon Ross 		kfree(entry, sizeof (*entry));
25847dc10d7SGordon Ross 		return -ret;
259829150b0SGordon Ross 	}
260829150b0SGordon Ross 
26147dc10d7SGordon Ross 	entry->handle = alloc.agpa_key + DRM_AGP_KEY_OFFSET;
262829150b0SGordon Ross 	entry->bound = 0;
26347dc10d7SGordon Ross 	entry->pages = (int) pages;
26447dc10d7SGordon Ross 	list_add(&entry->head, &dev->agp->memory, (caddr_t)entry);
26547dc10d7SGordon Ross 
26647dc10d7SGordon Ross 	request->handle = entry->handle;
26747dc10d7SGordon Ross 	request->physical = alloc.agpa_physical;
26847dc10d7SGordon Ross 
26947dc10d7SGordon Ross 	return 0;
270829150b0SGordon Ross }
271829150b0SGordon Ross 
27247dc10d7SGordon Ross /* LINTED */
drm_agp_alloc_ioctl(DRM_IOCTL_ARGS)27347dc10d7SGordon Ross int drm_agp_alloc_ioctl(DRM_IOCTL_ARGS)
274829150b0SGordon Ross {
27547dc10d7SGordon Ross 	struct drm_agp_buffer *request = data;
276829150b0SGordon Ross 
27747dc10d7SGordon Ross 	return drm_agp_alloc(dev, request);
27847dc10d7SGordon Ross }
27947dc10d7SGordon Ross 
28047dc10d7SGordon Ross /**
28147dc10d7SGordon Ross  * Search for the AGP memory entry associated with a handle.
28247dc10d7SGordon Ross  *
28347dc10d7SGordon Ross  * \param dev DRM device structure.
28447dc10d7SGordon Ross  * \param handle AGP memory handle.
28547dc10d7SGordon Ross  * \return pointer to the drm_agp_mem structure associated with \p handle.
28647dc10d7SGordon Ross  *
28747dc10d7SGordon Ross  * Walks through drm_agp_head::memory until finding a matching handle.
28847dc10d7SGordon Ross  */
drm_agp_lookup_entry(struct drm_device * dev,unsigned long handle)28947dc10d7SGordon Ross static struct drm_agp_mem *drm_agp_lookup_entry(struct drm_device * dev,
29047dc10d7SGordon Ross 					   unsigned long handle)
29147dc10d7SGordon Ross {
29247dc10d7SGordon Ross 	struct drm_agp_mem *entry;
29347dc10d7SGordon Ross 
29447dc10d7SGordon Ross 	list_for_each_entry(entry, struct drm_agp_mem, &dev->agp->memory, head) {
295829150b0SGordon Ross 		if (entry->handle == handle)
29647dc10d7SGordon Ross 			return entry;
297829150b0SGordon Ross 	}
29847dc10d7SGordon Ross 	return NULL;
299829150b0SGordon Ross }
300829150b0SGordon Ross 
30147dc10d7SGordon Ross /**
30247dc10d7SGordon Ross  * Unbind AGP memory from the GATT (ioctl).
30347dc10d7SGordon Ross  *
30447dc10d7SGordon Ross  * \param inode device inode.
30547dc10d7SGordon Ross  * \param file_priv DRM file private.
30647dc10d7SGordon Ross  * \param cmd command.
30747dc10d7SGordon Ross  * \param arg pointer to a drm_agp_binding structure.
30847dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
30947dc10d7SGordon Ross  *
31047dc10d7SGordon Ross  * Verifies the AGP device is present and acquired, looks-up the AGP memory
31147dc10d7SGordon Ross  * entry and passes it to the unbind_agp() function.
31247dc10d7SGordon Ross  */
drm_agp_unbind(struct drm_device * dev,struct drm_agp_binding * request)31347dc10d7SGordon Ross int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request)
314829150b0SGordon Ross {
31547dc10d7SGordon Ross 	struct drm_agp_mem *entry;
31647dc10d7SGordon Ross 	int ret;
317829150b0SGordon Ross 
318829150b0SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
31947dc10d7SGordon Ross 		return -EINVAL;
32047dc10d7SGordon Ross 	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
32147dc10d7SGordon Ross 		return -EINVAL;
322829150b0SGordon Ross 	if (!entry->bound)
32347dc10d7SGordon Ross 		return -EINVAL;
32447dc10d7SGordon Ross 	{
32547dc10d7SGordon Ross 		agp_unbind_t unbind;
32647dc10d7SGordon Ross 		int rval;
32747dc10d7SGordon Ross 		unbind.agpu_pri = 0;
32847dc10d7SGordon Ross 		unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
32947dc10d7SGordon Ross 		if (ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND,
33047dc10d7SGordon Ross 		    (intptr_t)&unbind, FKIOCTL, kcred, &rval)) {
33147dc10d7SGordon Ross 			DRM_ERROR("AGPIOC_UNBIND failed");
33247dc10d7SGordon Ross 			return -ret;
33347dc10d7SGordon Ross 		}
334829150b0SGordon Ross 	}
33547dc10d7SGordon Ross 		entry->bound = 0;
33647dc10d7SGordon Ross 	return ret;
337829150b0SGordon Ross }
338829150b0SGordon Ross 
33947dc10d7SGordon Ross /* LINTED */
drm_agp_unbind_ioctl(DRM_IOCTL_ARGS)34047dc10d7SGordon Ross int drm_agp_unbind_ioctl(DRM_IOCTL_ARGS)
341829150b0SGordon Ross {
34247dc10d7SGordon Ross 	struct drm_agp_binding *request = data;
343829150b0SGordon Ross 
34447dc10d7SGordon Ross 	return drm_agp_unbind(dev, request);
34547dc10d7SGordon Ross }
346829150b0SGordon Ross 
34747dc10d7SGordon Ross /**
34847dc10d7SGordon Ross  * Bind AGP memory into the GATT (ioctl)
34947dc10d7SGordon Ross  *
35047dc10d7SGordon Ross  * \param inode device inode.
35147dc10d7SGordon Ross  * \param file_priv DRM file private.
35247dc10d7SGordon Ross  * \param cmd command.
35347dc10d7SGordon Ross  * \param arg pointer to a drm_agp_binding structure.
35447dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
35547dc10d7SGordon Ross  *
35647dc10d7SGordon Ross  * Verifies the AGP device is present and has been acquired and that no memory
35747dc10d7SGordon Ross  * is currently bound into the GATT. Looks-up the AGP memory entry and passes
35847dc10d7SGordon Ross  * it to bind_agp() function.
35947dc10d7SGordon Ross  */
drm_agp_bind(struct drm_device * dev,struct drm_agp_binding * request)36047dc10d7SGordon Ross int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request)
36147dc10d7SGordon Ross {
36247dc10d7SGordon Ross 	struct drm_agp_mem *entry;
36347dc10d7SGordon Ross 	int retcode;
36447dc10d7SGordon Ross 	int page;
365829150b0SGordon Ross 
36647dc10d7SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
36747dc10d7SGordon Ross 		return -EINVAL;
36847dc10d7SGordon Ross 	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
36947dc10d7SGordon Ross 		return -EINVAL;
37047dc10d7SGordon Ross 	if (entry->bound)
37147dc10d7SGordon Ross 		return -EINVAL;
37247dc10d7SGordon Ross 	page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE;
37347dc10d7SGordon Ross 	{
37447dc10d7SGordon Ross 		uint_t key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
37547dc10d7SGordon Ross 		if (retcode = drm_agp_bind_memory(key, page, dev)) {
37647dc10d7SGordon Ross 			DRM_ERROR("failed key=0x%x, page=0x%x, "
37747dc10d7SGordon Ross 			    "agp_base=0x%lx", key, page, dev->agp->base);
37847dc10d7SGordon Ross 			return retcode;
37947dc10d7SGordon Ross 		}
380829150b0SGordon Ross 	}
38147dc10d7SGordon Ross 	entry->bound = dev->agp->base + (page << PAGE_SHIFT);
38247dc10d7SGordon Ross 	DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n",
38347dc10d7SGordon Ross 		  dev->agp->base, entry->bound);
38447dc10d7SGordon Ross 	return 0;
38547dc10d7SGordon Ross }
386829150b0SGordon Ross 
38747dc10d7SGordon Ross /* LINTED */
drm_agp_bind_ioctl(DRM_IOCTL_ARGS)38847dc10d7SGordon Ross int drm_agp_bind_ioctl(DRM_IOCTL_ARGS)
38947dc10d7SGordon Ross {
39047dc10d7SGordon Ross 	struct drm_agp_binding *request = data;
391829150b0SGordon Ross 
39247dc10d7SGordon Ross 	return drm_agp_bind(dev, request);
393829150b0SGordon Ross }
394829150b0SGordon Ross 
39547dc10d7SGordon Ross /**
39647dc10d7SGordon Ross  * Free AGP memory (ioctl).
39747dc10d7SGordon Ross  *
39847dc10d7SGordon Ross  * \param inode device inode.
39947dc10d7SGordon Ross  * \param file_priv DRM file private.
40047dc10d7SGordon Ross  * \param cmd command.
40147dc10d7SGordon Ross  * \param arg pointer to a drm_agp_buffer structure.
40247dc10d7SGordon Ross  * \return zero on success or a negative number on failure.
40347dc10d7SGordon Ross  *
40447dc10d7SGordon Ross  * Verifies the AGP device is present and has been acquired and looks up the
40547dc10d7SGordon Ross  * AGP memory entry. If the memory it's currently bound, unbind it via
40647dc10d7SGordon Ross  * unbind_agp(). Frees it via free_agp() as well as the entry itself
40747dc10d7SGordon Ross  * and unlinks from the doubly linked list it's inserted in.
40847dc10d7SGordon Ross  */
drm_agp_free(struct drm_device * dev,struct drm_agp_buffer * request)40947dc10d7SGordon Ross int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request)
410829150b0SGordon Ross {
41147dc10d7SGordon Ross 	struct drm_agp_mem *entry;
412829150b0SGordon Ross 
413829150b0SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
41447dc10d7SGordon Ross 		return -EINVAL;
41547dc10d7SGordon Ross 	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
41647dc10d7SGordon Ross 		return -EINVAL;
417829150b0SGordon Ross 	if (entry->bound)
41847dc10d7SGordon Ross 		(void) drm_agp_unbind_memory(request->handle, dev);
41947dc10d7SGordon Ross 
42047dc10d7SGordon Ross 	list_del(&entry->head);
42147dc10d7SGordon Ross 	{
42247dc10d7SGordon Ross 		int agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
42347dc10d7SGordon Ross 		int ret, rval;
42447dc10d7SGordon Ross 		ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_DEALLOCATE,
42547dc10d7SGordon Ross 		    (intptr_t)agpu_key, FKIOCTL, kcred, &rval);
42647dc10d7SGordon Ross 		if (ret) {
42747dc10d7SGordon Ross 			DRM_ERROR("AGPIOC_DEALLOCATE failed,"
42847dc10d7SGordon Ross 			    "akey=%d, ret=%d", agpu_key, ret);
42947dc10d7SGordon Ross 			return -ret;
43047dc10d7SGordon Ross 		}
431829150b0SGordon Ross 	}
43247dc10d7SGordon Ross 	kfree(entry, sizeof (*entry));
43347dc10d7SGordon Ross 	return 0;
434829150b0SGordon Ross }
435829150b0SGordon Ross 
43647dc10d7SGordon Ross /* LINTED */
drm_agp_free_ioctl(DRM_IOCTL_ARGS)43747dc10d7SGordon Ross int drm_agp_free_ioctl(DRM_IOCTL_ARGS)
438829150b0SGordon Ross {
43947dc10d7SGordon Ross 	struct drm_agp_buffer *request = data;
44047dc10d7SGordon Ross 
44147dc10d7SGordon Ross 	return drm_agp_free(dev, request);
44247dc10d7SGordon Ross }
443829150b0SGordon Ross 
44447dc10d7SGordon Ross /**
44547dc10d7SGordon Ross  * Initialize the AGP resources.
44647dc10d7SGordon Ross  *
44747dc10d7SGordon Ross  * \return pointer to a drm_agp_head structure.
44847dc10d7SGordon Ross  *
44947dc10d7SGordon Ross  * Gets the drm_agp_t structure which is made available by the agpgart module
45047dc10d7SGordon Ross  * via the inter_module_* functions. Creates and initializes a drm_agp_head
45147dc10d7SGordon Ross  * structure.
45247dc10d7SGordon Ross  */
drm_agp_init(struct drm_device * dev)45347dc10d7SGordon Ross struct drm_agp_head *drm_agp_init(struct drm_device *dev)
45447dc10d7SGordon Ross {
45547dc10d7SGordon Ross 	struct drm_agp_head *head = NULL;
45647dc10d7SGordon Ross 	int ret, rval;
457829150b0SGordon Ross 
45847dc10d7SGordon Ross 	if (!(head = kmalloc(sizeof(*head), GFP_KERNEL)))
45947dc10d7SGordon Ross 		return NULL;
46047dc10d7SGordon Ross 	(void) memset((void *)head, 0, sizeof(*head));
46147dc10d7SGordon Ross 	ret = ldi_ident_from_dip(dev->devinfo, &head->agpgart_li);
46247dc10d7SGordon Ross 	if (ret) {
46347dc10d7SGordon Ross 		DRM_ERROR("failed to get layerd ident, ret=%d", ret);
464829150b0SGordon Ross 		goto err_1;
465829150b0SGordon Ross 	}
466829150b0SGordon Ross 
46747dc10d7SGordon Ross 	ret = ldi_open_by_name(AGP_DEVICE, FEXCL, kcred,
46847dc10d7SGordon Ross 	    &head->agpgart_lh, head->agpgart_li);
46947dc10d7SGordon Ross 	if (ret) {
47047dc10d7SGordon Ross 		DRM_ERROR("failed to open %s, ret=%d", AGP_DEVICE, ret);
471829150b0SGordon Ross 		goto err_2;
472829150b0SGordon Ross 	}
473829150b0SGordon Ross 
47447dc10d7SGordon Ross 	ret = ldi_ioctl(head->agpgart_lh, AGPIOC_INFO,
47547dc10d7SGordon Ross 	    (intptr_t)&head->agp_info, FKIOCTL, kcred, &rval);
47647dc10d7SGordon Ross 	if (ret) {
47747dc10d7SGordon Ross 		DRM_ERROR("failed to get agpinfo, ret=%d", ret);
478829150b0SGordon Ross 		goto err_3;
479829150b0SGordon Ross 	}
48047dc10d7SGordon Ross 	INIT_LIST_HEAD(&head->memory);
48147dc10d7SGordon Ross 	head->base = head->agp_info.agpi_aperbase;
48247dc10d7SGordon Ross 	return head;
483829150b0SGordon Ross 
484829150b0SGordon Ross err_3:
48547dc10d7SGordon Ross 	(void) ldi_close(head->agpgart_lh, FEXCL, kcred);
486829150b0SGordon Ross err_2:
48747dc10d7SGordon Ross 	ldi_ident_release(head->agpgart_li);
488829150b0SGordon Ross err_1:
48947dc10d7SGordon Ross 	kfree(head, sizeof(*head));
49047dc10d7SGordon Ross 	return NULL;
491829150b0SGordon Ross }
492829150b0SGordon Ross 
49347dc10d7SGordon Ross /* LINTED */
drm_agp_allocate_memory(size_t pages,uint32_t type,struct drm_device * dev)49447dc10d7SGordon Ross void *drm_agp_allocate_memory(size_t pages, uint32_t type, struct drm_device *dev)
495829150b0SGordon Ross {
49647dc10d7SGordon Ross 	return NULL;
497829150b0SGordon Ross }
498829150b0SGordon Ross 
49947dc10d7SGordon Ross /* LINTED */
drm_agp_free_memory(agp_allocate_t * handle,struct drm_device * dev)50047dc10d7SGordon Ross int drm_agp_free_memory(agp_allocate_t *handle, struct drm_device *dev)
501829150b0SGordon Ross {
50247dc10d7SGordon Ross 	return 1;
503829150b0SGordon Ross }
504829150b0SGordon Ross 
drm_agp_bind_memory(unsigned int key,uint32_t start,struct drm_device * dev)50547dc10d7SGordon Ross int drm_agp_bind_memory(unsigned int key, uint32_t start, struct drm_device *dev)
506829150b0SGordon Ross {
507829150b0SGordon Ross 	agp_bind_t bind;
50847dc10d7SGordon Ross 	int ret, rval;
509829150b0SGordon Ross 
510829150b0SGordon Ross 	bind.agpb_pgstart = start;
511829150b0SGordon Ross 	bind.agpb_key = key;
51247dc10d7SGordon Ross 	if (ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_BIND,
51347dc10d7SGordon Ross 	    (intptr_t)&bind, FKIOCTL, kcred, &rval)) {
51447dc10d7SGordon Ross 		DRM_DEBUG("AGPIOC_BIND failed");
51547dc10d7SGordon Ross 		return -ret;
516829150b0SGordon Ross 	}
51747dc10d7SGordon Ross 	return 0;
518829150b0SGordon Ross }
519829150b0SGordon Ross 
drm_agp_unbind_memory(unsigned long handle,struct drm_device * dev)52047dc10d7SGordon Ross int drm_agp_unbind_memory(unsigned long handle, struct drm_device *dev)
521829150b0SGordon Ross {
52247dc10d7SGordon Ross 	struct drm_agp_mem *entry;
523829150b0SGordon Ross 	agp_unbind_t unbind;
524829150b0SGordon Ross 	int ret, rval;
525829150b0SGordon Ross 
526829150b0SGordon Ross 	if (!dev->agp || !dev->agp->acquired)
52747dc10d7SGordon Ross 		return -EINVAL;
528829150b0SGordon Ross 
52947dc10d7SGordon Ross 	entry = drm_agp_lookup_entry(dev, handle);
530829150b0SGordon Ross 	if (!entry || !entry->bound)
53147dc10d7SGordon Ross 		return -EINVAL;
532829150b0SGordon Ross 
533829150b0SGordon Ross 	unbind.agpu_pri = 0;
534829150b0SGordon Ross 	unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET;
53547dc10d7SGordon Ross 	if (ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND,
53647dc10d7SGordon Ross 	    (intptr_t)&unbind, FKIOCTL, kcred, &rval)) {
53747dc10d7SGordon Ross 		DRM_ERROR("AGPIO_UNBIND failed");
53847dc10d7SGordon Ross 		return -ret;
539829150b0SGordon Ross 	}
540829150b0SGordon Ross 	entry->bound = 0;
54147dc10d7SGordon Ross 	return 0;
542829150b0SGordon Ross }
543829150b0SGordon Ross 
54447dc10d7SGordon Ross /**
545829150b0SGordon Ross  * Binds a collection of pages into AGP memory at the given offset, returning
546829150b0SGordon Ross  * the AGP memory structure containing them.
547829150b0SGordon Ross  *
548829150b0SGordon Ross  * No reference is held on the pages during this time -- it is up to the
549829150b0SGordon Ross  * caller to handle that.
550829150b0SGordon Ross  */
551829150b0SGordon Ross int
drm_agp_bind_pages(struct drm_device * dev,pfn_t * pages,unsigned long num_pages,uint32_t gtt_offset,unsigned int agp_type)55247dc10d7SGordon Ross drm_agp_bind_pages(struct drm_device *dev,
553829150b0SGordon Ross 		    pfn_t *pages,
554829150b0SGordon Ross 		    unsigned long num_pages,
55547dc10d7SGordon Ross 		    uint32_t gtt_offset,
55647dc10d7SGordon Ross 		    unsigned int agp_type)
557829150b0SGordon Ross {
558*34389f68SGordon Ross 	agp_bind_pages_t bind;
55947dc10d7SGordon Ross 	int ret, rval;
560829150b0SGordon Ross 
561*34389f68SGordon Ross 	bind.agpb_pgstart = gtt_offset / AGP_PAGE_SIZE;
562*34389f68SGordon Ross 	bind.agpb_pgcount = num_pages;
563*34389f68SGordon Ross 	bind.agpb_pages = pages;
564*34389f68SGordon Ross 	bind.agpb_type = agp_type;
565829150b0SGordon Ross 
566829150b0SGordon Ross 	ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_BIND,
567829150b0SGordon Ross 	    (intptr_t)&bind, FKIOCTL, kcred, &rval);
568829150b0SGordon Ross 	if (ret) {
569829150b0SGordon Ross 		DRM_ERROR("AGPIOC_PAGES_BIND failed ret %d", ret);
57047dc10d7SGordon Ross 		return -ret;
571829150b0SGordon Ross 	}
57247dc10d7SGordon Ross 	return 0;
573829150b0SGordon Ross }
574829150b0SGordon Ross 
575829150b0SGordon Ross int
drm_agp_unbind_pages(struct drm_device * dev,pfn_t * pages,unsigned long num_pages,uint32_t gtt_offset,pfn_t scratch,uint32_t ignored)57647dc10d7SGordon Ross drm_agp_unbind_pages(struct drm_device *dev,
577*34389f68SGordon Ross 		    pfn_t *pages,	/* NULL */
578829150b0SGordon Ross 		    unsigned long num_pages,
579829150b0SGordon Ross 		    uint32_t gtt_offset,
58047dc10d7SGordon Ross 		    pfn_t scratch,
581*34389f68SGordon Ross 		    uint32_t ignored)	/* old "VT_switch" flag */
582829150b0SGordon Ross {
583*34389f68SGordon Ross 	agp_unbind_pages_t unbind;
58447dc10d7SGordon Ross 	int ret, rval;
585829150b0SGordon Ross 
586*34389f68SGordon Ross 	unbind.agpu_pgstart = gtt_offset / AGP_PAGE_SIZE;
587*34389f68SGordon Ross 	unbind.agpu_pgcount = num_pages;
588*34389f68SGordon Ross 	unbind.agpu_scratch = scratch;
589*34389f68SGordon Ross 	unbind.agpu_flags = 0;
590829150b0SGordon Ross 
591829150b0SGordon Ross 	ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_UNBIND,
592829150b0SGordon Ross 	    (intptr_t)&unbind, FKIOCTL, kcred, &rval);
593829150b0SGordon Ross 	if (ret) {
59447dc10d7SGordon Ross 		DRM_ERROR("AGPIOC_PAGES_UNBIND failed %d", ret);
59547dc10d7SGordon Ross 		return -ret;
596829150b0SGordon Ross 	}
59747dc10d7SGordon Ross 	return 0;
598829150b0SGordon Ross }
599829150b0SGordon Ross 
drm_agp_chipset_flush(struct drm_device * dev)60047dc10d7SGordon Ross void drm_agp_chipset_flush(struct drm_device *dev)
601829150b0SGordon Ross {
602829150b0SGordon Ross 	int ret, rval;
603829150b0SGordon Ross 
604829150b0SGordon Ross 	ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_FLUSHCHIPSET,
605829150b0SGordon Ross 	    (intptr_t)0, FKIOCTL, kcred, &rval);
60647dc10d7SGordon Ross 	if (ret)
60747dc10d7SGordon Ross 		DRM_ERROR("AGPIOC_FLUSHCHIPSET failed, ret=%d", ret);
608829150b0SGordon Ross }
609829150b0SGordon Ross 
61047dc10d7SGordon Ross int
drm_agp_rw_gtt(struct drm_device * dev,unsigned long num_pages,uint32_t gtt_offset,void * gttp,uint32_t rw_flag)61147dc10d7SGordon Ross drm_agp_rw_gtt(struct drm_device *dev,
61247dc10d7SGordon Ross 		    unsigned long num_pages,
61347dc10d7SGordon Ross 		    uint32_t gtt_offset,
61447dc10d7SGordon Ross 		    void *gttp,
615*34389f68SGordon Ross 		    uint32_t rw_flag)	/* read = 0 write = 1 */
616829150b0SGordon Ross {
617*34389f68SGordon Ross 	agp_rw_gtt_t rw;
618829150b0SGordon Ross 	int ret, rval;
619829150b0SGordon Ross 
620*34389f68SGordon Ross 	rw.agprw_pgstart = gtt_offset / AGP_PAGE_SIZE;
621*34389f68SGordon Ross 	rw.agprw_pgcount = num_pages;
622*34389f68SGordon Ross 	rw.agprw_addr = gttp;
623*34389f68SGordon Ross 	rw.agprw_flags = rw_flag;
624*34389f68SGordon Ross 
62547dc10d7SGordon Ross 	ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RW_GTT,
626*34389f68SGordon Ross 	    (intptr_t)&rw, FKIOCTL, kcred, &rval);
62747dc10d7SGordon Ross 	if (ret) {
62847dc10d7SGordon Ross 		DRM_ERROR("AGPIOC_RW_GTT failed %d", ret);
62947dc10d7SGordon Ross 		return -ret;
630829150b0SGordon Ross 	}
63147dc10d7SGordon Ross 	return 0;
632829150b0SGordon Ross }
633