1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * AGPGART driver backend routines. 3*1da177e4SLinus Torvalds * Copyright (C) 2004 Silicon Graphics, Inc. 4*1da177e4SLinus Torvalds * Copyright (C) 2002-2003 Dave Jones. 5*1da177e4SLinus Torvalds * Copyright (C) 1999 Jeff Hartmann. 6*1da177e4SLinus Torvalds * Copyright (C) 1999 Precision Insight, Inc. 7*1da177e4SLinus Torvalds * Copyright (C) 1999 Xi Graphics, Inc. 8*1da177e4SLinus Torvalds * 9*1da177e4SLinus Torvalds * Permission is hereby granted, free of charge, to any person obtaining a 10*1da177e4SLinus Torvalds * copy of this software and associated documentation files (the "Software"), 11*1da177e4SLinus Torvalds * to deal in the Software without restriction, including without limitation 12*1da177e4SLinus Torvalds * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13*1da177e4SLinus Torvalds * and/or sell copies of the Software, and to permit persons to whom the 14*1da177e4SLinus Torvalds * Software is furnished to do so, subject to the following conditions: 15*1da177e4SLinus Torvalds * 16*1da177e4SLinus Torvalds * The above copyright notice and this permission notice shall be included 17*1da177e4SLinus Torvalds * in all copies or substantial portions of the Software. 18*1da177e4SLinus Torvalds * 19*1da177e4SLinus Torvalds * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20*1da177e4SLinus Torvalds * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21*1da177e4SLinus Torvalds * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22*1da177e4SLinus Torvalds * JEFF HARTMANN, DAVE JONES, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 23*1da177e4SLinus Torvalds * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24*1da177e4SLinus Torvalds * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 25*1da177e4SLinus Torvalds * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26*1da177e4SLinus Torvalds * 27*1da177e4SLinus Torvalds * TODO: 28*1da177e4SLinus Torvalds * - Allocate more than order 0 pages to avoid too much linear map splitting. 29*1da177e4SLinus Torvalds */ 30*1da177e4SLinus Torvalds #include <linux/module.h> 31*1da177e4SLinus Torvalds #include <linux/pci.h> 32*1da177e4SLinus Torvalds #include <linux/init.h> 33*1da177e4SLinus Torvalds #include <linux/pagemap.h> 34*1da177e4SLinus Torvalds #include <linux/miscdevice.h> 35*1da177e4SLinus Torvalds #include <linux/pm.h> 36*1da177e4SLinus Torvalds #include <linux/agp_backend.h> 37*1da177e4SLinus Torvalds #include <linux/agpgart.h> 38*1da177e4SLinus Torvalds #include <linux/vmalloc.h> 39*1da177e4SLinus Torvalds #include <asm/io.h> 40*1da177e4SLinus Torvalds #include "agp.h" 41*1da177e4SLinus Torvalds 42*1da177e4SLinus Torvalds /* Due to XFree86 brain-damage, we can't go to 1.0 until they 43*1da177e4SLinus Torvalds * fix some real stupidity. It's only by chance we can bump 44*1da177e4SLinus Torvalds * past 0.99 at all due to some boolean logic error. */ 45*1da177e4SLinus Torvalds #define AGPGART_VERSION_MAJOR 0 46*1da177e4SLinus Torvalds #define AGPGART_VERSION_MINOR 101 47*1da177e4SLinus Torvalds static struct agp_version agp_current_version = 48*1da177e4SLinus Torvalds { 49*1da177e4SLinus Torvalds .major = AGPGART_VERSION_MAJOR, 50*1da177e4SLinus Torvalds .minor = AGPGART_VERSION_MINOR, 51*1da177e4SLinus Torvalds }; 52*1da177e4SLinus Torvalds 53*1da177e4SLinus Torvalds struct agp_bridge_data *(*agp_find_bridge)(struct pci_dev *) = 54*1da177e4SLinus Torvalds &agp_generic_find_bridge; 55*1da177e4SLinus Torvalds 56*1da177e4SLinus Torvalds struct agp_bridge_data *agp_bridge; 57*1da177e4SLinus Torvalds LIST_HEAD(agp_bridges); 58*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_bridge); 59*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_bridges); 60*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_find_bridge); 61*1da177e4SLinus Torvalds 62*1da177e4SLinus Torvalds /** 63*1da177e4SLinus Torvalds * agp_backend_acquire - attempt to acquire an agp backend. 64*1da177e4SLinus Torvalds * 65*1da177e4SLinus Torvalds */ 66*1da177e4SLinus Torvalds struct agp_bridge_data *agp_backend_acquire(struct pci_dev *pdev) 67*1da177e4SLinus Torvalds { 68*1da177e4SLinus Torvalds struct agp_bridge_data *bridge; 69*1da177e4SLinus Torvalds 70*1da177e4SLinus Torvalds bridge = agp_find_bridge(pdev); 71*1da177e4SLinus Torvalds 72*1da177e4SLinus Torvalds if (!bridge) 73*1da177e4SLinus Torvalds return NULL; 74*1da177e4SLinus Torvalds 75*1da177e4SLinus Torvalds if (atomic_read(&bridge->agp_in_use)) 76*1da177e4SLinus Torvalds return NULL; 77*1da177e4SLinus Torvalds atomic_inc(&bridge->agp_in_use); 78*1da177e4SLinus Torvalds return bridge; 79*1da177e4SLinus Torvalds } 80*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_backend_acquire); 81*1da177e4SLinus Torvalds 82*1da177e4SLinus Torvalds 83*1da177e4SLinus Torvalds /** 84*1da177e4SLinus Torvalds * agp_backend_release - release the lock on the agp backend. 85*1da177e4SLinus Torvalds * 86*1da177e4SLinus Torvalds * The caller must insure that the graphics aperture translation table 87*1da177e4SLinus Torvalds * is read for use by another entity. 88*1da177e4SLinus Torvalds * 89*1da177e4SLinus Torvalds * (Ensure that all memory it bound is unbound.) 90*1da177e4SLinus Torvalds */ 91*1da177e4SLinus Torvalds void agp_backend_release(struct agp_bridge_data *bridge) 92*1da177e4SLinus Torvalds { 93*1da177e4SLinus Torvalds 94*1da177e4SLinus Torvalds if (bridge) 95*1da177e4SLinus Torvalds atomic_dec(&bridge->agp_in_use); 96*1da177e4SLinus Torvalds } 97*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_backend_release); 98*1da177e4SLinus Torvalds 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds struct { int mem, agp; } maxes_table[] = { 101*1da177e4SLinus Torvalds {0, 0}, 102*1da177e4SLinus Torvalds {32, 4}, 103*1da177e4SLinus Torvalds {64, 28}, 104*1da177e4SLinus Torvalds {128, 96}, 105*1da177e4SLinus Torvalds {256, 204}, 106*1da177e4SLinus Torvalds {512, 440}, 107*1da177e4SLinus Torvalds {1024, 942}, 108*1da177e4SLinus Torvalds {2048, 1920}, 109*1da177e4SLinus Torvalds {4096, 3932} 110*1da177e4SLinus Torvalds }; 111*1da177e4SLinus Torvalds 112*1da177e4SLinus Torvalds static int agp_find_max(void) 113*1da177e4SLinus Torvalds { 114*1da177e4SLinus Torvalds long memory, index, result; 115*1da177e4SLinus Torvalds 116*1da177e4SLinus Torvalds #if PAGE_SHIFT < 20 117*1da177e4SLinus Torvalds memory = num_physpages >> (20 - PAGE_SHIFT); 118*1da177e4SLinus Torvalds #else 119*1da177e4SLinus Torvalds memory = num_physpages << (PAGE_SHIFT - 20); 120*1da177e4SLinus Torvalds #endif 121*1da177e4SLinus Torvalds index = 1; 122*1da177e4SLinus Torvalds 123*1da177e4SLinus Torvalds while ((memory > maxes_table[index].mem) && (index < 8)) 124*1da177e4SLinus Torvalds index++; 125*1da177e4SLinus Torvalds 126*1da177e4SLinus Torvalds result = maxes_table[index - 1].agp + 127*1da177e4SLinus Torvalds ( (memory - maxes_table[index - 1].mem) * 128*1da177e4SLinus Torvalds (maxes_table[index].agp - maxes_table[index - 1].agp)) / 129*1da177e4SLinus Torvalds (maxes_table[index].mem - maxes_table[index - 1].mem); 130*1da177e4SLinus Torvalds 131*1da177e4SLinus Torvalds result = result << (20 - PAGE_SHIFT); 132*1da177e4SLinus Torvalds return result; 133*1da177e4SLinus Torvalds } 134*1da177e4SLinus Torvalds 135*1da177e4SLinus Torvalds 136*1da177e4SLinus Torvalds static int agp_backend_initialize(struct agp_bridge_data *bridge) 137*1da177e4SLinus Torvalds { 138*1da177e4SLinus Torvalds int size_value, rc, got_gatt=0, got_keylist=0; 139*1da177e4SLinus Torvalds 140*1da177e4SLinus Torvalds bridge->max_memory_agp = agp_find_max(); 141*1da177e4SLinus Torvalds bridge->version = &agp_current_version; 142*1da177e4SLinus Torvalds 143*1da177e4SLinus Torvalds if (bridge->driver->needs_scratch_page) { 144*1da177e4SLinus Torvalds void *addr = bridge->driver->agp_alloc_page(bridge); 145*1da177e4SLinus Torvalds 146*1da177e4SLinus Torvalds if (!addr) { 147*1da177e4SLinus Torvalds printk(KERN_ERR PFX "unable to get memory for scratch page.\n"); 148*1da177e4SLinus Torvalds return -ENOMEM; 149*1da177e4SLinus Torvalds } 150*1da177e4SLinus Torvalds 151*1da177e4SLinus Torvalds bridge->scratch_page_real = virt_to_phys(addr); 152*1da177e4SLinus Torvalds bridge->scratch_page = 153*1da177e4SLinus Torvalds bridge->driver->mask_memory(bridge, bridge->scratch_page_real, 0); 154*1da177e4SLinus Torvalds } 155*1da177e4SLinus Torvalds 156*1da177e4SLinus Torvalds size_value = bridge->driver->fetch_size(); 157*1da177e4SLinus Torvalds if (size_value == 0) { 158*1da177e4SLinus Torvalds printk(KERN_ERR PFX "unable to determine aperture size.\n"); 159*1da177e4SLinus Torvalds rc = -EINVAL; 160*1da177e4SLinus Torvalds goto err_out; 161*1da177e4SLinus Torvalds } 162*1da177e4SLinus Torvalds if (bridge->driver->create_gatt_table(bridge)) { 163*1da177e4SLinus Torvalds printk(KERN_ERR PFX 164*1da177e4SLinus Torvalds "unable to get memory for graphics translation table.\n"); 165*1da177e4SLinus Torvalds rc = -ENOMEM; 166*1da177e4SLinus Torvalds goto err_out; 167*1da177e4SLinus Torvalds } 168*1da177e4SLinus Torvalds got_gatt = 1; 169*1da177e4SLinus Torvalds 170*1da177e4SLinus Torvalds bridge->key_list = vmalloc(PAGE_SIZE * 4); 171*1da177e4SLinus Torvalds if (bridge->key_list == NULL) { 172*1da177e4SLinus Torvalds printk(KERN_ERR PFX "error allocating memory for key lists.\n"); 173*1da177e4SLinus Torvalds rc = -ENOMEM; 174*1da177e4SLinus Torvalds goto err_out; 175*1da177e4SLinus Torvalds } 176*1da177e4SLinus Torvalds got_keylist = 1; 177*1da177e4SLinus Torvalds 178*1da177e4SLinus Torvalds /* FIXME vmalloc'd memory not guaranteed contiguous */ 179*1da177e4SLinus Torvalds memset(bridge->key_list, 0, PAGE_SIZE * 4); 180*1da177e4SLinus Torvalds 181*1da177e4SLinus Torvalds if (bridge->driver->configure()) { 182*1da177e4SLinus Torvalds printk(KERN_ERR PFX "error configuring host chipset.\n"); 183*1da177e4SLinus Torvalds rc = -EINVAL; 184*1da177e4SLinus Torvalds goto err_out; 185*1da177e4SLinus Torvalds } 186*1da177e4SLinus Torvalds 187*1da177e4SLinus Torvalds return 0; 188*1da177e4SLinus Torvalds 189*1da177e4SLinus Torvalds err_out: 190*1da177e4SLinus Torvalds if (bridge->driver->needs_scratch_page) 191*1da177e4SLinus Torvalds bridge->driver->agp_destroy_page( 192*1da177e4SLinus Torvalds phys_to_virt(bridge->scratch_page_real)); 193*1da177e4SLinus Torvalds if (got_gatt) 194*1da177e4SLinus Torvalds bridge->driver->free_gatt_table(bridge); 195*1da177e4SLinus Torvalds if (got_keylist) { 196*1da177e4SLinus Torvalds vfree(bridge->key_list); 197*1da177e4SLinus Torvalds bridge->key_list = NULL; 198*1da177e4SLinus Torvalds } 199*1da177e4SLinus Torvalds return rc; 200*1da177e4SLinus Torvalds } 201*1da177e4SLinus Torvalds 202*1da177e4SLinus Torvalds /* cannot be __exit b/c as it could be called from __init code */ 203*1da177e4SLinus Torvalds static void agp_backend_cleanup(struct agp_bridge_data *bridge) 204*1da177e4SLinus Torvalds { 205*1da177e4SLinus Torvalds if (bridge->driver->cleanup) 206*1da177e4SLinus Torvalds bridge->driver->cleanup(); 207*1da177e4SLinus Torvalds if (bridge->driver->free_gatt_table) 208*1da177e4SLinus Torvalds bridge->driver->free_gatt_table(bridge); 209*1da177e4SLinus Torvalds if (bridge->key_list) { 210*1da177e4SLinus Torvalds vfree(bridge->key_list); 211*1da177e4SLinus Torvalds bridge->key_list = NULL; 212*1da177e4SLinus Torvalds } 213*1da177e4SLinus Torvalds 214*1da177e4SLinus Torvalds if (bridge->driver->agp_destroy_page && 215*1da177e4SLinus Torvalds bridge->driver->needs_scratch_page) 216*1da177e4SLinus Torvalds bridge->driver->agp_destroy_page( 217*1da177e4SLinus Torvalds phys_to_virt(bridge->scratch_page_real)); 218*1da177e4SLinus Torvalds } 219*1da177e4SLinus Torvalds 220*1da177e4SLinus Torvalds /* When we remove the global variable agp_bridge from all drivers 221*1da177e4SLinus Torvalds * then agp_alloc_bridge and agp_generic_find_bridge need to be updated 222*1da177e4SLinus Torvalds */ 223*1da177e4SLinus Torvalds 224*1da177e4SLinus Torvalds struct agp_bridge_data *agp_alloc_bridge(void) 225*1da177e4SLinus Torvalds { 226*1da177e4SLinus Torvalds struct agp_bridge_data *bridge = kmalloc(sizeof(*bridge), GFP_KERNEL); 227*1da177e4SLinus Torvalds 228*1da177e4SLinus Torvalds if (!bridge) 229*1da177e4SLinus Torvalds return NULL; 230*1da177e4SLinus Torvalds 231*1da177e4SLinus Torvalds memset(bridge, 0, sizeof(*bridge)); 232*1da177e4SLinus Torvalds atomic_set(&bridge->agp_in_use, 0); 233*1da177e4SLinus Torvalds atomic_set(&bridge->current_memory_agp, 0); 234*1da177e4SLinus Torvalds 235*1da177e4SLinus Torvalds if (list_empty(&agp_bridges)) 236*1da177e4SLinus Torvalds agp_bridge = bridge; 237*1da177e4SLinus Torvalds 238*1da177e4SLinus Torvalds return bridge; 239*1da177e4SLinus Torvalds } 240*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_alloc_bridge); 241*1da177e4SLinus Torvalds 242*1da177e4SLinus Torvalds 243*1da177e4SLinus Torvalds void agp_put_bridge(struct agp_bridge_data *bridge) 244*1da177e4SLinus Torvalds { 245*1da177e4SLinus Torvalds kfree(bridge); 246*1da177e4SLinus Torvalds 247*1da177e4SLinus Torvalds if (list_empty(&agp_bridges)) 248*1da177e4SLinus Torvalds agp_bridge = NULL; 249*1da177e4SLinus Torvalds } 250*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_put_bridge); 251*1da177e4SLinus Torvalds 252*1da177e4SLinus Torvalds 253*1da177e4SLinus Torvalds int agp_add_bridge(struct agp_bridge_data *bridge) 254*1da177e4SLinus Torvalds { 255*1da177e4SLinus Torvalds int error; 256*1da177e4SLinus Torvalds 257*1da177e4SLinus Torvalds if (agp_off) 258*1da177e4SLinus Torvalds return -ENODEV; 259*1da177e4SLinus Torvalds 260*1da177e4SLinus Torvalds if (!bridge->dev) { 261*1da177e4SLinus Torvalds printk (KERN_DEBUG PFX "Erk, registering with no pci_dev!\n"); 262*1da177e4SLinus Torvalds return -EINVAL; 263*1da177e4SLinus Torvalds } 264*1da177e4SLinus Torvalds 265*1da177e4SLinus Torvalds /* Grab reference on the chipset driver. */ 266*1da177e4SLinus Torvalds if (!try_module_get(bridge->driver->owner)) { 267*1da177e4SLinus Torvalds printk (KERN_INFO PFX "Couldn't lock chipset driver.\n"); 268*1da177e4SLinus Torvalds return -EINVAL; 269*1da177e4SLinus Torvalds } 270*1da177e4SLinus Torvalds 271*1da177e4SLinus Torvalds error = agp_backend_initialize(bridge); 272*1da177e4SLinus Torvalds if (error) { 273*1da177e4SLinus Torvalds printk (KERN_INFO PFX "agp_backend_initialize() failed.\n"); 274*1da177e4SLinus Torvalds goto err_out; 275*1da177e4SLinus Torvalds } 276*1da177e4SLinus Torvalds 277*1da177e4SLinus Torvalds if (list_empty(&agp_bridges)) { 278*1da177e4SLinus Torvalds error = agp_frontend_initialize(); 279*1da177e4SLinus Torvalds if (error) { 280*1da177e4SLinus Torvalds printk (KERN_INFO PFX "agp_frontend_initialize() failed.\n"); 281*1da177e4SLinus Torvalds goto frontend_err; 282*1da177e4SLinus Torvalds } 283*1da177e4SLinus Torvalds 284*1da177e4SLinus Torvalds printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n", 285*1da177e4SLinus Torvalds bridge->driver->fetch_size(), bridge->gart_bus_addr); 286*1da177e4SLinus Torvalds 287*1da177e4SLinus Torvalds } 288*1da177e4SLinus Torvalds 289*1da177e4SLinus Torvalds list_add(&bridge->list, &agp_bridges); 290*1da177e4SLinus Torvalds return 0; 291*1da177e4SLinus Torvalds 292*1da177e4SLinus Torvalds frontend_err: 293*1da177e4SLinus Torvalds agp_backend_cleanup(bridge); 294*1da177e4SLinus Torvalds err_out: 295*1da177e4SLinus Torvalds module_put(bridge->driver->owner); 296*1da177e4SLinus Torvalds agp_put_bridge(bridge); 297*1da177e4SLinus Torvalds return error; 298*1da177e4SLinus Torvalds } 299*1da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_add_bridge); 300*1da177e4SLinus Torvalds 301*1da177e4SLinus Torvalds 302*1da177e4SLinus Torvalds void agp_remove_bridge(struct agp_bridge_data *bridge) 303*1da177e4SLinus Torvalds { 304*1da177e4SLinus Torvalds agp_backend_cleanup(bridge); 305*1da177e4SLinus Torvalds list_del(&bridge->list); 306*1da177e4SLinus Torvalds if (list_empty(&agp_bridges)) 307*1da177e4SLinus Torvalds agp_frontend_cleanup(); 308*1da177e4SLinus Torvalds module_put(bridge->driver->owner); 309*1da177e4SLinus Torvalds } 310*1da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(agp_remove_bridge); 311*1da177e4SLinus Torvalds 312*1da177e4SLinus Torvalds int agp_off; 313*1da177e4SLinus Torvalds int agp_try_unsupported_boot; 314*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_off); 315*1da177e4SLinus Torvalds EXPORT_SYMBOL(agp_try_unsupported_boot); 316*1da177e4SLinus Torvalds 317*1da177e4SLinus Torvalds static int __init agp_init(void) 318*1da177e4SLinus Torvalds { 319*1da177e4SLinus Torvalds if (!agp_off) 320*1da177e4SLinus Torvalds printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Dave Jones\n", 321*1da177e4SLinus Torvalds AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); 322*1da177e4SLinus Torvalds return 0; 323*1da177e4SLinus Torvalds } 324*1da177e4SLinus Torvalds 325*1da177e4SLinus Torvalds void __exit agp_exit(void) 326*1da177e4SLinus Torvalds { 327*1da177e4SLinus Torvalds } 328*1da177e4SLinus Torvalds 329*1da177e4SLinus Torvalds #ifndef MODULE 330*1da177e4SLinus Torvalds static __init int agp_setup(char *s) 331*1da177e4SLinus Torvalds { 332*1da177e4SLinus Torvalds if (!strcmp(s,"off")) 333*1da177e4SLinus Torvalds agp_off = 1; 334*1da177e4SLinus Torvalds if (!strcmp(s,"try_unsupported")) 335*1da177e4SLinus Torvalds agp_try_unsupported_boot = 1; 336*1da177e4SLinus Torvalds return 1; 337*1da177e4SLinus Torvalds } 338*1da177e4SLinus Torvalds __setup("agp=", agp_setup); 339*1da177e4SLinus Torvalds #endif 340*1da177e4SLinus Torvalds 341*1da177e4SLinus Torvalds MODULE_AUTHOR("Dave Jones <davej@codemonkey.org.uk>"); 342*1da177e4SLinus Torvalds MODULE_DESCRIPTION("AGP GART driver"); 343*1da177e4SLinus Torvalds MODULE_LICENSE("GPL and additional rights"); 344*1da177e4SLinus Torvalds MODULE_ALIAS_MISCDEV(AGPGART_MINOR); 345*1da177e4SLinus Torvalds 346*1da177e4SLinus Torvalds module_init(agp_init); 347*1da177e4SLinus Torvalds module_exit(agp_exit); 348*1da177e4SLinus Torvalds 349