Home | History | Annotate | Line # | Download | only in drm
drm_agpsupport.c revision 1.10.6.1
      1 /*	$NetBSD: drm_agpsupport.c,v 1.10.6.1 2020/02/29 20:20:13 ad Exp $	*/
      2 
      3 /**
      4  * \file drm_agpsupport.c
      5  * DRM support for AGP/GART backend
      6  *
      7  * \author Rickard E. (Rik) Faith <faith (at) valinux.com>
      8  * \author Gareth Hughes <gareth (at) valinux.com>
      9  */
     10 
     11 /*
     12  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
     13  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
     14  * All Rights Reserved.
     15  *
     16  * Permission is hereby granted, free of charge, to any person obtaining a
     17  * copy of this software and associated documentation files (the "Software"),
     18  * to deal in the Software without restriction, including without limitation
     19  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     20  * and/or sell copies of the Software, and to permit persons to whom the
     21  * Software is furnished to do so, subject to the following conditions:
     22  *
     23  * The above copyright notice and this permission notice (including the next
     24  * paragraph) shall be included in all copies or substantial portions of the
     25  * Software.
     26  *
     27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     28  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     29  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     30  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     31  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     32  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     33  * OTHER DEALINGS IN THE SOFTWARE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 __KERNEL_RCSID(0, "$NetBSD: drm_agpsupport.c,v 1.10.6.1 2020/02/29 20:20:13 ad Exp $");
     38 
     39 #include <drm/drmP.h>
     40 #include <linux/module.h>
     41 #include <linux/slab.h>
     42 #include "drm_legacy.h"
     43 
     44 #include <asm/agp.h>
     45 
     46 /**
     47  * Get AGP information.
     48  *
     49  * \param inode device inode.
     50  * \param file_priv DRM file private.
     51  * \param cmd command.
     52  * \param arg pointer to a (output) drm_agp_info structure.
     53  * \return zero on success or a negative number on failure.
     54  *
     55  * Verifies the AGP device has been initialized and acquired and fills in the
     56  * drm_agp_info structure with the information in drm_agp_head::agp_info.
     57  */
     58 static int drm_agp_info_hook(struct drm_device *dev, struct drm_agp_info *info)
     59 {
     60 	struct agp_kern_info *kern;
     61 
     62 	if (!dev->agp || !dev->agp->acquired)
     63 		return -EINVAL;
     64 
     65 	kern = &dev->agp->agp_info;
     66 #if __NetBSD__
     67 	info->agp_version_major = 1;
     68 	info->agp_version_minor = 0;
     69 	info->mode = kern->aki_info.ai_mode;
     70 	info->aperture_base = kern->aki_info.ai_aperture_base;
     71 	info->aperture_size = kern->aki_info.ai_aperture_size;
     72 	info->memory_allowed = kern->aki_info.ai_memory_allowed;
     73 	info->memory_used = kern->aki_info.ai_memory_used;
     74 	info->id_vendor = PCI_VENDOR(kern->aki_info.ai_devid);
     75 	info->id_device = PCI_PRODUCT(kern->aki_info.ai_devid);
     76 #else
     77 	info->agp_version_major = kern->version.major;
     78 	info->agp_version_minor = kern->version.minor;
     79 	info->mode = kern->mode;
     80 	info->aperture_base = kern->aper_base;
     81 	info->aperture_size = kern->aper_size * 1024 * 1024;
     82 	info->memory_allowed = kern->max_memory << PAGE_SHIFT;
     83 	info->memory_used = kern->current_memory << PAGE_SHIFT;
     84 	info->id_vendor = kern->device->vendor;
     85 	info->id_device = kern->device->device;
     86 #endif
     87 
     88 	return 0;
     89 }
     90 
     91 EXPORT_SYMBOL(drm_agp_info);
     92 
     93 static int drm_agp_info_ioctl_hook(struct drm_device *dev, void *data,
     94 		       struct drm_file *file_priv)
     95 {
     96 	struct drm_agp_info *info = data;
     97 	int err;
     98 
     99 	err = drm_agp_info(dev, info);
    100 	if (err)
    101 		return err;
    102 
    103 	return 0;
    104 }
    105 
    106 /**
    107  * Acquire the AGP device.
    108  *
    109  * \param dev DRM device that is to acquire AGP.
    110  * \return zero on success or a negative number on failure.
    111  *
    112  * Verifies the AGP device hasn't been acquired before and calls
    113  * \c agp_backend_acquire.
    114  */
    115 static int drm_agp_acquire_hook(struct drm_device * dev)
    116 {
    117 	if (!dev->agp)
    118 		return -ENODEV;
    119 	if (dev->agp->acquired)
    120 		return -EBUSY;
    121 	if (!(dev->agp->bridge = agp_backend_acquire(dev->pdev)))
    122 		return -ENODEV;
    123 	dev->agp->acquired = 1;
    124 	return 0;
    125 }
    126 
    127 EXPORT_SYMBOL(drm_agp_acquire);
    128 
    129 /**
    130  * Acquire the AGP device (ioctl).
    131  *
    132  * \param inode device inode.
    133  * \param file_priv DRM file private.
    134  * \param cmd command.
    135  * \param arg user argument.
    136  * \return zero on success or a negative number on failure.
    137  *
    138  * Verifies the AGP device hasn't been acquired before and calls
    139  * \c agp_backend_acquire.
    140  */
    141 static int drm_agp_acquire_ioctl_hook(struct drm_device *dev, void *data,
    142 			  struct drm_file *file_priv)
    143 {
    144 	return drm_agp_acquire((struct drm_device *) file_priv->minor->dev);
    145 }
    146 
    147 /**
    148  * Release the AGP device.
    149  *
    150  * \param dev DRM device that is to release AGP.
    151  * \return zero on success or a negative number on failure.
    152  *
    153  * Verifies the AGP device has been acquired and calls \c agp_backend_release.
    154  */
    155 static int drm_agp_release_hook(struct drm_device * dev)
    156 {
    157 	if (!dev->agp || !dev->agp->acquired)
    158 		return -EINVAL;
    159 	agp_backend_release(dev->agp->bridge);
    160 	dev->agp->acquired = 0;
    161 	return 0;
    162 }
    163 EXPORT_SYMBOL(drm_agp_release);
    164 
    165 static int drm_agp_release_ioctl_hook(struct drm_device *dev, void *data,
    166 			  struct drm_file *file_priv)
    167 {
    168 	return drm_agp_release(dev);
    169 }
    170 
    171 /**
    172  * Enable the AGP bus.
    173  *
    174  * \param dev DRM device that has previously acquired AGP.
    175  * \param mode Requested AGP mode.
    176  * \return zero on success or a negative number on failure.
    177  *
    178  * Verifies the AGP device has been acquired but not enabled, and calls
    179  * \c agp_enable.
    180  */
    181 static int drm_agp_enable_hook(struct drm_device * dev, struct drm_agp_mode mode)
    182 {
    183 	if (!dev->agp || !dev->agp->acquired)
    184 		return -EINVAL;
    185 
    186 	dev->agp->mode = mode.mode;
    187 	agp_enable(dev->agp->bridge, mode.mode);
    188 	dev->agp->enabled = 1;
    189 	return 0;
    190 }
    191 
    192 EXPORT_SYMBOL(drm_agp_enable);
    193 
    194 static int drm_agp_enable_ioctl_hook(struct drm_device *dev, void *data,
    195 			 struct drm_file *file_priv)
    196 {
    197 	struct drm_agp_mode *mode = data;
    198 
    199 	return drm_agp_enable(dev, *mode);
    200 }
    201 
    202 /**
    203  * Allocate AGP memory.
    204  *
    205  * \param inode device inode.
    206  * \param file_priv file private pointer.
    207  * \param cmd command.
    208  * \param arg pointer to a drm_agp_buffer structure.
    209  * \return zero on success or a negative number on failure.
    210  *
    211  * Verifies the AGP device is present and has been acquired, allocates the
    212  * memory via agp_allocate_memory() and creates a drm_agp_mem entry for it.
    213  */
    214 static int drm_agp_alloc_hook(struct drm_device *dev, struct drm_agp_buffer *request)
    215 {
    216 	struct drm_agp_mem *entry;
    217 	struct agp_memory *memory;
    218 	unsigned long pages;
    219 	u32 type;
    220 
    221 	if (!dev->agp || !dev->agp->acquired)
    222 		return -EINVAL;
    223 	if (!(entry = kzalloc(sizeof(*entry), GFP_KERNEL)))
    224 		return -ENOMEM;
    225 
    226 	pages = (request->size + AGP_PAGE_SIZE - 1) / AGP_PAGE_SIZE;
    227 	type = (u32) request->type;
    228 	if (!(memory = agp_allocate_memory(dev->agp->bridge, pages, type))) {
    229 		kfree(entry);
    230 		return -ENOMEM;
    231 	}
    232 
    233 #ifdef __NetBSD__
    234 	/* I presume the `+ 1' is there to avoid an id of 0 or something.  */
    235 	entry->handle = (unsigned long)memory->am_id + 1;
    236 #else
    237 	entry->handle = (unsigned long)memory->key + 1;
    238 #endif
    239 	entry->memory = memory;
    240 	entry->bound = 0;
    241 	entry->pages = pages;
    242 	list_add(&entry->head, &dev->agp->memory);
    243 
    244 	request->handle = entry->handle;
    245 #ifdef __NetBSD__
    246 	{
    247 		struct agp_memory_info info;
    248 		agp_memory_info(dev->agp->bridge, memory, &info);
    249 		request->physical = info.ami_physical;
    250 	}
    251 #else
    252 	request->physical = memory->physical;
    253 #endif
    254 
    255 	return 0;
    256 }
    257 EXPORT_SYMBOL(drm_agp_alloc);
    258 
    259 
    260 static int drm_agp_alloc_ioctl_hook(struct drm_device *dev, void *data,
    261 			struct drm_file *file_priv)
    262 {
    263 	struct drm_agp_buffer *request = data;
    264 
    265 	return drm_agp_alloc(dev, request);
    266 }
    267 
    268 /**
    269  * Search for the AGP memory entry associated with a handle.
    270  *
    271  * \param dev DRM device structure.
    272  * \param handle AGP memory handle.
    273  * \return pointer to the drm_agp_mem structure associated with \p handle.
    274  *
    275  * Walks through drm_agp_head::memory until finding a matching handle.
    276  */
    277 static struct drm_agp_mem *drm_agp_lookup_entry(struct drm_device * dev,
    278 					   unsigned long handle)
    279 {
    280 	struct drm_agp_mem *entry;
    281 
    282 	list_for_each_entry(entry, &dev->agp->memory, head) {
    283 		if (entry->handle == handle)
    284 			return entry;
    285 	}
    286 	return NULL;
    287 }
    288 
    289 /**
    290  * Unbind AGP memory from the GATT (ioctl).
    291  *
    292  * \param inode device inode.
    293  * \param file_priv DRM file private.
    294  * \param cmd command.
    295  * \param arg pointer to a drm_agp_binding structure.
    296  * \return zero on success or a negative number on failure.
    297  *
    298  * Verifies the AGP device is present and acquired, looks-up the AGP memory
    299  * entry and passes it to the unbind_agp() function.
    300  */
    301 static int drm_agp_unbind_hook(struct drm_device *dev, struct drm_agp_binding *request)
    302 {
    303 	struct drm_agp_mem *entry;
    304 	int ret;
    305 
    306 	if (!dev->agp || !dev->agp->acquired)
    307 		return -EINVAL;
    308 	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
    309 		return -EINVAL;
    310 	if (!entry->bound)
    311 		return -EINVAL;
    312 #ifdef __NetBSD__
    313 	ret = drm_unbind_agp(dev->agp->bridge, entry->memory);
    314 #else
    315 	ret = drm_unbind_agp(entry->memory);
    316 #endif
    317 	if (ret == 0)
    318 		entry->bound = 0;
    319 	return ret;
    320 }
    321 EXPORT_SYMBOL(drm_agp_unbind);
    322 
    323 
    324 static int drm_agp_unbind_ioctl_hook(struct drm_device *dev, void *data,
    325 			 struct drm_file *file_priv)
    326 {
    327 	struct drm_agp_binding *request = data;
    328 
    329 	return drm_agp_unbind(dev, request);
    330 }
    331 
    332 /**
    333  * Bind AGP memory into the GATT (ioctl)
    334  *
    335  * \param inode device inode.
    336  * \param file_priv DRM file private.
    337  * \param cmd command.
    338  * \param arg pointer to a drm_agp_binding structure.
    339  * \return zero on success or a negative number on failure.
    340  *
    341  * Verifies the AGP device is present and has been acquired and that no memory
    342  * is currently bound into the GATT. Looks-up the AGP memory entry and passes
    343  * it to bind_agp() function.
    344  */
    345 static int drm_agp_bind_hook(struct drm_device *dev, struct drm_agp_binding *request)
    346 {
    347 	struct drm_agp_mem *entry;
    348 	int retcode;
    349 	int page;
    350 
    351 	if (!dev->agp || !dev->agp->acquired)
    352 		return -EINVAL;
    353 	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
    354 		return -EINVAL;
    355 	if (entry->bound)
    356 		return -EINVAL;
    357 	page = (request->offset + AGP_PAGE_SIZE - 1) / AGP_PAGE_SIZE;
    358 #ifdef __NetBSD__
    359 	if ((retcode = drm_bind_agp(dev->agp->bridge, entry->memory, page)))
    360 		return retcode;
    361 #else
    362 	if ((retcode = drm_bind_agp(entry->memory, page)))
    363 		return retcode;
    364 #endif
    365 	entry->bound = dev->agp->base + (page << PAGE_SHIFT);
    366 	DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n",
    367 		  dev->agp->base, entry->bound);
    368 	return 0;
    369 }
    370 EXPORT_SYMBOL(drm_agp_bind);
    371 
    372 
    373 static int drm_agp_bind_ioctl_hook(struct drm_device *dev, void *data,
    374 		       struct drm_file *file_priv)
    375 {
    376 	struct drm_agp_binding *request = data;
    377 
    378 	return drm_agp_bind(dev, request);
    379 }
    380 
    381 /**
    382  * Free AGP memory (ioctl).
    383  *
    384  * \param inode device inode.
    385  * \param file_priv DRM file private.
    386  * \param cmd command.
    387  * \param arg pointer to a drm_agp_buffer structure.
    388  * \return zero on success or a negative number on failure.
    389  *
    390  * Verifies the AGP device is present and has been acquired and looks up the
    391  * AGP memory entry. If the memory it's currently bound, unbind it via
    392  * unbind_agp(). Frees it via free_agp() as well as the entry itself
    393  * and unlinks from the doubly linked list it's inserted in.
    394  */
    395 static int drm_agp_free_hook(struct drm_device *dev, struct drm_agp_buffer *request)
    396 {
    397 	struct drm_agp_mem *entry;
    398 
    399 	if (!dev->agp || !dev->agp->acquired)
    400 		return -EINVAL;
    401 	if (!(entry = drm_agp_lookup_entry(dev, request->handle)))
    402 		return -EINVAL;
    403 	if (entry->bound)
    404 #ifdef __NetBSD__
    405 		drm_unbind_agp(dev->agp->bridge, entry->memory);
    406 #else
    407 		drm_unbind_agp(entry->memory);
    408 #endif
    409 
    410 	list_del(&entry->head);
    411 
    412 #ifdef __NetBSD__
    413 	drm_free_agp(dev->agp->bridge, entry->memory, entry->pages);
    414 #else
    415 	drm_free_agp(entry->memory, entry->pages);
    416 #endif
    417 	kfree(entry);
    418 	return 0;
    419 }
    420 EXPORT_SYMBOL(drm_agp_free);
    421 
    422 
    423 
    424 static int drm_agp_free_ioctl_hook(struct drm_device *dev, void *data,
    425 		       struct drm_file *file_priv)
    426 {
    427 	struct drm_agp_buffer *request = data;
    428 
    429 	return drm_agp_free(dev, request);
    430 }
    431 
    432 /**
    433  * Initialize the AGP resources.
    434  *
    435  * \return pointer to a drm_agp_head structure.
    436  *
    437  * Gets the drm_agp_t structure which is made available by the agpgart module
    438  * via the inter_module_* functions. Creates and initializes a drm_agp_head
    439  * structure.
    440  *
    441  * Note that final cleanup of the kmalloced structure is directly done in
    442  * drm_pci_agp_destroy.
    443  */
    444 static struct drm_agp_head *drm_agp_init_hook(struct drm_device *dev)
    445 {
    446 	struct drm_agp_head *head = NULL;
    447 
    448 	if (!(head = kzalloc(sizeof(*head), GFP_KERNEL)))
    449 		return NULL;
    450 	head->bridge = agp_find_bridge(dev->pdev);
    451 	if (!head->bridge) {
    452 		if (!(head->bridge = agp_backend_acquire(dev->pdev))) {
    453 			kfree(head);
    454 			return NULL;
    455 		}
    456 		agp_copy_info(head->bridge, &head->agp_info);
    457 		agp_backend_release(head->bridge);
    458 	} else {
    459 		agp_copy_info(head->bridge, &head->agp_info);
    460 	}
    461 #ifndef __NetBSD__
    462 	/* Why would anything even attach in this case?  */
    463 	if (head->agp_info.chipset == NOT_SUPPORTED) {
    464 		kfree(head);
    465 		return NULL;
    466 	}
    467 #endif
    468 	INIT_LIST_HEAD(&head->memory);
    469 #ifdef __NetBSD__
    470 	head->cant_use_aperture = false; /* XXX */
    471 	head->page_mask = ~0UL;
    472 	head->base = head->agp_info.aki_info.ai_aperture_base;
    473 #else
    474 	head->cant_use_aperture = head->agp_info.cant_use_aperture;
    475 	head->page_mask = head->agp_info.page_mask;
    476 	head->base = head->agp_info.aper_base;
    477 #endif
    478 	return head;
    479 }
    480 
    481 /**
    482  * drm_agp_clear - Clear AGP resource list
    483  * @dev: DRM device
    484  *
    485  * Iterate over all AGP resources and remove them. But keep the AGP head
    486  * intact so it can still be used. It is safe to call this if AGP is disabled or
    487  * was already removed.
    488  *
    489  * If DRIVER_MODESET is active, nothing is done to protect the modesetting
    490  * resources from getting destroyed. Drivers are responsible of cleaning them up
    491  * during device shutdown.
    492  */
    493 static void drm_agp_clear_hook(struct drm_device *dev)
    494 {
    495 	struct drm_agp_mem *entry, *tempe;
    496 
    497 	if (!dev->agp)
    498 		return;
    499 	if (drm_core_check_feature(dev, DRIVER_MODESET))
    500 		return;
    501 
    502 	list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) {
    503 #ifdef __NetBSD__
    504 		if (entry->bound)
    505 			drm_unbind_agp(dev->agp->bridge, entry->memory);
    506 		drm_free_agp(dev->agp->bridge, entry->memory, entry->pages);
    507 #else
    508 		if (entry->bound)
    509 			drm_unbind_agp(entry->memory);
    510 		drm_free_agp(entry->memory, entry->pages);
    511 #endif
    512 		kfree(entry);
    513 	}
    514 	INIT_LIST_HEAD(&dev->agp->memory);
    515 
    516 	if (dev->agp->acquired)
    517 		drm_agp_release(dev);
    518 
    519 	dev->agp->acquired = 0;
    520 	dev->agp->enabled = 0;
    521 }
    522 
    523 #ifndef __NetBSD__		/* XXX Dead code that doesn't make sense...  */
    524 /**
    525  * Binds a collection of pages into AGP memory at the given offset, returning
    526  * the AGP memory structure containing them.
    527  *
    528  * No reference is held on the pages during this time -- it is up to the
    529  * caller to handle that.
    530  */
    531 struct agp_memory *
    532 drm_agp_bind_pages(struct drm_device *dev,
    533 		   struct page **pages,
    534 		   unsigned long num_pages,
    535 		   uint32_t gtt_offset,
    536 		   u32 type)
    537 {
    538 	struct agp_memory *mem;
    539 	int ret, i;
    540 
    541 	DRM_DEBUG("\n");
    542 
    543 	mem = agp_allocate_memory(dev->agp->bridge, num_pages,
    544 				      type);
    545 	if (mem == NULL) {
    546 		DRM_ERROR("Failed to allocate memory for %ld pages\n",
    547 			  num_pages);
    548 		return NULL;
    549 	}
    550 
    551 	for (i = 0; i < num_pages; i++)
    552 		mem->pages[i] = pages[i];
    553 	mem->page_count = num_pages;
    554 
    555 	mem->is_flushed = true;
    556 	ret = agp_bind_memory(mem, gtt_offset / AGP_PAGE_SIZE);
    557 	if (ret != 0) {
    558 		DRM_ERROR("Failed to bind AGP memory: %d\n", ret);
    559 		agp_free_memory(mem);
    560 		return NULL;
    561 	}
    562 
    563 	return mem;
    564 }
    565 EXPORT_SYMBOL(drm_agp_bind_pages);
    566 #endif
    567 
    568 #ifdef __NetBSD__
    569 
    570 static void __pci_iomem *
    571 drm_agp_borrow_hook(struct drm_device *dev, unsigned i, bus_size_t size)
    572 {
    573 	struct pci_dev *pdev = dev->pdev;
    574 
    575 	if (!agp_i810_borrow(pdev->pd_resources[i].addr, size,
    576 		&pdev->pd_resources[i].bsh))
    577 		return NULL;
    578 	/* XXX Synchronize with pci_iomap in linux_pci.c.  */
    579 	pdev->pd_resources[i].bst = pdev->pd_pa.pa_memt;
    580 	pdev->pd_resources[i].kva = bus_space_vaddr(pdev->pd_resources[i].bst,
    581 	    pdev->pd_resources[i].bsh);
    582 	pdev->pd_resources[i].mapped = true;
    583 
    584 	return pdev->pd_resources[i].kva;
    585 }
    586 
    587 static void
    588 drm_agp_flush_hook(void)
    589 {
    590 
    591 	agp_flush_cache();
    592 }
    593 
    594 static const struct drm_agp_hooks agp_hooks = {
    595 	.agph_info = drm_agp_info_hook,
    596 	.agph_info_ioctl = drm_agp_info_ioctl_hook,
    597 	.agph_acquire = drm_agp_acquire_hook,
    598 	.agph_acquire_ioctl = drm_agp_acquire_ioctl_hook,
    599 	.agph_release = drm_agp_release_hook,
    600 	.agph_release_ioctl = drm_agp_release_ioctl_hook,
    601 	.agph_enable = drm_agp_enable_hook,
    602 	.agph_enable_ioctl = drm_agp_enable_ioctl_hook,
    603 	.agph_alloc = drm_agp_alloc_hook,
    604 	.agph_alloc_ioctl = drm_agp_alloc_ioctl_hook,
    605 	.agph_unbind = drm_agp_unbind_hook,
    606 	.agph_unbind_ioctl = drm_agp_unbind_ioctl_hook,
    607 	.agph_bind = drm_agp_bind_hook,
    608 	.agph_bind_ioctl = drm_agp_bind_ioctl_hook,
    609 	.agph_free = drm_agp_free_hook,
    610 	.agph_free_ioctl = drm_agp_free_ioctl_hook,
    611 	.agph_init = drm_agp_init_hook,
    612 	.agph_clear = drm_agp_clear_hook,
    613 	.agph_borrow = drm_agp_borrow_hook,
    614 	.agph_flush = drm_agp_flush_hook,
    615 };
    616 
    617 #include <sys/module.h>
    618 #include <sys/once.h>
    619 
    620 MODULE(MODULE_CLASS_MISC, drmkms_agp, "drmkms"); /* XXX agp */
    621 
    622 static int
    623 drmkms_agp_init(void)
    624 {
    625 
    626 	return drm_agp_register(&agp_hooks);
    627 }
    628 
    629 int
    630 drmkms_agp_guarantee_initialized(void)
    631 {
    632 #ifdef _MODULE
    633 	return 0;
    634 #else
    635 	static ONCE_DECL(drmkms_agp_init_once);
    636 
    637 	return RUN_ONCE(&drmkms_agp_init_once, &drmkms_agp_init);
    638 #endif
    639 }
    640 
    641 static int
    642 drmkms_agp_fini(void)
    643 {
    644 
    645 	return drm_agp_deregister(&agp_hooks);
    646 }
    647 
    648 static int
    649 drmkms_agp_modcmd(modcmd_t cmd, void *arg __unused)
    650 {
    651 	int error;
    652 
    653 	switch (cmd) {
    654 	case MODULE_CMD_INIT:
    655 #ifdef _MODULE
    656 		error = drmkms_agp_init();
    657 #else
    658 		error = drmkms_agp_guarantee_initialized();
    659 #endif
    660 		if (error)
    661 			return error;
    662 		return 0;
    663 	case MODULE_CMD_FINI:
    664 		error = drmkms_agp_fini();
    665 		if (error)
    666 			return error;
    667 		return 0;
    668 	default:
    669 		return ENOTTY;
    670 	}
    671 }
    672 
    673 #endif	/* __NetBSD__ */
    674