1 /*- 2 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 3 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 * OTHER DEALINGS IN THE SOFTWARE. 24 * 25 * Author: 26 * Rickard E. (Rik) Faith <faith@valinux.com> 27 * Gareth Hughes <gareth@valinux.com> 28 * 29 */ 30 31 /* 32 * Support code for tying the kernel AGP support to DRM drivers and 33 * the DRM's AGP ioctls. 34 */ 35 36 #include "drmP.h" 37 38 struct drm_agp_mem *drm_agp_lookup_entry(struct drm_device *, void *); 39 void drm_agp_remove_entry(struct drm_device *, 40 struct drm_agp_mem *); 41 42 int 43 drm_device_is_agp(struct drm_device *dev) 44 { 45 if (dev->driver.device_is_agp != NULL) { 46 int ret; 47 /* 48 * device_is_agp returns a tristate, 0 = not AGP, 1 = definitely 49 * AGP, 2 = fall back to PCI capability 50 */ 51 ret = (*dev->driver.device_is_agp)(dev); 52 if (ret != DRM_MIGHT_BE_AGP) 53 return (ret); 54 } 55 56 return (pci_get_capability(dev->pa.pa_pc, dev->pa.pa_tag, PCI_CAP_AGP, 57 NULL, NULL)); 58 } 59 60 int 61 drm_device_is_pcie(struct drm_device *dev) 62 { 63 return (pci_get_capability(dev->pa.pa_pc, dev->pa.pa_tag, 64 PCI_CAP_PCIEXPRESS, NULL, NULL)); 65 } 66 67 int 68 drm_agp_info(struct drm_device * dev, struct drm_agp_info *info) 69 { 70 struct agp_info *kern; 71 72 if (dev->agp == NULL || !dev->agp->acquired) 73 return (EINVAL); 74 75 kern = &dev->agp->info; 76 #ifndef DRM_NO_AGP 77 agp_get_info(dev->agp->agpdev, kern); 78 #endif 79 info->agp_version_major = 1; 80 info->agp_version_minor = 0; 81 info->mode = kern->ai_mode; 82 info->aperture_base = kern->ai_aperture_base; 83 info->aperture_size = kern->ai_aperture_size; 84 info->memory_allowed = kern->ai_memory_allowed; 85 info->memory_used = kern->ai_memory_used; 86 info->id_vendor = kern->ai_devid & 0xffff; 87 info->id_device = kern->ai_devid >> 16; 88 89 return (0); 90 } 91 92 int 93 drm_agp_info_ioctl(struct drm_device *dev, void *data, 94 struct drm_file *file_priv) 95 { 96 struct drm_agp_info *info = data; 97 98 return (drm_agp_info(dev, info)); 99 } 100 101 int 102 drm_agp_acquire_ioctl(struct drm_device *dev, void *data, 103 struct drm_file *file_priv) 104 { 105 return (drm_agp_acquire(dev)); 106 } 107 108 int 109 drm_agp_acquire(struct drm_device *dev) 110 { 111 #ifndef DRM_NO_AGP 112 int retcode; 113 114 if (dev->agp == NULL || dev->agp->acquired) 115 return (EINVAL); 116 117 retcode = agp_acquire(dev->agp->agpdev); 118 if (retcode) 119 return (retcode); 120 121 dev->agp->acquired = 1; 122 #endif 123 return (0); 124 } 125 126 int 127 drm_agp_release_ioctl(struct drm_device *dev, void *data, 128 struct drm_file *file_priv) 129 { 130 return (drm_agp_release(dev)); 131 } 132 133 int 134 drm_agp_release(struct drm_device * dev) 135 { 136 #ifndef DRM_NO_AGP 137 if (dev->agp == NULL || !dev->agp->acquired) 138 return (EINVAL); 139 agp_release(dev->agp->agpdev); 140 dev->agp->acquired = 0; 141 #endif 142 return (0); 143 } 144 145 int 146 drm_agp_enable(struct drm_device *dev, drm_agp_mode_t mode) 147 { 148 int retcode = 0; 149 #ifndef DRM_NO_AGP 150 if (dev->agp == NULL || !dev->agp->acquired) 151 return (EINVAL); 152 153 dev->agp->mode = mode.mode; 154 if ((retcode = agp_enable(dev->agp->agpdev, mode.mode)) == 0) 155 dev->agp->enabled = 1; 156 #endif 157 return (retcode); 158 } 159 160 int 161 drm_agp_enable_ioctl(struct drm_device *dev, void *data, 162 struct drm_file *file_priv) 163 { 164 struct drm_agp_mode *mode = data; 165 166 return (drm_agp_enable(dev, *mode)); 167 } 168 169 int 170 drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request) 171 { 172 #ifndef DRM_NO_AGP 173 struct drm_agp_mem *entry; 174 void *handle; 175 struct agp_memory_info info; 176 unsigned long pages; 177 u_int32_t type; 178 179 if (dev->agp == NULL || !dev->agp->acquired) 180 return (EINVAL); 181 182 entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS); 183 if (entry == NULL) 184 return (ENOMEM); 185 186 pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE; 187 type = (u_int32_t)request->type; 188 189 handle = agp_alloc_memory(dev->agp->agpdev, type, 190 pages << AGP_PAGE_SHIFT); 191 if (handle == NULL) { 192 drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); 193 return (ENOMEM); 194 } 195 196 entry->handle = handle; 197 entry->bound = 0; 198 entry->pages = pages; 199 200 agp_memory_info(dev->agp->agpdev, entry->handle, &info); 201 202 request->handle = (unsigned long)entry->handle; 203 request->physical = info.ami_physical; 204 DRM_LOCK(); 205 TAILQ_INSERT_HEAD(&dev->agp->memory, entry, link); 206 DRM_UNLOCK(); 207 #endif 208 209 return (0); 210 } 211 212 int 213 drm_agp_alloc_ioctl(struct drm_device *dev, void *data, 214 struct drm_file *file_priv) 215 { 216 struct drm_agp_buffer *request = data; 217 218 return (drm_agp_alloc(dev, request)); 219 } 220 221 /* 222 * find entry on agp list. Must be called with dev_lock locked. 223 */ 224 struct drm_agp_mem * 225 drm_agp_lookup_entry(struct drm_device *dev, void *handle) 226 { 227 struct drm_agp_mem *entry; 228 229 TAILQ_FOREACH(entry, &dev->agp->memory, link) { 230 if (entry->handle == handle) 231 break; 232 } 233 return (entry); 234 } 235 236 int 237 drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request) 238 { 239 struct drm_agp_mem *entry; 240 int retcode; 241 242 if (dev->agp == NULL || !dev->agp->acquired) 243 return (EINVAL); 244 245 DRM_LOCK(); 246 entry = drm_agp_lookup_entry(dev, (void *)request->handle); 247 if (entry == NULL || !entry->bound) { 248 DRM_UNLOCK(); 249 return (EINVAL); 250 } 251 252 retcode = agp_unbind_memory(dev->agp->agpdev, entry->handle); 253 254 if (retcode == 0) 255 entry->bound = 0; 256 DRM_UNLOCK(); 257 258 return (retcode); 259 } 260 261 int 262 drm_agp_unbind_ioctl(struct drm_device *dev, void *data, 263 struct drm_file *file_priv) 264 { 265 struct drm_agp_binding *request = data; 266 267 return (drm_agp_unbind(dev, request)); 268 } 269 270 int 271 drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request) 272 { 273 struct drm_agp_mem *entry; 274 int retcode, page; 275 276 if (dev->agp == NULL || !dev->agp->acquired) 277 return (EINVAL); 278 279 DRM_DEBUG("agp_bind, page_size=%x\n", PAGE_SIZE); 280 281 DRM_LOCK(); 282 entry = drm_agp_lookup_entry(dev, (void *)request->handle); 283 if (entry == NULL || entry->bound) { 284 DRM_UNLOCK(); 285 return (EINVAL); 286 } 287 288 page = (request->offset + PAGE_SIZE - 1) / PAGE_SIZE; 289 290 retcode = agp_bind_memory(dev->agp->agpdev, entry->handle, 291 page * PAGE_SIZE); 292 if (retcode == 0) 293 entry->bound = dev->agp->base + (page << PAGE_SHIFT); 294 DRM_UNLOCK(); 295 296 return (retcode); 297 } 298 299 int 300 drm_agp_bind_ioctl(struct drm_device *dev, void *data, 301 struct drm_file *file_priv) 302 { 303 struct drm_agp_binding *request = data; 304 305 return (drm_agp_bind(dev, request)); 306 } 307 308 /* 309 * Remove entry from list and free. Call locked. 310 */ 311 void 312 drm_agp_remove_entry(struct drm_device *dev, struct drm_agp_mem *entry) 313 { 314 TAILQ_REMOVE(&dev->agp->memory, entry, link); 315 316 if (entry->bound) 317 agp_unbind_memory(dev->agp->agpdev, entry->handle); 318 agp_free_memory(dev->agp->agpdev, entry->handle); 319 drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); 320 } 321 322 void 323 drm_agp_takedown(struct drm_device *dev) 324 { 325 struct drm_agp_mem *entry; 326 327 if (dev->agp == NULL) 328 return; 329 330 /* 331 * Remove AGP resources, but leave dev->agp intact until 332 * we detach the device 333 */ 334 DRM_LOCK(); 335 while ((entry = TAILQ_FIRST(&dev->agp->memory)) != NULL) 336 drm_agp_remove_entry(dev, entry); 337 DRM_UNLOCK(); 338 339 drm_agp_release(dev); 340 dev->agp->enabled = 0; 341 } 342 343 int 344 drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request) 345 { 346 struct drm_agp_mem *entry; 347 348 if (dev->agp == NULL || !dev->agp->acquired) 349 return (EINVAL); 350 351 DRM_LOCK(); 352 entry = drm_agp_lookup_entry(dev, (void*)request->handle); 353 if (entry == NULL) { 354 DRM_UNLOCK(); 355 return (EINVAL); 356 } 357 358 drm_agp_remove_entry(dev, entry); 359 DRM_UNLOCK(); 360 361 return (0); 362 } 363 364 int 365 drm_agp_free_ioctl(struct drm_device *dev, void *data, 366 struct drm_file *file_priv) 367 { 368 struct drm_agp_buffer *request = data; 369 370 return (drm_agp_free(dev, request)); 371 } 372 373 struct drm_agp_head * 374 drm_agp_init(void) 375 { 376 #ifndef DRM_NO_AGP 377 struct device *agpdev; 378 struct drm_agp_head *head = NULL; 379 int agp_available = 1; 380 381 agpdev = agp_find_device(0); 382 if (agpdev == NULL) 383 agp_available = 0; 384 385 DRM_DEBUG("agp_available = %d\n", agp_available); 386 387 if (agp_available) { 388 head = drm_calloc(1, sizeof(*head), DRM_MEM_AGPLISTS); 389 if (head == NULL) 390 return (NULL); 391 head->agpdev = agpdev; 392 agp_get_info(agpdev, &head->info); 393 head->base = head->info.ai_aperture_base; 394 TAILQ_INIT(&head->memory); 395 } 396 return (head); 397 #else 398 return (NULL); 399 #endif 400 } 401