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