1 /* 2 * Copyright 2008 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 * All Rights Reserved. 14 * 15 * Permission is hereby granted, free of charge, to any person obtaining a 16 * copy of this software and associated documentation files (the "Software"), 17 * to deal in the Software without restriction, including without limitation 18 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 19 * and/or sell copies of the Software, and to permit persons to whom the 20 * Software is furnished to do so, subject to the following conditions: 21 * 22 * The above copyright notice and this permission notice (including the next 23 * paragraph) shall be included in all copies or substantial portions of the 24 * Software. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 29 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 30 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 31 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 * OTHER DEALINGS IN THE SOFTWARE. 33 * 34 * Author: 35 * Rickard E. (Rik) Faith <faith@valinux.com> 36 * Gareth Hughes <gareth@valinux.com> 37 * 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include "drm.h" 43 #include "drmP.h" 44 45 #ifndef AGP_PAGE_SIZE 46 #define AGP_PAGE_SIZE 4096 47 #define AGP_PAGE_SHIFT 12 48 #endif 49 50 /* 51 * The agpa_key field of struct agp_allocate_t actually is 52 * an index to an array. It can be zero. But we will use 53 * this agpa_key as a handle returned to userland. Generally, 54 * 0 is not a valid value for a handle, so we add an offset 55 * to the key to get a handle. 56 */ 57 #define DRM_AGP_KEY_OFFSET 8 58 59 extern int drm_supp_device_capability(void *handle, int capid); 60 61 /*ARGSUSED*/ 62 int 63 drm_device_is_agp(drm_device_t *dev) 64 { 65 int ret; 66 67 if (dev->driver->device_is_agp != NULL) { 68 /* 69 * device_is_agp returns a tristate: 70 * 0 = not AGP; 71 * 1 = definitely AGP; 72 * 2 = fall back to PCI capability 73 */ 74 ret = (*dev->driver->device_is_agp)(dev); 75 if (ret != DRM_MIGHT_BE_AGP) 76 return (ret); 77 } 78 79 return (drm_supp_device_capability(dev->drm_handle, PCIY_AGP)); 80 81 } 82 83 /*ARGSUSED*/ 84 int 85 drm_device_is_pcie(drm_device_t *dev) 86 { 87 return (drm_supp_device_capability(dev->drm_handle, PCIY_EXPRESS)); 88 } 89 90 91 /*ARGSUSED*/ 92 int 93 drm_agp_info(DRM_IOCTL_ARGS) 94 { 95 DRM_DEVICE; 96 agp_info_t *agpinfo; 97 drm_agp_info_t info; 98 99 if (!dev->agp || !dev->agp->acquired) 100 return (EINVAL); 101 102 agpinfo = &dev->agp->agp_info; 103 info.agp_version_major = agpinfo->agpi_version.agpv_major; 104 info.agp_version_minor = agpinfo->agpi_version.agpv_minor; 105 info.mode = agpinfo->agpi_mode; 106 info.aperture_base = agpinfo->agpi_aperbase; 107 info.aperture_size = agpinfo->agpi_apersize* 1024 * 1024; 108 info.memory_allowed = agpinfo->agpi_pgtotal << PAGE_SHIFT; 109 info.memory_used = agpinfo->agpi_pgused << PAGE_SHIFT; 110 info.id_vendor = agpinfo->agpi_devid & 0xffff; 111 info.id_device = agpinfo->agpi_devid >> 16; 112 113 DRM_COPYTO_WITH_RETURN((void *)data, &info, sizeof (info)); 114 return (0); 115 } 116 117 /*ARGSUSED*/ 118 int 119 drm_agp_acquire(DRM_IOCTL_ARGS) 120 { 121 DRM_DEVICE; 122 int ret, rval; 123 124 if (!dev->agp) { 125 DRM_ERROR("drm_agp_acquire : agp isn't initialized yet"); 126 return (ENODEV); 127 } 128 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ACQUIRE, 129 (uintptr_t)0, FKIOCTL, kcred, &rval); 130 if (ret) { 131 DRM_ERROR("drm_agp_acquired: AGPIOC_ACQUIRE failed\n"); 132 return (EIO); 133 } 134 dev->agp->acquired = 1; 135 136 return (0); 137 } 138 139 /*ARGSUSED*/ 140 int 141 drm_agp_release(DRM_IOCTL_ARGS) 142 { 143 DRM_DEVICE; 144 int ret, rval; 145 146 if (!dev->agp) 147 return (ENODEV); 148 if (!dev->agp->acquired) 149 return (EBUSY); 150 151 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RELEASE, 152 (intptr_t)0, FKIOCTL, kcred, &rval); 153 if (ret) { 154 DRM_ERROR("drm_agp_release: AGPIOC_RELEASE failed\n"); 155 return (ENXIO); 156 } 157 dev->agp->acquired = 0; 158 159 return (ret); 160 } 161 162 163 int 164 drm_agp_do_release(drm_device_t *dev) 165 { 166 int ret, rval; 167 168 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_RELEASE, 169 (intptr_t)0, FKIOCTL, kcred, &rval); 170 171 if (ret == 0) 172 dev->agp->acquired = 0; 173 174 return (ret); 175 } 176 177 /*ARGSUSED*/ 178 int 179 drm_agp_enable(DRM_IOCTL_ARGS) 180 { 181 DRM_DEVICE; 182 drm_agp_mode_t modes; 183 agp_setup_t setup; 184 int ret, rval; 185 186 if (!dev->agp) 187 return (ENODEV); 188 if (!dev->agp->acquired) 189 return (EBUSY); 190 191 DRM_COPYFROM_WITH_RETURN(&modes, (void *)data, sizeof (modes)); 192 193 dev->agp->mode = modes.mode; 194 setup.agps_mode = (uint32_t)modes.mode; 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 return (0); 209 } 210 211 /*ARGSUSED*/ 212 int 213 drm_agp_alloc(DRM_IOCTL_ARGS) 214 { 215 DRM_DEVICE; 216 drm_agp_mem_t *entry; 217 agp_allocate_t alloc; 218 drm_agp_buffer_t request; 219 int pages; 220 int ret, rval; 221 222 if (!dev->agp || !dev->agp->acquired) 223 return (EINVAL); 224 225 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 226 227 entry = kmem_zalloc(sizeof (*entry), KM_SLEEP); 228 229 pages = btopr(request.size); 230 alloc.agpa_pgcount = pages; 231 alloc.agpa_type = AGP_NORMAL; 232 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_ALLOCATE, 233 (intptr_t)&alloc, FKIOCTL, kcred, &rval); 234 if (ret) { 235 DRM_ERROR("drm_agp_alloc: AGPIOC_ALLOCATE failed, ret=%d", ret); 236 kmem_free(entry, sizeof (*entry)); 237 return (ret); 238 } 239 240 entry->bound = 0; 241 entry->pages = pages; 242 entry->handle = (void*)(uintptr_t)(alloc.agpa_key + DRM_AGP_KEY_OFFSET); 243 entry->prev = NULL; 244 entry->phys_addr = (void*)(uintptr_t)alloc.agpa_physical; 245 entry->next = dev->agp->memory; 246 if (dev->agp->memory) 247 dev->agp->memory->prev = entry; 248 dev->agp->memory = entry; 249 250 /* physical is used only by i810 driver */ 251 request.physical = alloc.agpa_physical; 252 request.handle = (unsigned long)entry->handle; 253 254 /* 255 * If failed to ddi_copyout(), we will free allocated AGP memory 256 * when closing drm 257 */ 258 DRM_COPYTO_WITH_RETURN((void *)data, &request, sizeof (request)); 259 260 return (0); 261 } 262 263 /*ARGSUSED*/ 264 static drm_agp_mem_t * 265 drm_agp_lookup_entry(drm_device_t *dev, void *handle) 266 { 267 drm_agp_mem_t *entry; 268 269 for (entry = dev->agp->memory; entry; entry = entry->next) { 270 if (entry->handle == handle) 271 return (entry); 272 } 273 274 return (NULL); 275 } 276 277 /*ARGSUSED*/ 278 int 279 drm_agp_unbind(DRM_IOCTL_ARGS) 280 { 281 DRM_DEVICE; 282 agp_unbind_t unbind; 283 drm_agp_binding_t request; 284 drm_agp_mem_t *entry; 285 int ret, rval; 286 287 if (!dev->agp || !dev->agp->acquired) 288 return (EINVAL); 289 290 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 291 292 if (!(entry = drm_agp_lookup_entry(dev, (void *)request.handle))) 293 return (EINVAL); 294 if (!entry->bound) 295 return (EINVAL); 296 297 unbind.agpu_pri = 0; 298 unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 299 300 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND, 301 (intptr_t)&unbind, FKIOCTL, kcred, &rval); 302 if (ret) { 303 DRM_ERROR("drm_agp_unbind: AGPIOC_UNBIND failed"); 304 return (EIO); 305 } 306 entry->bound = 0; 307 return (0); 308 } 309 310 /*ARGSUSED*/ 311 int 312 drm_agp_bind(DRM_IOCTL_ARGS) 313 { 314 DRM_DEVICE; 315 drm_agp_binding_t request; 316 drm_agp_mem_t *entry; 317 int start; 318 uint_t key; 319 320 if (!dev->agp || !dev->agp->acquired) 321 return (EINVAL); 322 323 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 324 325 entry = drm_agp_lookup_entry(dev, (void *)request.handle); 326 if (!entry || entry->bound) 327 return (EINVAL); 328 329 key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 330 start = btopr(request.offset); 331 if (drm_agp_bind_memory(key, start, dev)) { 332 DRM_ERROR("drm_agp_bind: failed key=%x, start=0x%x, " 333 "agp_base=0x%lx", key, start, dev->agp->base); 334 return (EIO); 335 } 336 337 entry->bound = dev->agp->base + (start << AGP_PAGE_SHIFT); 338 339 return (0); 340 } 341 342 /*ARGSUSED*/ 343 int 344 drm_agp_free(DRM_IOCTL_ARGS) 345 { 346 DRM_DEVICE; 347 drm_agp_buffer_t request; 348 drm_agp_mem_t *entry; 349 int ret, rval; 350 int agpu_key; 351 352 DRM_COPYFROM_WITH_RETURN(&request, (void *)data, sizeof (request)); 353 if (!dev->agp || !dev->agp->acquired) 354 return (EINVAL); 355 if (!(entry = drm_agp_lookup_entry(dev, (void *)request.handle))) 356 return (EINVAL); 357 if (entry->bound) 358 (void) drm_agp_unbind_memory(request.handle, dev); 359 360 if (entry == dev->agp->memory) 361 dev->agp->memory = entry->next; 362 if (entry->prev) 363 entry->prev->next = entry->next; 364 if (entry->next) 365 entry->next->prev = entry->prev; 366 367 agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 368 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_DEALLOCATE, 369 (intptr_t)agpu_key, FKIOCTL, kcred, &rval); 370 if (ret) { 371 DRM_ERROR("drm_agp_free: AGPIOC_DEALLOCATE failed," 372 "akey=%d, ret=%d", agpu_key, ret); 373 return (EIO); 374 } 375 drm_free(entry, sizeof (*entry), DRM_MEM_AGPLISTS); 376 return (0); 377 } 378 379 /*ARGSUSED*/ 380 drm_agp_head_t * 381 drm_agp_init(drm_device_t *dev) 382 { 383 drm_agp_head_t *agp = NULL; 384 int retval, rval; 385 386 DRM_DEBUG("drm_agp_init\n"); 387 agp = kmem_zalloc(sizeof (drm_agp_head_t), KM_SLEEP); 388 389 retval = ldi_ident_from_dip(dev->dip, &agp->agpgart_li); 390 if (retval != 0) { 391 DRM_ERROR("drm_agp_init: failed to get layerd ident, retval=%d", 392 retval); 393 goto err_1; 394 } 395 396 retval = ldi_open_by_name(AGP_DEVICE, FEXCL, kcred, 397 &agp->agpgart_lh, agp->agpgart_li); 398 if (retval != 0) { 399 DRM_ERROR("drm_agp_init: failed to open %s, retval=%d", 400 AGP_DEVICE, retval); 401 goto err_2; 402 } 403 404 retval = ldi_ioctl(agp->agpgart_lh, AGPIOC_INFO, 405 (intptr_t)&agp->agp_info, FKIOCTL, kcred, &rval); 406 407 if (retval != 0) { 408 DRM_ERROR("drm_agp_init: failed to get agpinfo, retval=%d", 409 retval); 410 goto err_3; 411 } 412 413 return (agp); 414 415 err_3: 416 (void) ldi_close(agp->agpgart_lh, FEXCL, kcred); 417 418 err_2: 419 ldi_ident_release(agp->agpgart_li); 420 421 err_1: 422 kmem_free(agp, sizeof (drm_agp_head_t)); 423 return (NULL); 424 } 425 426 /*ARGSUSED*/ 427 void 428 drm_agp_fini(drm_device_t *dev) 429 { 430 drm_agp_head_t *agp = dev->agp; 431 (void) ldi_close(agp->agpgart_lh, FEXCL, kcred); 432 ldi_ident_release(agp->agpgart_li); 433 kmem_free(agp, sizeof (drm_agp_head_t)); 434 dev->agp = NULL; 435 } 436 437 438 /*ARGSUSED*/ 439 void * 440 drm_agp_allocate_memory(size_t pages, uint32_t type) 441 { 442 return (NULL); 443 } 444 445 /*ARGSUSED*/ 446 int 447 drm_agp_free_memory(void *handle) 448 { 449 return (1); 450 } 451 452 /*ARGSUSED*/ 453 int 454 drm_agp_bind_memory(unsigned int key, uint32_t start, drm_device_t *dev) 455 { 456 agp_bind_t bind; 457 int ret, rval; 458 459 bind.agpb_pgstart = start; 460 bind.agpb_key = key; 461 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_BIND, 462 (intptr_t)&bind, FKIOCTL, kcred, &rval); 463 if (ret) { 464 DRM_DEBUG("drm_agp_bind_meory: AGPIOC_BIND failed"); 465 return (EIO); 466 } 467 return (0); 468 } 469 470 /*ARGSUSED*/ 471 int 472 drm_agp_unbind_memory(unsigned long handle, drm_device_t *dev) 473 { 474 agp_unbind_t unbind; 475 drm_agp_mem_t *entry; 476 int ret, rval; 477 478 if (!dev->agp || !dev->agp->acquired) 479 return (EINVAL); 480 481 entry = drm_agp_lookup_entry(dev, (void *)handle); 482 if (!entry || !entry->bound) 483 return (EINVAL); 484 485 unbind.agpu_pri = 0; 486 unbind.agpu_key = (uintptr_t)entry->handle - DRM_AGP_KEY_OFFSET; 487 488 ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_UNBIND, 489 (intptr_t)&unbind, FKIOCTL, kcred, &rval); 490 if (ret) { 491 DRM_ERROR("drm_agp_unbind: AGPIO_UNBIND failed"); 492 return (EIO); 493 } 494 entry->bound = 0; 495 return (0); 496 } 497