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@valinux.com> 8 * \author Gareth Hughes <gareth@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