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