OSDN Git Service

drm/nouveau/pci: merge agp handling from nouveau drm
authorBen Skeggs <bskeggs@redhat.com>
Thu, 20 Aug 2015 04:54:23 +0000 (14:54 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 28 Aug 2015 02:40:49 +0000 (12:40 +1000)
This commit reinstates the pre-DEVINIT AGP fiddling that was broken in
an earlier commit.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
18 files changed:
drivers/gpu/drm/nouveau/Kbuild
drivers/gpu/drm/nouveau/include/nvif/device.h
drivers/gpu/drm/nouveau/include/nvif/os.h
drivers/gpu/drm/nouveau/include/nvkm/core/option.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/pci.h
drivers/gpu/drm/nouveau/nouveau_abi16.c
drivers/gpu/drm/nouveau/nouveau_agp.c [deleted file]
drivers/gpu/drm/nouveau/nouveau_agp.h [deleted file]
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_chan.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_drm.h
drivers/gpu/drm/nouveau/nouveau_ttm.c
drivers/gpu/drm/nouveau/nvkm/core/option.c
drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c

index 2b76566..a34b437 100644 (file)
@@ -18,7 +18,6 @@ nouveau-y += $(nvkm-y)
 ifdef CONFIG_X86
 nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
 endif
-nouveau-y += nouveau_agp.o
 nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o
 nouveau-y += nouveau_drm.o
 nouveau-y += nouveau_hwmon.o
index 900e492..700a9b2 100644 (file)
@@ -45,6 +45,7 @@ u64  nvif_device_time(struct nvif_device *);
 #include <subdev/i2c.h>
 #include <subdev/timer.h>
 #include <subdev/therm.h>
+#include <subdev/pci.h>
 
 #define nvxx_device(a) ({                                                      \
        struct nvif_device *_device = (a);                                     \
index 54492cb..97317f7 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/power_supply.h>
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
+#include <linux/agp_backend.h>
 
 #include <asm/unaligned.h>
 
index 532bfa8..80fdc14 100644 (file)
@@ -4,6 +4,7 @@
 
 const char *nvkm_stropt(const char *optstr, const char *opt, int *len);
 bool nvkm_boolopt(const char *optstr, const char *opt, bool value);
+long nvkm_longopt(const char *optstr, const char *opt, long value);
 int  nvkm_dbgopt(const char *optstr, const char *sub);
 
 /* compares unterminated string 'str' with zero-terminated string 'cmp' */
index ac14fdf..5b3c054 100644 (file)
@@ -7,6 +7,17 @@ struct nvkm_pci {
        struct nvkm_subdev subdev;
        struct pci_dev *pdev;
        int irq;
+
+       struct {
+               struct agp_bridge_data *bridge;
+               u32 mode;
+               u64 base;
+               u64 size;
+               int mtrr;
+               bool cma;
+               bool acquired;
+       } agp;
+
        bool msi;
 };
 
index 4252e77..d336c22 100644 (file)
@@ -498,7 +498,7 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
                args.start += chan->ntfy_vma.offset;
                args.limit += chan->ntfy_vma.offset;
        } else
-       if (drm->agp.stat == ENABLED) {
+       if (drm->agp.bridge) {
                args.target = NV_DMA_V0_TARGET_AGP;
                args.access = NV_DMA_V0_ACCESS_RDWR;
                args.start += drm->agp.base + chan->ntfy->bo.offset;
diff --git a/drivers/gpu/drm/nouveau/nouveau_agp.c b/drivers/gpu/drm/nouveau/nouveau_agp.c
deleted file mode 100644 (file)
index c3f3e49..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-#include <linux/module.h>
-
-#include "nouveau_drm.h"
-#include "nouveau_agp.h"
-#include "nouveau_reg.h"
-
-#include <core/pci.h>
-
-#if __OS_HAS_AGP
-MODULE_PARM_DESC(agpmode, "AGP mode (0 to disable AGP)");
-static int nouveau_agpmode = -1;
-module_param_named(agpmode, nouveau_agpmode, int, 0400);
-
-struct nouveau_agpmode_quirk {
-       u16 hostbridge_vendor;
-       u16 hostbridge_device;
-       u16 chip_vendor;
-       u16 chip_device;
-       int mode;
-};
-
-static struct nouveau_agpmode_quirk nouveau_agpmode_quirk_list[] = {
-       /* VIA Apollo PRO133x / GeForce FX 5600 Ultra, max agpmode 2, fdo #20341 */
-       { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_NVIDIA, 0x0311, 2 },
-
-       {},
-};
-
-static unsigned long
-get_agp_mode(struct nouveau_drm *drm, const struct drm_agp_info *info)
-{
-       struct nvif_device *device = &drm->device;
-       struct pci_dev *pdev = nvxx_device(device)->func->pci(nvxx_device(device))->pdev;
-       struct nouveau_agpmode_quirk *quirk = nouveau_agpmode_quirk_list;
-       int agpmode = nouveau_agpmode;
-       unsigned long mode = info->mode;
-
-       /*
-        * FW seems to be broken on nv18, it makes the card lock up
-        * randomly.
-        */
-       if (device->info.chipset == 0x18)
-               mode &= ~PCI_AGP_COMMAND_FW;
-
-       /*
-        * Go through the quirks list and adjust the agpmode accordingly.
-        */
-       while (agpmode == -1 && quirk->hostbridge_vendor) {
-               if (info->id_vendor == quirk->hostbridge_vendor &&
-                   info->id_device == quirk->hostbridge_device &&
-                   pdev->vendor == quirk->chip_vendor &&
-                   pdev->device == quirk->chip_device) {
-                       agpmode = quirk->mode;
-                       NV_INFO(drm, "Forcing agp mode to %dX. Use agpmode to override.\n",
-                               agpmode);
-                       break;
-               }
-               ++quirk;
-       }
-
-       /*
-        * AGP mode set in the command line.
-        */
-       if (agpmode > 0) {
-               bool agpv3 = mode & 0x8;
-               int rate = agpv3 ? agpmode / 4 : agpmode;
-
-               mode = (mode & ~0x7) | (rate & 0x7);
-       }
-
-       return mode;
-}
-
-static bool
-nouveau_agp_enabled(struct nouveau_drm *drm)
-{
-       struct drm_device *dev = drm->dev;
-
-       if (!dev->pdev || !drm_pci_device_is_agp(dev) || !dev->agp)
-               return false;
-
-       if (drm->agp.stat == UNKNOWN) {
-               if (!nouveau_agpmode)
-                       return false;
-#ifdef __powerpc__
-               /* Disable AGP by default on all PowerPC machines for
-                * now -- At least some UniNorth-2 AGP bridges are
-                * known to be broken: DMA from the host to the card
-                * works just fine, but writeback from the card to the
-                * host goes straight to memory untranslated bypassing
-                * the GATT somehow, making them quite painful to deal
-                * with...
-                */
-               if (nouveau_agpmode == -1)
-                       return false;
-#endif
-               return true;
-       }
-
-       return (drm->agp.stat == ENABLED);
-}
-#endif
-
-void
-nouveau_agp_reset(struct nouveau_drm *drm)
-{
-#if __OS_HAS_AGP
-       struct nvif_object *device = &drm->device.object;
-       struct drm_device *dev = drm->dev;
-       u32 save[2];
-       int ret;
-
-       if (!nouveau_agp_enabled(drm))
-               return;
-
-       /* First of all, disable fast writes, otherwise if it's
-        * already enabled in the AGP bridge and we disable the card's
-        * AGP controller we might be locking ourselves out of it. */
-       if ((nvif_rd32(device, NV04_PBUS_PCI_NV_19) |
-            dev->agp->mode) & PCI_AGP_COMMAND_FW) {
-               struct drm_agp_info info;
-               struct drm_agp_mode mode;
-
-               ret = drm_agp_info(dev, &info);
-               if (ret)
-                       return;
-
-               mode.mode  = get_agp_mode(drm, &info);
-               mode.mode &= ~PCI_AGP_COMMAND_FW;
-
-               ret = drm_agp_enable(dev, mode);
-               if (ret)
-                       return;
-       }
-
-
-       /* clear busmaster bit, and disable AGP */
-       save[0] = nvif_mask(device, NV04_PBUS_PCI_NV_1, 0x00000004, 0x00000000);
-       nvif_wr32(device, NV04_PBUS_PCI_NV_19, 0);
-
-       /* reset PGRAPH, PFIFO and PTIMER */
-       save[1] = nvif_mask(device, 0x000200, 0x00011100, 0x00000000);
-       nvif_mask(device, 0x000200, 0x00011100, save[1]);
-
-       /* and restore bustmaster bit (gives effect of resetting AGP) */
-       nvif_wr32(device, NV04_PBUS_PCI_NV_1, save[0]);
-#endif
-}
-
-void
-nouveau_agp_init(struct nouveau_drm *drm)
-{
-#if __OS_HAS_AGP
-       struct drm_device *dev = drm->dev;
-       struct drm_agp_info info;
-       struct drm_agp_mode mode;
-       int ret;
-
-       if (!nouveau_agp_enabled(drm))
-               return;
-       drm->agp.stat = DISABLE;
-
-       ret = drm_agp_acquire(dev);
-       if (ret) {
-               NV_ERROR(drm, "unable to acquire AGP: %d\n", ret);
-               return;
-       }
-
-       ret = drm_agp_info(dev, &info);
-       if (ret) {
-               NV_ERROR(drm, "unable to get AGP info: %d\n", ret);
-               return;
-       }
-
-       /* see agp.h for the AGPSTAT_* modes available */
-       mode.mode = get_agp_mode(drm, &info);
-
-       ret = drm_agp_enable(dev, mode);
-       if (ret) {
-               NV_ERROR(drm, "unable to enable AGP: %d\n", ret);
-               return;
-       }
-
-       drm->agp.stat = ENABLED;
-       drm->agp.base = info.aperture_base;
-       drm->agp.size = info.aperture_size;
-#endif
-}
-
-void
-nouveau_agp_fini(struct nouveau_drm *drm)
-{
-#if __OS_HAS_AGP
-       struct drm_device *dev = drm->dev;
-       if (dev->agp && dev->agp->acquired)
-               drm_agp_release(dev);
-#endif
-}
diff --git a/drivers/gpu/drm/nouveau/nouveau_agp.h b/drivers/gpu/drm/nouveau/nouveau_agp.h
deleted file mode 100644 (file)
index b55c086..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef __NOUVEAU_AGP_H__
-#define __NOUVEAU_AGP_H__
-
-struct nouveau_drm;
-
-void nouveau_agp_reset(struct nouveau_drm *);
-void nouveau_agp_init(struct nouveau_drm *);
-void nouveau_agp_fini(struct nouveau_drm *);
-
-#endif
index 373fbd2..15057b3 100644 (file)
@@ -576,10 +576,9 @@ nouveau_ttm_tt_create(struct ttm_bo_device *bdev, unsigned long size,
 {
 #if __OS_HAS_AGP
        struct nouveau_drm *drm = nouveau_bdev(bdev);
-       struct drm_device *dev = drm->dev;
 
-       if (drm->agp.stat == ENABLED) {
-               return ttm_agp_tt_create(bdev, dev->agp->bridge, size,
+       if (drm->agp.bridge) {
+               return ttm_agp_tt_create(bdev, drm->agp.bridge, size,
                                         page_flags, dummy_read);
        }
 #endif
@@ -631,12 +630,12 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
                        man->func = &nouveau_gart_manager;
                else
-               if (drm->agp.stat != ENABLED)
+               if (!drm->agp.bridge)
                        man->func = &nv04_gart_manager;
                else
                        man->func = &ttm_bo_manager_func;
 
-               if (drm->agp.stat == ENABLED) {
+               if (drm->agp.bridge) {
                        man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
                        man->available_caching = TTM_PL_FLAG_UNCACHED |
                                TTM_PL_FLAG_WC;
@@ -1368,10 +1367,10 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
                return 0;
        case TTM_PL_TT:
 #if __OS_HAS_AGP
-               if (drm->agp.stat == ENABLED) {
+               if (drm->agp.bridge) {
                        mem->bus.offset = mem->start << PAGE_SHIFT;
                        mem->bus.base = drm->agp.base;
-                       mem->bus.is_iomem = !drm->dev->agp->cant_use_aperture;
+                       mem->bus.is_iomem = !drm->agp.cma;
                }
 #endif
                if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA || !node->memtype)
@@ -1498,7 +1497,7 @@ nouveau_ttm_tt_populate(struct ttm_tt *ttm)
                return ttm_dma_populate(ttm_dma, dev->dev);
 
 #if __OS_HAS_AGP
-       if (drm->agp.stat == ENABLED) {
+       if (drm->agp.bridge) {
                return ttm_agp_tt_populate(ttm);
        }
 #endif
@@ -1565,7 +1564,7 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
        }
 
 #if __OS_HAS_AGP
-       if (drm->agp.stat == ENABLED) {
+       if (drm->agp.bridge) {
                ttm_agp_tt_unpopulate(ttm);
                return;
        }
index f59c4f5..ff5e59d 100644 (file)
@@ -160,7 +160,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
                        args.limit = device->info.ram_user - 1;
                }
        } else {
-               if (chan->drm->agp.stat == ENABLED) {
+               if (chan->drm->agp.bridge) {
                        args.target = NV_DMA_V0_TARGET_AGP;
                        args.access = NV_DMA_V0_ACCESS_RDWR;
                        args.start = chan->drm->agp.base;
@@ -328,7 +328,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart)
                        args.start = 0;
                        args.limit = cli->vm->mmu->limit - 1;
                } else
-               if (chan->drm->agp.stat == ENABLED) {
+               if (chan->drm->agp.bridge) {
                        args.target = NV_DMA_V0_TARGET_AGP;
                        args.access = NV_DMA_V0_ACCESS_RDWR;
                        args.start = chan->drm->agp.base;
index 14a1348..ccefb64 100644 (file)
@@ -41,7 +41,6 @@
 #include "nouveau_dma.h"
 #include "nouveau_ttm.h"
 #include "nouveau_gem.h"
-#include "nouveau_agp.h"
 #include "nouveau_vga.h"
 #include "nouveau_sysfs.h"
 #include "nouveau_hwmon.h"
@@ -423,7 +422,6 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
                nvif_mask(&drm->device.object, 0x00088080, 0x00000800, 0x00000000);
 
        nouveau_vga_init(drm);
-       nouveau_agp_init(drm);
 
        if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
                ret = nvkm_vm_new(nvxx_device(&drm->device), 0, (1ULL << 40),
@@ -474,7 +472,6 @@ fail_dispctor:
 fail_bios:
        nouveau_ttm_fini(drm);
 fail_ttm:
-       nouveau_agp_fini(drm);
        nouveau_vga_fini(drm);
 fail_device:
        nvif_device_fini(&drm->device);
@@ -500,7 +497,6 @@ nouveau_drm_unload(struct drm_device *dev)
        nouveau_bios_takedown(dev);
 
        nouveau_ttm_fini(drm);
-       nouveau_agp_fini(drm);
        nouveau_vga_fini(drm);
 
        nvif_device_fini(&drm->device);
@@ -584,7 +580,6 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
        if (ret)
                goto fail_client;
 
-       nouveau_agp_fini(drm);
        return 0;
 
 fail_client:
@@ -609,13 +604,8 @@ nouveau_do_resume(struct drm_device *dev, bool runtime)
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_cli *cli;
 
-       NV_INFO(drm, "re-enabling device...\n");
-
-       nouveau_agp_reset(drm);
-
        NV_INFO(drm, "resuming kernel object tree...\n");
        nvif_client_resume(&drm->client.base);
-       nouveau_agp_init(drm);
 
        NV_INFO(drm, "resuming client object trees...\n");
        if (drm->fence && nouveau_fence(drm)->resume)
@@ -929,7 +919,6 @@ nouveau_driver_fops = {
 static struct drm_driver
 driver_stub = {
        .driver_features =
-               DRIVER_USE_AGP |
                DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER |
                DRIVER_KMS_LEGACY_CONTEXT,
 
index f18710a..7fb3a8a 100644 (file)
@@ -111,13 +111,10 @@ struct nouveau_drm {
        struct list_head clients;
 
        struct {
-               enum {
-                       UNKNOWN = 0,
-                       DISABLE = 1,
-                       ENABLED = 2
-               } stat;
+               struct agp_bridge_data *bridge;
                u32 base;
                u32 size;
+               bool cma;
        } agp;
 
        /* TTM interface support */
index ba9fd15..3f0fb55 100644 (file)
@@ -336,13 +336,21 @@ int
 nouveau_ttm_init(struct nouveau_drm *drm)
 {
        struct nvkm_device *device = nvxx_device(&drm->device);
+       struct nvkm_pci *pci = device->pci;
        struct drm_device *dev = drm->dev;
        u32 bits;
        int ret;
 
+       if (pci && pci->agp.bridge) {
+               drm->agp.bridge = pci->agp.bridge;
+               drm->agp.base = pci->agp.base;
+               drm->agp.size = pci->agp.size;
+               drm->agp.cma = pci->agp.cma;
+       }
+
        bits = nvxx_mmu(&drm->device)->dma_bits;
        if (nvxx_device(&drm->device)->func->pci) {
-               if (drm->agp.stat == ENABLED ||
+               if (drm->agp.bridge ||
                     !pci_dma_supported(dev->pdev, DMA_BIT_MASK(bits)))
                        bits = 32;
 
@@ -386,7 +394,7 @@ nouveau_ttm_init(struct nouveau_drm *drm)
                                         device->func->resource_size(device, 1));
 
        /* GART init */
-       if (drm->agp.stat != ENABLED) {
+       if (!drm->agp.bridge) {
                drm->gem.gart_available = nvxx_mmu(&drm->device)->limit;
        } else {
                drm->gem.gart_available = drm->agp.size;
index 98ebde3..3e62cf8 100644 (file)
@@ -73,6 +73,24 @@ nvkm_boolopt(const char *optstr, const char *opt, bool value)
        return value;
 }
 
+long
+nvkm_longopt(const char *optstr, const char *opt, long value)
+{
+       long result = value;
+       int arglen;
+       char *s;
+
+       optstr = nvkm_stropt(optstr, opt, &arglen);
+       if (optstr && (s = kstrndup(optstr, arglen, GFP_KERNEL))) {
+               int ret = kstrtol(s, 0, &value);
+               if (ret == 0)
+                       result = value;
+               kfree(s);
+       }
+
+       return result;
+}
+
 int
 nvkm_dbgopt(const char *optstr, const char *sub)
 {
index a8e9b0f..99672c3 100644 (file)
@@ -1,3 +1,4 @@
+nvkm-y += nvkm/subdev/pci/agp.o
 nvkm-y += nvkm/subdev/pci/base.o
 nvkm-y += nvkm/subdev/pci/nv04.o
 nvkm-y += nvkm/subdev/pci/nv40.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c
new file mode 100644 (file)
index 0000000..814cb51
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2015 Nouveau Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "agp.h"
+#ifdef __NVKM_PCI_AGP_H__
+#include <core/option.h>
+
+struct nvkm_device_agp_quirk {
+       u16 hostbridge_vendor;
+       u16 hostbridge_device;
+       u16 chip_vendor;
+       u16 chip_device;
+       int mode;
+};
+
+static const struct nvkm_device_agp_quirk
+nvkm_device_agp_quirks[] = {
+       /* VIA Apollo PRO133x / GeForce FX 5600 Ultra - fdo#20341 */
+       { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_NVIDIA, 0x0311, 2 },
+       {},
+};
+
+void
+nvkm_agp_fini(struct nvkm_pci *pci)
+{
+       if (pci->agp.acquired) {
+               agp_backend_release(pci->agp.bridge);
+               pci->agp.acquired = false;
+       }
+}
+
+/* Ensure AGP controller is in a consistent state in case we need to
+ * execute the VBIOS DEVINIT scripts.
+ */
+void
+nvkm_agp_preinit(struct nvkm_pci *pci)
+{
+       struct nvkm_device *device = pci->subdev.device;
+       u32 mode = nvkm_pci_rd32(pci, 0x004c);
+       u32 save[2];
+
+       /* First of all, disable fast writes, otherwise if it's already
+        * enabled in the AGP bridge and we disable the card's AGP
+        * controller we might be locking ourselves out of it.
+        */
+       if ((mode | pci->agp.mode) & PCI_AGP_COMMAND_FW) {
+               mode = pci->agp.mode & ~PCI_AGP_COMMAND_FW;
+               agp_enable(pci->agp.bridge, mode);
+       }
+
+       /* clear busmaster bit, and disable AGP */
+       save[0] = nvkm_pci_rd32(pci, 0x0004);
+       nvkm_pci_wr32(pci, 0x0004, save[0] & ~0x00000004);
+       nvkm_pci_wr32(pci, 0x004c, 0x00000000);
+
+       /* reset PGRAPH, PFIFO and PTIMER */
+       save[1] = nvkm_mask(device, 0x000200, 0x00011100, 0x00000000);
+       nvkm_mask(device, 0x000200, 0x00011100, save[1]);
+
+       /* and restore busmaster bit (gives effect of resetting AGP) */
+       nvkm_pci_wr32(pci, 0x0004, save[0]);
+}
+
+int
+nvkm_agp_init(struct nvkm_pci *pci)
+{
+       if (!agp_backend_acquire(pci->pdev)) {
+               nvkm_error(&pci->subdev, "failed to acquire agp\n");
+               return -ENODEV;
+       }
+
+       agp_enable(pci->agp.bridge, pci->agp.mode);
+       pci->agp.acquired = true;
+       return 0;
+}
+
+void
+nvkm_agp_dtor(struct nvkm_pci *pci)
+{
+       arch_phys_wc_del(pci->agp.mtrr);
+}
+
+void
+nvkm_agp_ctor(struct nvkm_pci *pci)
+{
+       const struct nvkm_device_agp_quirk *quirk = nvkm_device_agp_quirks;
+       struct nvkm_subdev *subdev = &pci->subdev;
+       struct nvkm_device *device = subdev->device;
+       struct agp_kern_info info;
+       int mode = -1;
+
+#ifdef __powerpc__
+       /* Disable AGP by default on all PowerPC machines for now -- At
+        * least some UniNorth-2 AGP bridges are known to be broken:
+        * DMA from the host to the card works just fine, but writeback
+        * from the card to the host goes straight to memory
+        * untranslated bypassing that GATT somehow, making them quite
+        * painful to deal with...
+        */
+       mode = 0;
+#endif
+       mode = nvkm_longopt(device->cfgopt, "NvAGP", mode);
+
+       /* acquire bridge temporarily, so that we can copy its info */
+       if (!(pci->agp.bridge = agp_backend_acquire(pci->pdev))) {
+               nvkm_warn(subdev, "failed to acquire agp\n");
+               return;
+       }
+       agp_copy_info(pci->agp.bridge, &info);
+       agp_backend_release(pci->agp.bridge);
+
+       pci->agp.mode = info.mode;
+       pci->agp.base = info.aper_base;
+       pci->agp.size = info.aper_size * 1024 * 1024;
+       pci->agp.cma  = info.cant_use_aperture;
+       pci->agp.mtrr = -1;
+
+       /* determine if bridge + chipset combination needs a workaround */
+       while (quirk->hostbridge_vendor) {
+               if (info.device->vendor == quirk->hostbridge_vendor &&
+                   info.device->device == quirk->hostbridge_device &&
+                   pci->pdev->vendor == quirk->chip_vendor &&
+                   pci->pdev->device == quirk->chip_device) {
+                       nvkm_info(subdev, "forcing default agp mode to %dX, "
+                                         "use NvAGP=<mode> to override\n",
+                                 quirk->mode);
+                       mode = quirk->mode;
+                       break;
+               }
+               quirk++;
+       }
+
+       /* apply quirk / user-specified mode */
+       if (mode >= 1) {
+               if (pci->agp.mode & 0x00000008)
+                       mode /= 4; /* AGPv3 */
+               pci->agp.mode &= ~0x00000007;
+               pci->agp.mode |= (mode & 0x7);
+       } else
+       if (mode == 0) {
+               pci->agp.bridge = NULL;
+               return;
+       }
+
+       /* fast writes appear to be broken on nv18, they make the card
+        * lock up randomly.
+        */
+       if (device->chipset == 0x18)
+               pci->agp.mode &= ~PCI_AGP_COMMAND_FW;
+
+       pci->agp.mtrr = arch_phys_wc_add(pci->agp.base, pci->agp.size);
+}
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h
new file mode 100644 (file)
index 0000000..df2dd08
--- /dev/null
@@ -0,0 +1,18 @@
+#include "priv.h"
+#if defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE))
+#ifndef __NVKM_PCI_AGP_H__
+#define __NVKM_PCI_AGP_H__
+
+void nvkm_agp_ctor(struct nvkm_pci *);
+void nvkm_agp_dtor(struct nvkm_pci *);
+void nvkm_agp_preinit(struct nvkm_pci *);
+int nvkm_agp_init(struct nvkm_pci *);
+void nvkm_agp_fini(struct nvkm_pci *);
+#endif
+#else
+static inline void nvkm_agp_ctor(struct nvkm_pci *pci) {}
+static inline void nvkm_agp_dtor(struct nvkm_pci *pci) {}
+static inline void nvkm_agp_preinit(struct nvkm_pci *pci) {}
+static inline int nvkm_agp_init(struct nvkm_pci *pci) { return -ENOSYS; }
+static inline void nvkm_agp_fini(struct nvkm_pci *pci) {}
+#endif
index e5e0d02..d1c148e 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs <bskeggs@redhat.com>
  */
 #include "priv.h"
+#include "agp.h"
 
 #include <core/option.h>
 #include <core/pci.h>
@@ -76,10 +77,24 @@ static int
 nvkm_pci_fini(struct nvkm_subdev *subdev, bool suspend)
 {
        struct nvkm_pci *pci = nvkm_pci(subdev);
+
        if (pci->irq >= 0) {
                free_irq(pci->irq, pci);
                pci->irq = -1;
        };
+
+       if (pci->agp.bridge)
+               nvkm_agp_fini(pci);
+
+       return 0;
+}
+
+static int
+nvkm_pci_preinit(struct nvkm_subdev *subdev)
+{
+       struct nvkm_pci *pci = nvkm_pci(subdev);
+       if (pci->agp.bridge)
+               nvkm_agp_preinit(pci);
        return 0;
 }
 
@@ -90,6 +105,12 @@ nvkm_pci_init(struct nvkm_subdev *subdev)
        struct pci_dev *pdev = pci->pdev;
        int ret;
 
+       if (pci->agp.bridge) {
+               ret = nvkm_agp_init(pci);
+               if (ret)
+                       return ret;
+       }
+
        ret = request_irq(pdev->irq, nvkm_pci_intr, IRQF_SHARED, "nvkm", pci);
        if (ret)
                return ret;
@@ -102,6 +123,7 @@ static void *
 nvkm_pci_dtor(struct nvkm_subdev *subdev)
 {
        struct nvkm_pci *pci = nvkm_pci(subdev);
+       nvkm_agp_dtor(pci);
        if (pci->msi)
                pci_disable_msi(pci->pdev);
        return nvkm_pci(subdev);
@@ -110,6 +132,7 @@ nvkm_pci_dtor(struct nvkm_subdev *subdev)
 static const struct nvkm_subdev_func
 nvkm_pci_func = {
        .dtor = nvkm_pci_dtor,
+       .preinit = nvkm_pci_preinit,
        .init = nvkm_pci_init,
        .fini = nvkm_pci_fini,
 };
@@ -127,6 +150,9 @@ nvkm_pci_new_(const struct nvkm_pci_func *func, struct nvkm_device *device,
        pci->pdev = device->func->pci(device)->pdev;
        pci->irq = -1;
 
+       if (device->type == NVKM_DEVICE_AGP)
+               nvkm_agp_ctor(pci);
+
        switch (pci->pdev->device & 0x0ff0) {
        case 0x00f0:
        case 0x02e0: