1/* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6/* 7 * drm_agpsupport.h -- DRM support for AGP/GART backend -*- linux-c -*- 8 * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com 9 */ 10/* 11 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 12 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 13 * Copyright (c) 2009, Intel Corporation. 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 * Author: 36 * Rickard E. (Rik) Faith <faith@valinux.com> 37 * Gareth Hughes <gareth@valinux.com> 38 * 39 */ 40 41#include "drm.h" 42#include "drmP.h" 43 44#ifndef AGP_PAGE_SIZE 45#define AGP_PAGE_SIZE 4096 46#define AGP_PAGE_SHIFT 12 47#endif 48 49/* 50 * The agpa_key field of struct agp_allocate_t actually is 51 * an index to an array. It can be zero. But we will use 52 * this agpa_key as a handle returned to userland. Generally, 53 * 0 is not a valid value for a handle, so we add an offset 54 * to the key to get a handle. 55 */ 56#define DRM_AGP_KEY_OFFSET 8 57 58extern int drm_supp_device_capability(void *handle, int capid); 59 60/*ARGSUSED*/ 61int 62drm_device_is_agp(drm_device_t *dev) 63{ 64 int ret; 65 66 if (dev->driver->device_is_agp != NULL) { 67 /* 68 * device_is_agp returns a tristate: 69 * 0 = not AGP; 70 * 1 = definitely AGP; 71 * 2 = fall back to PCI capability 72 */ 73 ret = (*dev->driver->device_is_agp)(dev); 74 if (ret != DRM_MIGHT_BE_AGP) 75 return (ret); 76 } 77 78 return (drm_supp_device_capability(dev->drm_handle, PCIY_AGP)); 79 80} 81 82/*ARGSUSED*/ 83int 84drm_device_is_pcie(drm_device_t *dev) 85{ 86 return (drm_supp_device_capability(dev->drm_handle, PCIY_EXPRESS)); 87} 88 89 90/*ARGSUSED*/ 91int 92drm_agp_info(DRM_IOCTL_ARGS) 93{ 94 DRM_DEVICE; 95 agp_info_t *agpinfo; 96 drm_agp_info_t info; 97 98 if (!dev->agp || !dev->agp->acquired) 99 return (EINVAL); 100 101 agpinfo = &dev->agp->agp_info; 102 info.agp_version_major = agpinfo->agpi_version.agpv_major; 103 info.agp_version_minor = agpinfo->agpi_version.agpv_minor; 104 info.mode = agpinfo->agpi_mode; 105 info.aperture_base = agpinfo->agpi_aperbase; 106 info.aperture_size = agpinfo->agpi_apersize* 1024 * 1024; 107 info.memory_allowed = agpinfo->agpi_pgtotal << PAGE_SHIFT; 108 info.memory_used = agpinfo->agpi_pgused << PAGE_SHIFT; 109 info.id_vendor = agpinfo->agpi_devid & 0xffff; 110 info.id_device = agpinfo->agpi_devid >> 16; 111 112 DRM_COPYTO_WITH_RETURN((void *)data, &info, sizeof (info)); 113 return (0); 114} 115 116/*ARGSUSED*/ 117int 118drm_agp_acquire(DRM_IOCTL_ARGS) 119{ 120 DRM_DEVICE; 121 int ret, rval; 122 123 if (!dev->agp) { 124 DRM_ERROR("drm_agp_acquire : agp isn't initialized yet"); 125 return (ENODEV); 126 } 127 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ACQUIRE, 128 (uintptr_t)0, FKIOCTL, kcred, &rval); 129 if (ret) { 130 DRM_ERROR("drm_agp_acquired: AGPIOC_ACQUIRE failed\n"); 131 return (EIO); 132 } 133 dev->agp->acquired = 1; 134 135 return (0); 136} 137 138/*ARGSUSED*/ 139int 140drm_agp_release(DRM_IOCTL_ARGS) 141{ 142 DRM_DEVICE; 143 int ret, rval; 144 145 if (!dev->agp) 146 return (ENODEV); 147 if (!dev->agp->acquired) 148 return (EBUSY); 149 150 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RELEASE, 151 (intptr_t)0, FKIOCTL, kcred, &rval); 152 if (ret) { 153 DRM_ERROR("drm_agp_release: AGPIOC_RELEASE failed\n"); 154 return (ENXIO); 155 } 156 dev->agp->acquired = 0; 157 158 return (ret); 159} 160 161 162int 163drm_agp_do_release(drm_device_t *dev) 164{ 165 int ret, rval; 166 167 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RELEASE, 168 (intptr_t)0, FKIOCTL, kcred, &rval); 169 170 if (ret == 0) 171 dev->agp->acquired = 0; 172 173 return (ret); 174} 175 176/*ARGSUSED*/ 177int 178drm_agp_enable(DRM_IOCTL_ARGS) 179{ 180 DRM_DEVICE; 181 drm_agp_mode_t modes; 182 agp_setup_t setup; 183 int ret, rval; 184 185 if (!dev->agp) 186 return (ENODEV); 187 if (!dev->agp->acquired) 188 return (EBUSY); 189 190 DRM_COPYFROM_WITH_RETURN(&modes, (void *)data, sizeof (modes)); 191 192 dev->agp->mode = modes.mode; 193 setup.agps_mode = (uint32_t)modes.mode; 194 195 196 DRM_DEBUG("drm_agp_enable: dev->agp->mode=%lx", modes.mode); 197 198 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_SETUP, 199 (intptr_t)&setup, FKIOCTL, kcred, &rval); 200 if (ret) { 201 DRM_ERROR("drm_agp_enable: failed"); 202 return (EIO); 203 } 204 205 dev->agp->base = dev->agp->agp_info.agpi_aperbase; 206 dev->agp->enabled = 1; 207 208 DRM_DEBUG("drm_agp_enable: dev->agp->base=0x%lx", dev->agp->base); 209 return (0); 210} 211 212/*ARGSUSED*/ 213int 214drm_agp_alloc(DRM_IOCTL_ARGS) 215{ 216 DRM_DEVICE; 217 drm_agp_mem_t *entry; 218 agp_allocate_t alloc; 219 drm_agp_buffer_t request; 220 int pages; 221 int ret, rval; 222 223 if (!dev->agp || !dev->agp->acquired) 224 return (EINVAL); 225 226 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 227 228 entry = kmem_zalloc(sizeof (*entry), KM_SLEEP); 229 230 pages = btopr(request.size); 231 alloc.agpa_pgcount = pages; 232 alloc.agpa_type = AGP_NORMAL; 233 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ALLOCATE, 234 (intptr_t)&alloc, FKIOCTL, kcred, &rval); 235 if (ret) { 236 DRM_ERROR("drm_agp_alloc: AGPIOC_ALLOCATE failed, ret=%d", ret); 237 kmem_free(entry, sizeof (*entry)); 238 return (ret); 239 } 240 241 entry->bound = 0; 242 entry->pages = pages; 243 entry->handle = (void*)(uintptr_t)(alloc.agpa_key + DRM_AGP_KEY_OFFSET); 244 entry->prev = NULL; 245 entry->phys_addr = (void*)(uintptr_t)alloc.agpa_physical; 246 entry->next = dev->agp->memory; 247 if (dev->agp->memory) 248 dev->agp->memory->prev = entry; 249 dev->agp->memory = entry; 250 251 DRM_DEBUG("entry->phys_addr %lx", entry->phys_addr); 252 253 /* physical is used only by i810 driver */ 254 request.physical = alloc.agpa_physical; 255 request.handle = (unsigned long)entry->handle; 256 257 /* 258 * If failed to ddi_copyout(), we will free allocated AGP memory 259 * when closing drm 260 */ 261 DRM_COPYTO_WITH_RETURN((void *)data, &request, sizeof (request)); 262 263 return (0); 264} 265 266/*ARGSUSED*/ 267static drm_agp_mem_t * 268drm_agp_lookup_entry(drm_device_t *dev, void *handle) 269{ 270 drm_agp_mem_t *entry; 271 272 for (entry = dev->agp->memory; entry; entry = entry->next) { 273 if (entry->handle == handle) 274 return (entry); 275 } 276 277 return (NULL); 278} 279 280/*ARGSUSED*/ 281int 282drm_agp_unbind(DRM_IOCTL_ARGS) 283{ 284 DRM_DEVICE; 285 agp_unbind_t unbind; 286 drm_agp_binding_t request; 287 drm_agp_mem_t *entry; 288 int ret, rval; 289 290 if (!dev->agp || !dev->agp->acquired) 291 return (EINVAL); 292 293 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 294 295 if (!(entry = drm_agp_lookup_entry(dev, (void *)request.handle))) 296 return (EINVAL); 297 if (!entry->bound) 298 return (EINVAL); 299 300 unbind.agpu_pri = 0; 301 unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 302 303 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND, 304 (intptr_t)&unbind, FKIOCTL, kcred, &rval); 305 if (ret) { 306 DRM_ERROR("drm_agp_unbind: AGPIOC_UNBIND failed"); 307 return (EIO); 308 } 309 entry->bound = 0; 310 return (0); 311} 312 313/*ARGSUSED*/ 314int 315drm_agp_bind(DRM_IOCTL_ARGS) 316{ 317 DRM_DEVICE; 318 drm_agp_binding_t request; 319 drm_agp_mem_t *entry; 320 int start; 321 uint_t key; 322 323 if (!dev->agp || !dev->agp->acquired) 324 return (EINVAL); 325 326 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 327 328 entry = drm_agp_lookup_entry(dev, (void *)request.handle); 329 if (!entry || entry->bound) 330 return (EINVAL); 331 332 key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 333 start = btopr(request.offset); 334 if (drm_agp_bind_memory(key, start, dev)) { 335 DRM_ERROR("drm_agp_bind: failed key=%x, start=0x%x, " 336 "agp_base=0x%lx", key, start, dev->agp->base); 337 return (EIO); 338 } 339 340 entry->bound = dev->agp->base + (start << AGP_PAGE_SHIFT); 341 342 return (0); 343} 344 345/*ARGSUSED*/ 346int 347drm_agp_free(DRM_IOCTL_ARGS) 348{ 349 DRM_DEVICE; 350 drm_agp_buffer_t request; 351 drm_agp_mem_t *entry; 352 int ret, rval; 353 int agpu_key; 354 355 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 356 if (!dev->agp || !dev->agp->acquired) 357 return (EINVAL); 358 if (!(entry = drm_agp_lookup_entry(dev, (void *)request.handle))) 359 return (EINVAL); 360 if (entry->bound) 361 (void) drm_agp_unbind_memory(request.handle, dev); 362 363 if (entry == dev->agp->memory) 364 dev->agp->memory = entry->next; 365 if (entry->prev) 366 entry->prev->next = entry->next; 367 if (entry->next) 368 entry->next->prev = entry->prev; 369 370 agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 371 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_DEALLOCATE, 372 (intptr_t)agpu_key, FKIOCTL, kcred, &rval); 373 if (ret) { 374 DRM_ERROR("drm_agp_free: AGPIOC_DEALLOCATE failed," 375 "akey=%d, ret=%d", agpu_key, ret); 376 return (EIO); 377 } 378 drm_free(entry, sizeof (*entry), DRM_MEM_AGPLISTS); 379 return (0); 380} 381 382/*ARGSUSED*/ 383drm_agp_head_t * 384drm_agp_init(drm_device_t *dev) 385{ 386 drm_agp_head_t *agp = NULL; 387 int retval, rval; 388 389 agp = kmem_zalloc(sizeof (drm_agp_head_t), KM_SLEEP); 390 391 retval = ldi_ident_from_dip(dev->dip, &agp->agpgart_li); 392 if (retval != 0) { 393 DRM_ERROR("drm_agp_init: failed to get layerd ident, retval=%d", 394 retval); 395 goto err_1; 396 } 397 398 retval = ldi_open_by_name(AGP_DEVICE, FEXCL, kcred, 399 &agp->agpgart_lh, agp->agpgart_li); 400 if (retval != 0) { 401 DRM_ERROR("drm_agp_init: failed to open %s, retval=%d", 402 AGP_DEVICE, retval); 403 goto err_2; 404 } 405 406 retval = ldi_ioctl(agp->agpgart_lh, AGPIOC_INFO, 407 (intptr_t)&agp->agp_info, FKIOCTL, kcred, &rval); 408 409 if (retval != 0) { 410 DRM_ERROR("drm_agp_init: failed to get agpinfo, retval=%d", 411 retval); 412 goto err_3; 413 } 414 415 return (agp); 416 417err_3: 418 (void) ldi_close(agp->agpgart_lh, FEXCL, kcred); 419 420err_2: 421 ldi_ident_release(agp->agpgart_li); 422 423err_1: 424 kmem_free(agp, sizeof (drm_agp_head_t)); 425 return (NULL); 426} 427 428/*ARGSUSED*/ 429void 430drm_agp_fini(drm_device_t *dev) 431{ 432 drm_agp_head_t *agp = dev->agp; 433 (void) ldi_close(agp->agpgart_lh, FEXCL, kcred); 434 ldi_ident_release(agp->agpgart_li); 435 kmem_free(agp, sizeof (drm_agp_head_t)); 436 dev->agp = NULL; 437} 438 439 440/*ARGSUSED*/ 441void * 442drm_agp_allocate_memory(size_t pages, uint32_t type, drm_device_t *dev) 443{ 444 return (NULL); 445} 446 447/*ARGSUSED*/ 448int 449drm_agp_free_memory(agp_allocate_t *handle, drm_device_t *dev) 450{ 451 return (1); 452} 453 454/*ARGSUSED*/ 455int 456drm_agp_bind_memory(unsigned int key, uint32_t start, drm_device_t *dev) 457{ 458 agp_bind_t bind; 459 int ret, rval; 460 461 bind.agpb_pgstart = start; 462 bind.agpb_key = key; 463 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_BIND, 464 (intptr_t)&bind, FKIOCTL, kcred, &rval); 465 if (ret) { 466 DRM_DEBUG("drm_agp_bind_meory: AGPIOC_BIND failed"); 467 return (EIO); 468 } 469 return (0); 470} 471 472/*ARGSUSED*/ 473int 474drm_agp_unbind_memory(unsigned long handle, drm_device_t *dev) 475{ 476 agp_unbind_t unbind; 477 drm_agp_mem_t *entry; 478 int ret, rval; 479 480 if (!dev->agp || !dev->agp->acquired) 481 return (EINVAL); 482 483 entry = drm_agp_lookup_entry(dev, (void *)handle); 484 if (!entry || !entry->bound) 485 return (EINVAL); 486 487 unbind.agpu_pri = 0; 488 unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 489 490 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND, 491 (intptr_t)&unbind, FKIOCTL, kcred, &rval); 492 if (ret) { 493 DRM_ERROR("drm_agp_unbind: AGPIO_UNBIND failed"); 494 return (EIO); 495 } 496 entry->bound = 0; 497 return (0); 498} 499 500/* 501 * Binds a collection of pages into AGP memory at the given offset, returning 502 * the AGP memory structure containing them. 503 * 504 * No reference is held on the pages during this time -- it is up to the 505 * caller to handle that. 506 */ 507int 508drm_agp_bind_pages(drm_device_t *dev, 509 pfn_t *pages, 510 unsigned long num_pages, 511 uint32_t gtt_offset) 512{ 513 514 agp_bind_pages_t bind; 515 int ret, rval; 516 517 bind.agpb_pgstart = gtt_offset / AGP_PAGE_SIZE; 518 bind.agpb_pgcount = num_pages; 519 bind.agpb_pages = pages; 520 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_BIND, 521 (intptr_t)&bind, FKIOCTL, kcred, &rval); 522 if (ret) { 523 DRM_ERROR("AGPIOC_PAGES_BIND failed ret %d", ret); 524 return (ret); 525 } 526 return (0); 527} 528 529int 530drm_agp_unbind_pages(drm_device_t *dev, 531 unsigned long num_pages, 532 uint32_t gtt_offset, 533 uint32_t type) 534{ 535 536 agp_unbind_pages_t unbind; 537 int ret, rval; 538 539 unbind.agpb_pgstart = gtt_offset / AGP_PAGE_SIZE; 540 unbind.agpb_pgcount = num_pages; 541 unbind.agpb_type = type; 542 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_UNBIND, 543 (intptr_t)&unbind, FKIOCTL, kcred, &rval); 544 if (ret) { 545 DRM_DEBUG("drm_agp_unbind_pages AGPIOC_PAGES_UNBIND failed"); 546 return (ret); 547 } 548 return (0); 549} 550 551/* 552 * Certain Intel chipsets contains a global write buffer, and this can require 553 * flushing from the drm or X.org to make sure all data has hit RAM before 554 * initiating a GPU transfer, due to a lack of coherency with the integrated 555 * graphics device and this buffer. 556 */ 557void 558drm_agp_chipset_flush(struct drm_device *dev) 559{ 560 int ret, rval; 561 562 DRM_DEBUG("agp_chipset_flush"); 563 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_FLUSHCHIPSET, 564 (intptr_t)0, FKIOCTL, kcred, &rval); 565 if (ret != 0) { 566 DRM_ERROR("Failed to drm_agp_chipset_flush ret %d", ret); 567 } 568} 569 570/* 571 * The pages are evict on suspend, so re-bind it at resume time 572 */ 573void 574drm_agp_rebind(struct drm_device *dev) 575{ 576 int ret, rval; 577 578 if (!dev->agp) { 579 return; 580 } 581 582 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_REBIND, 583 (intptr_t)0, FKIOCTL, kcred, &rval); 584 if (ret != 0) { 585 DRM_ERROR("rebind failed %d", ret); 586 } 587} 588