drm_agpsupport.c revision 182080
1184588Sdfr/*- 2184588Sdfr * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 3184588Sdfr * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 4184588Sdfr * All Rights Reserved. 5184588Sdfr * 6184588Sdfr * Permission is hereby granted, free of charge, to any person obtaining a 7184588Sdfr * copy of this software and associated documentation files (the "Software"), 8184588Sdfr * to deal in the Software without restriction, including without limitation 9184588Sdfr * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10184588Sdfr * and/or sell copies of the Software, and to permit persons to whom the 11184588Sdfr * Software is furnished to do so, subject to the following conditions: 12184588Sdfr * 13184588Sdfr * The above copyright notice and this permission notice (including the next 14184588Sdfr * paragraph) shall be included in all copies or substantial portions of the 15184588Sdfr * Software. 16184588Sdfr * 17184588Sdfr * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18184588Sdfr * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19184588Sdfr * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20184588Sdfr * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21184588Sdfr * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22184588Sdfr * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23184588Sdfr * OTHER DEALINGS IN THE SOFTWARE. 24184588Sdfr * 25184588Sdfr * Author: 26184588Sdfr * Rickard E. (Rik) Faith <faith@valinux.com> 27184588Sdfr * Gareth Hughes <gareth@valinux.com> 28184588Sdfr * 29184588Sdfr */ 30184588Sdfr 31184588Sdfr#include <sys/cdefs.h> 32184588Sdfr__FBSDID("$FreeBSD: head/sys/dev/drm/drm_agpsupport.c 182080 2008-08-23 20:59:12Z rnoland $"); 33184588Sdfr 34245018Srmacklem/** @file drm_agpsupport.c 35184588Sdfr * Support code for tying the kernel AGP support to DRM drivers and 36245018Srmacklem * the DRM's AGP ioctls. 37184588Sdfr */ 38184588Sdfr 39184588Sdfr#include "dev/drm/drmP.h" 40184588Sdfr 41184588Sdfr#ifdef __FreeBSD__ 42184588Sdfr#if __FreeBSD_version >= 800004 43184588Sdfr#include <dev/agp/agpreg.h> 44184588Sdfr#else /* __FreeBSD_version >= 800004 */ 45184588Sdfr#include <pci/agpreg.h> 46184588Sdfr#endif /* __FreeBSD_version >= 800004 */ 47184588Sdfr#include <dev/pci/pcireg.h> 48184588Sdfr#endif 49184588Sdfr 50245018Srmacklem/* Returns 1 if AGP or 0 if not. */ 51184588Sdfrstatic int 52245018Srmacklemdrm_device_find_capability(struct drm_device *dev, int cap) 53245018Srmacklem{ 54184588Sdfr#ifdef __FreeBSD__ 55184588Sdfr#if __FreeBSD_version >= 602102 56184588Sdfr 57184588Sdfr return (pci_find_extcap(dev->device, cap, NULL) == 0); 58184588Sdfr#else 59184588Sdfr /* Code taken from agp.c. IWBNI that was a public interface. */ 60184588Sdfr u_int32_t status; 61245018Srmacklem u_int8_t ptr, next; 62245018Srmacklem 63245018Srmacklem /* 64245018Srmacklem * Check the CAP_LIST bit of the PCI status register first. 65245018Srmacklem */ 66184588Sdfr status = pci_read_config(dev->device, PCIR_STATUS, 2); 67184588Sdfr if (!(status & 0x10)) 68184588Sdfr return 0; 69184588Sdfr 70184588Sdfr /* 71184588Sdfr * Traverse the capabilities list. 72184588Sdfr */ 73184588Sdfr for (ptr = pci_read_config(dev->device, AGP_CAPPTR, 1); 74184588Sdfr ptr != 0; 75184588Sdfr ptr = next) { 76184588Sdfr u_int32_t capid = pci_read_config(dev->device, ptr, 4); 77184588Sdfr next = AGP_CAPID_GET_NEXT_PTR(capid); 78184588Sdfr 79184588Sdfr /* 80184588Sdfr * If this capability entry ID is cap, then we are done. 81184588Sdfr */ 82 if (AGP_CAPID_GET_CAP_ID(capid) == cap) 83 return 1; 84 } 85 86 return 0; 87#endif 88#else 89 /* XXX: fill me in for non-FreeBSD */ 90 return 1; 91#endif 92} 93 94int drm_device_is_agp(struct drm_device *dev) 95{ 96 if (dev->driver.device_is_agp != NULL) { 97 int ret; 98 99 /* device_is_agp returns a tristate, 0 = not AGP, 1 = definitely 100 * AGP, 2 = fall back to PCI capability 101 */ 102 ret = (*dev->driver.device_is_agp)(dev); 103 if (ret != DRM_MIGHT_BE_AGP) 104 return ret; 105 } 106 107 return (drm_device_find_capability(dev, PCIY_AGP)); 108} 109 110int drm_device_is_pcie(struct drm_device *dev) 111{ 112 return (drm_device_find_capability(dev, PCIY_EXPRESS)); 113} 114 115int drm_agp_info(struct drm_device * dev, drm_agp_info_t *info) 116{ 117 struct agp_info *kern; 118 119 if (!dev->agp || !dev->agp->acquired) 120 return EINVAL; 121 122 kern = &dev->agp->info; 123 agp_get_info(dev->agp->agpdev, kern); 124 info->agp_version_major = 1; 125 info->agp_version_minor = 0; 126 info->mode = kern->ai_mode; 127 info->aperture_base = kern->ai_aperture_base; 128 info->aperture_size = kern->ai_aperture_size; 129 info->memory_allowed = kern->ai_memory_allowed; 130 info->memory_used = kern->ai_memory_used; 131 info->id_vendor = kern->ai_devid & 0xffff; 132 info->id_device = kern->ai_devid >> 16; 133 134 return 0; 135} 136 137int drm_agp_info_ioctl(struct drm_device *dev, void *data, 138 struct drm_file *file_priv) 139{ 140 int err; 141 drm_agp_info_t info; 142 143 err = drm_agp_info(dev, &info); 144 if (err != 0) 145 return err; 146 147 *(drm_agp_info_t *) data = info; 148 return 0; 149} 150 151int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, 152 struct drm_file *file_priv) 153{ 154 155 return drm_agp_acquire(dev); 156} 157 158int drm_agp_acquire(struct drm_device *dev) 159{ 160 int retcode; 161 162 if (!dev->agp || dev->agp->acquired) 163 return EINVAL; 164 165 retcode = agp_acquire(dev->agp->agpdev); 166 if (retcode) 167 return retcode; 168 169 dev->agp->acquired = 1; 170 return 0; 171} 172 173int drm_agp_release_ioctl(struct drm_device *dev, void *data, 174 struct drm_file *file_priv) 175{ 176 177 return drm_agp_release(dev); 178} 179 180int drm_agp_release(struct drm_device * dev) 181{ 182 if (!dev->agp || !dev->agp->acquired) 183 return EINVAL; 184 agp_release(dev->agp->agpdev); 185 dev->agp->acquired = 0; 186 return 0; 187} 188 189int drm_agp_enable(struct drm_device *dev, drm_agp_mode_t mode) 190{ 191 192 if (!dev->agp || !dev->agp->acquired) 193 return EINVAL; 194 195 dev->agp->mode = mode.mode; 196 agp_enable(dev->agp->agpdev, mode.mode); 197 dev->agp->enabled = 1; 198 return 0; 199} 200 201int drm_agp_enable_ioctl(struct drm_device *dev, void *data, 202 struct drm_file *file_priv) 203{ 204 drm_agp_mode_t mode; 205 206 mode = *(drm_agp_mode_t *) data; 207 208 return drm_agp_enable(dev, mode); 209} 210 211int drm_agp_alloc(struct drm_device *dev, drm_agp_buffer_t *request) 212{ 213 drm_agp_mem_t *entry; 214 void *handle; 215 unsigned long pages; 216 u_int32_t type; 217 struct agp_memory_info info; 218 219 if (!dev->agp || !dev->agp->acquired) 220 return EINVAL; 221 222 entry = malloc(sizeof(*entry), M_DRM, M_NOWAIT | M_ZERO); 223 if (entry == NULL) 224 return ENOMEM; 225 226 pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE; 227 type = (u_int32_t) request->type; 228 229 DRM_UNLOCK(); 230 handle = drm_agp_allocate_memory(pages, type); 231 DRM_LOCK(); 232 if (handle == NULL) { 233 free(entry, M_DRM); 234 return ENOMEM; 235 } 236 237 entry->handle = handle; 238 entry->bound = 0; 239 entry->pages = pages; 240 entry->prev = NULL; 241 entry->next = dev->agp->memory; 242 if (dev->agp->memory) 243 dev->agp->memory->prev = entry; 244 dev->agp->memory = entry; 245 246 agp_memory_info(dev->agp->agpdev, entry->handle, &info); 247 248 request->handle = (unsigned long) entry->handle; 249 request->physical = info.ami_physical; 250 251 return 0; 252} 253 254int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, 255 struct drm_file *file_priv) 256{ 257 drm_agp_buffer_t request; 258 int retcode; 259 260 request = *(drm_agp_buffer_t *) data; 261 262 DRM_LOCK(); 263 retcode = drm_agp_alloc(dev, &request); 264 DRM_UNLOCK(); 265 266 *(drm_agp_buffer_t *) data = request; 267 268 return retcode; 269} 270 271static drm_agp_mem_t * drm_agp_lookup_entry(struct drm_device *dev, 272 void *handle) 273{ 274 drm_agp_mem_t *entry; 275 276 for (entry = dev->agp->memory; entry; entry = entry->next) { 277 if (entry->handle == handle) return entry; 278 } 279 return NULL; 280} 281 282int drm_agp_unbind(struct drm_device *dev, drm_agp_binding_t *request) 283{ 284 drm_agp_mem_t *entry; 285 int retcode; 286 287 if (!dev->agp || !dev->agp->acquired) 288 return EINVAL; 289 290 entry = drm_agp_lookup_entry(dev, (void *)request->handle); 291 if (entry == NULL || !entry->bound) 292 return EINVAL; 293 294 DRM_UNLOCK(); 295 retcode = drm_agp_unbind_memory(entry->handle); 296 DRM_LOCK(); 297 298 if (retcode == 0) 299 entry->bound = 0; 300 301 return retcode; 302} 303 304int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, 305 struct drm_file *file_priv) 306{ 307 drm_agp_binding_t request; 308 int retcode; 309 310 request = *(drm_agp_binding_t *) data; 311 312 DRM_LOCK(); 313 retcode = drm_agp_unbind(dev, &request); 314 DRM_UNLOCK(); 315 316 return retcode; 317} 318 319int drm_agp_bind(struct drm_device *dev, drm_agp_binding_t *request) 320{ 321 drm_agp_mem_t *entry; 322 int retcode; 323 int page; 324 325 if (!dev->agp || !dev->agp->acquired) 326 return EINVAL; 327 328 DRM_DEBUG("agp_bind, page_size=%x\n", PAGE_SIZE); 329 330 entry = drm_agp_lookup_entry(dev, (void *)request->handle); 331 if (entry == NULL || entry->bound) 332 return EINVAL; 333 334 page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE; 335 336 DRM_UNLOCK(); 337 retcode = drm_agp_bind_memory(entry->handle, page); 338 DRM_LOCK(); 339 if (retcode == 0) 340 entry->bound = dev->agp->base + (page << PAGE_SHIFT); 341 342 return retcode; 343} 344 345int drm_agp_bind_ioctl(struct drm_device *dev, void *data, 346 struct drm_file *file_priv) 347{ 348 drm_agp_binding_t request; 349 int retcode; 350 351 request = *(drm_agp_binding_t *) data; 352 353 DRM_LOCK(); 354 retcode = drm_agp_bind(dev, &request); 355 DRM_UNLOCK(); 356 357 return retcode; 358} 359 360int drm_agp_free(struct drm_device *dev, drm_agp_buffer_t *request) 361{ 362 drm_agp_mem_t *entry; 363 364 if (!dev->agp || !dev->agp->acquired) 365 return EINVAL; 366 367 entry = drm_agp_lookup_entry(dev, (void*)request->handle); 368 if (entry == NULL) 369 return EINVAL; 370 371 if (entry->prev) 372 entry->prev->next = entry->next; 373 else 374 dev->agp->memory = entry->next; 375 if (entry->next) 376 entry->next->prev = entry->prev; 377 378 DRM_UNLOCK(); 379 if (entry->bound) 380 drm_agp_unbind_memory(entry->handle); 381 drm_agp_free_memory(entry->handle); 382 DRM_LOCK(); 383 384 free(entry, M_DRM); 385 386 return 0; 387 388} 389 390int drm_agp_free_ioctl(struct drm_device *dev, void *data, 391 struct drm_file *file_priv) 392{ 393 drm_agp_buffer_t request; 394 int retcode; 395 396 request = *(drm_agp_buffer_t *) data; 397 398 DRM_LOCK(); 399 retcode = drm_agp_free(dev, &request); 400 DRM_UNLOCK(); 401 402 return retcode; 403} 404 405drm_agp_head_t *drm_agp_init(void) 406{ 407 device_t agpdev; 408 drm_agp_head_t *head = NULL; 409 int agp_available = 1; 410 411 agpdev = DRM_AGP_FIND_DEVICE(); 412 if (!agpdev) 413 agp_available = 0; 414 415 DRM_DEBUG("agp_available = %d\n", agp_available); 416 417 if (agp_available) { 418 head = malloc(sizeof(*head), M_DRM, M_NOWAIT | M_ZERO); 419 if (head == NULL) 420 return NULL; 421 head->agpdev = agpdev; 422 agp_get_info(agpdev, &head->info); 423 head->base = head->info.ai_aperture_base; 424 head->memory = NULL; 425 DRM_INFO("AGP at 0x%08lx %dMB\n", 426 (long)head->info.ai_aperture_base, 427 (int)(head->info.ai_aperture_size >> 20)); 428 } 429 return head; 430} 431 432void *drm_agp_allocate_memory(size_t pages, u32 type) 433{ 434 device_t agpdev; 435 436 agpdev = DRM_AGP_FIND_DEVICE(); 437 if (!agpdev) 438 return NULL; 439 440 return agp_alloc_memory(agpdev, type, pages << AGP_PAGE_SHIFT); 441} 442 443int drm_agp_free_memory(void *handle) 444{ 445 device_t agpdev; 446 447 agpdev = DRM_AGP_FIND_DEVICE(); 448 if (!agpdev || !handle) 449 return 0; 450 451 agp_free_memory(agpdev, handle); 452 return 1; 453} 454 455int drm_agp_bind_memory(void *handle, off_t start) 456{ 457 device_t agpdev; 458 459 agpdev = DRM_AGP_FIND_DEVICE(); 460 if (!agpdev || !handle) 461 return EINVAL; 462 463 return agp_bind_memory(agpdev, handle, start * PAGE_SIZE); 464} 465 466int drm_agp_unbind_memory(void *handle) 467{ 468 device_t agpdev; 469 470 agpdev = DRM_AGP_FIND_DEVICE(); 471 if (!agpdev || !handle) 472 return EINVAL; 473 474 return agp_unbind_memory(agpdev, handle); 475} 476