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