drm/nouveau/pci: merge agp handling from nouveau drm

This commit reinstates the pre-DEVINIT AGP fiddling that was broken in
an earlier commit.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Ben Skeggs 2015-08-20 14:54:23 +10:00
parent 26c9e8effe
commit 340b0e7c50
18 changed files with 271 additions and 239 deletions

View File

@ -18,7 +18,6 @@ nouveau-y += $(nvkm-y)
ifdef CONFIG_X86 ifdef CONFIG_X86
nouveau-$(CONFIG_ACPI) += nouveau_acpi.o nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
endif endif
nouveau-y += nouveau_agp.o
nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o
nouveau-y += nouveau_drm.o nouveau-y += nouveau_drm.o
nouveau-y += nouveau_hwmon.o nouveau-y += nouveau_hwmon.o

View File

@ -45,6 +45,7 @@ u64 nvif_device_time(struct nvif_device *);
#include <subdev/i2c.h> #include <subdev/i2c.h>
#include <subdev/timer.h> #include <subdev/timer.h>
#include <subdev/therm.h> #include <subdev/therm.h>
#include <subdev/pci.h>
#define nvxx_device(a) ({ \ #define nvxx_device(a) ({ \
struct nvif_device *_device = (a); \ struct nvif_device *_device = (a); \

View File

@ -24,6 +24,7 @@
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/agp_backend.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>

View File

@ -4,6 +4,7 @@
const char *nvkm_stropt(const char *optstr, const char *opt, int *len); const char *nvkm_stropt(const char *optstr, const char *opt, int *len);
bool nvkm_boolopt(const char *optstr, const char *opt, bool value); 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); int nvkm_dbgopt(const char *optstr, const char *sub);
/* compares unterminated string 'str' with zero-terminated string 'cmp' */ /* compares unterminated string 'str' with zero-terminated string 'cmp' */

View File

@ -7,6 +7,17 @@ struct nvkm_pci {
struct nvkm_subdev subdev; struct nvkm_subdev subdev;
struct pci_dev *pdev; struct pci_dev *pdev;
int irq; int irq;
struct {
struct agp_bridge_data *bridge;
u32 mode;
u64 base;
u64 size;
int mtrr;
bool cma;
bool acquired;
} agp;
bool msi; bool msi;
}; };

View File

@ -498,7 +498,7 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
args.start += chan->ntfy_vma.offset; args.start += chan->ntfy_vma.offset;
args.limit += chan->ntfy_vma.offset; args.limit += chan->ntfy_vma.offset;
} else } else
if (drm->agp.stat == ENABLED) { if (drm->agp.bridge) {
args.target = NV_DMA_V0_TARGET_AGP; args.target = NV_DMA_V0_TARGET_AGP;
args.access = NV_DMA_V0_ACCESS_RDWR; args.access = NV_DMA_V0_ACCESS_RDWR;
args.start += drm->agp.base + chan->ntfy->bo.offset; args.start += drm->agp.base + chan->ntfy->bo.offset;

View File

@ -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
}

View File

@ -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

View File

@ -576,10 +576,9 @@ nouveau_ttm_tt_create(struct ttm_bo_device *bdev, unsigned long size,
{ {
#if __OS_HAS_AGP #if __OS_HAS_AGP
struct nouveau_drm *drm = nouveau_bdev(bdev); struct nouveau_drm *drm = nouveau_bdev(bdev);
struct drm_device *dev = drm->dev;
if (drm->agp.stat == ENABLED) { if (drm->agp.bridge) {
return ttm_agp_tt_create(bdev, dev->agp->bridge, size, return ttm_agp_tt_create(bdev, drm->agp.bridge, size,
page_flags, dummy_read); page_flags, dummy_read);
} }
#endif #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) if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
man->func = &nouveau_gart_manager; man->func = &nouveau_gart_manager;
else else
if (drm->agp.stat != ENABLED) if (!drm->agp.bridge)
man->func = &nv04_gart_manager; man->func = &nv04_gart_manager;
else else
man->func = &ttm_bo_manager_func; man->func = &ttm_bo_manager_func;
if (drm->agp.stat == ENABLED) { if (drm->agp.bridge) {
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_FLAG_UNCACHED | man->available_caching = TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_WC; 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; return 0;
case TTM_PL_TT: case TTM_PL_TT:
#if __OS_HAS_AGP #if __OS_HAS_AGP
if (drm->agp.stat == ENABLED) { if (drm->agp.bridge) {
mem->bus.offset = mem->start << PAGE_SHIFT; mem->bus.offset = mem->start << PAGE_SHIFT;
mem->bus.base = drm->agp.base; mem->bus.base = drm->agp.base;
mem->bus.is_iomem = !drm->dev->agp->cant_use_aperture; mem->bus.is_iomem = !drm->agp.cma;
} }
#endif #endif
if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA || !node->memtype) 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); return ttm_dma_populate(ttm_dma, dev->dev);
#if __OS_HAS_AGP #if __OS_HAS_AGP
if (drm->agp.stat == ENABLED) { if (drm->agp.bridge) {
return ttm_agp_tt_populate(ttm); return ttm_agp_tt_populate(ttm);
} }
#endif #endif
@ -1565,7 +1564,7 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm)
} }
#if __OS_HAS_AGP #if __OS_HAS_AGP
if (drm->agp.stat == ENABLED) { if (drm->agp.bridge) {
ttm_agp_tt_unpopulate(ttm); ttm_agp_tt_unpopulate(ttm);
return; return;
} }

View File

@ -160,7 +160,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nvif_device *device,
args.limit = device->info.ram_user - 1; args.limit = device->info.ram_user - 1;
} }
} else { } else {
if (chan->drm->agp.stat == ENABLED) { if (chan->drm->agp.bridge) {
args.target = NV_DMA_V0_TARGET_AGP; args.target = NV_DMA_V0_TARGET_AGP;
args.access = NV_DMA_V0_ACCESS_RDWR; args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = chan->drm->agp.base; 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.start = 0;
args.limit = cli->vm->mmu->limit - 1; args.limit = cli->vm->mmu->limit - 1;
} else } else
if (chan->drm->agp.stat == ENABLED) { if (chan->drm->agp.bridge) {
args.target = NV_DMA_V0_TARGET_AGP; args.target = NV_DMA_V0_TARGET_AGP;
args.access = NV_DMA_V0_ACCESS_RDWR; args.access = NV_DMA_V0_ACCESS_RDWR;
args.start = chan->drm->agp.base; args.start = chan->drm->agp.base;

View File

@ -41,7 +41,6 @@
#include "nouveau_dma.h" #include "nouveau_dma.h"
#include "nouveau_ttm.h" #include "nouveau_ttm.h"
#include "nouveau_gem.h" #include "nouveau_gem.h"
#include "nouveau_agp.h"
#include "nouveau_vga.h" #include "nouveau_vga.h"
#include "nouveau_sysfs.h" #include "nouveau_sysfs.h"
#include "nouveau_hwmon.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); nvif_mask(&drm->device.object, 0x00088080, 0x00000800, 0x00000000);
nouveau_vga_init(drm); nouveau_vga_init(drm);
nouveau_agp_init(drm);
if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) { if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
ret = nvkm_vm_new(nvxx_device(&drm->device), 0, (1ULL << 40), ret = nvkm_vm_new(nvxx_device(&drm->device), 0, (1ULL << 40),
@ -474,7 +472,6 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
fail_bios: fail_bios:
nouveau_ttm_fini(drm); nouveau_ttm_fini(drm);
fail_ttm: fail_ttm:
nouveau_agp_fini(drm);
nouveau_vga_fini(drm); nouveau_vga_fini(drm);
fail_device: fail_device:
nvif_device_fini(&drm->device); nvif_device_fini(&drm->device);
@ -500,7 +497,6 @@ nouveau_drm_unload(struct drm_device *dev)
nouveau_bios_takedown(dev); nouveau_bios_takedown(dev);
nouveau_ttm_fini(drm); nouveau_ttm_fini(drm);
nouveau_agp_fini(drm);
nouveau_vga_fini(drm); nouveau_vga_fini(drm);
nvif_device_fini(&drm->device); nvif_device_fini(&drm->device);
@ -584,7 +580,6 @@ nouveau_do_suspend(struct drm_device *dev, bool runtime)
if (ret) if (ret)
goto fail_client; goto fail_client;
nouveau_agp_fini(drm);
return 0; return 0;
fail_client: fail_client:
@ -609,13 +604,8 @@ nouveau_do_resume(struct drm_device *dev, bool runtime)
struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_drm *drm = nouveau_drm(dev);
struct nouveau_cli *cli; struct nouveau_cli *cli;
NV_INFO(drm, "re-enabling device...\n");
nouveau_agp_reset(drm);
NV_INFO(drm, "resuming kernel object tree...\n"); NV_INFO(drm, "resuming kernel object tree...\n");
nvif_client_resume(&drm->client.base); nvif_client_resume(&drm->client.base);
nouveau_agp_init(drm);
NV_INFO(drm, "resuming client object trees...\n"); NV_INFO(drm, "resuming client object trees...\n");
if (drm->fence && nouveau_fence(drm)->resume) if (drm->fence && nouveau_fence(drm)->resume)
@ -929,7 +919,6 @@ nouveau_driver_fops = {
static struct drm_driver static struct drm_driver
driver_stub = { driver_stub = {
.driver_features = .driver_features =
DRIVER_USE_AGP |
DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER | DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER |
DRIVER_KMS_LEGACY_CONTEXT, DRIVER_KMS_LEGACY_CONTEXT,

View File

@ -111,13 +111,10 @@ struct nouveau_drm {
struct list_head clients; struct list_head clients;
struct { struct {
enum { struct agp_bridge_data *bridge;
UNKNOWN = 0,
DISABLE = 1,
ENABLED = 2
} stat;
u32 base; u32 base;
u32 size; u32 size;
bool cma;
} agp; } agp;
/* TTM interface support */ /* TTM interface support */

View File

@ -336,13 +336,21 @@ int
nouveau_ttm_init(struct nouveau_drm *drm) nouveau_ttm_init(struct nouveau_drm *drm)
{ {
struct nvkm_device *device = nvxx_device(&drm->device); struct nvkm_device *device = nvxx_device(&drm->device);
struct nvkm_pci *pci = device->pci;
struct drm_device *dev = drm->dev; struct drm_device *dev = drm->dev;
u32 bits; u32 bits;
int ret; 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; bits = nvxx_mmu(&drm->device)->dma_bits;
if (nvxx_device(&drm->device)->func->pci) { 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))) !pci_dma_supported(dev->pdev, DMA_BIT_MASK(bits)))
bits = 32; bits = 32;
@ -386,7 +394,7 @@ nouveau_ttm_init(struct nouveau_drm *drm)
device->func->resource_size(device, 1)); device->func->resource_size(device, 1));
/* GART init */ /* GART init */
if (drm->agp.stat != ENABLED) { if (!drm->agp.bridge) {
drm->gem.gart_available = nvxx_mmu(&drm->device)->limit; drm->gem.gart_available = nvxx_mmu(&drm->device)->limit;
} else { } else {
drm->gem.gart_available = drm->agp.size; drm->gem.gart_available = drm->agp.size;

View File

@ -73,6 +73,24 @@ nvkm_boolopt(const char *optstr, const char *opt, bool value)
return 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 int
nvkm_dbgopt(const char *optstr, const char *sub) nvkm_dbgopt(const char *optstr, const char *sub)
{ {

View File

@ -1,3 +1,4 @@
nvkm-y += nvkm/subdev/pci/agp.o
nvkm-y += nvkm/subdev/pci/base.o nvkm-y += nvkm/subdev/pci/base.o
nvkm-y += nvkm/subdev/pci/nv04.o nvkm-y += nvkm/subdev/pci/nv04.o
nvkm-y += nvkm/subdev/pci/nv40.o nvkm-y += nvkm/subdev/pci/nv40.o

View File

@ -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

View File

@ -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

View File

@ -22,6 +22,7 @@
* Authors: Ben Skeggs <bskeggs@redhat.com> * Authors: Ben Skeggs <bskeggs@redhat.com>
*/ */
#include "priv.h" #include "priv.h"
#include "agp.h"
#include <core/option.h> #include <core/option.h>
#include <core/pci.h> #include <core/pci.h>
@ -76,10 +77,24 @@ static int
nvkm_pci_fini(struct nvkm_subdev *subdev, bool suspend) nvkm_pci_fini(struct nvkm_subdev *subdev, bool suspend)
{ {
struct nvkm_pci *pci = nvkm_pci(subdev); struct nvkm_pci *pci = nvkm_pci(subdev);
if (pci->irq >= 0) { if (pci->irq >= 0) {
free_irq(pci->irq, pci); free_irq(pci->irq, pci);
pci->irq = -1; 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; return 0;
} }
@ -90,6 +105,12 @@ nvkm_pci_init(struct nvkm_subdev *subdev)
struct pci_dev *pdev = pci->pdev; struct pci_dev *pdev = pci->pdev;
int ret; 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); ret = request_irq(pdev->irq, nvkm_pci_intr, IRQF_SHARED, "nvkm", pci);
if (ret) if (ret)
return ret; return ret;
@ -102,6 +123,7 @@ static void *
nvkm_pci_dtor(struct nvkm_subdev *subdev) nvkm_pci_dtor(struct nvkm_subdev *subdev)
{ {
struct nvkm_pci *pci = nvkm_pci(subdev); struct nvkm_pci *pci = nvkm_pci(subdev);
nvkm_agp_dtor(pci);
if (pci->msi) if (pci->msi)
pci_disable_msi(pci->pdev); pci_disable_msi(pci->pdev);
return nvkm_pci(subdev); return nvkm_pci(subdev);
@ -110,6 +132,7 @@ nvkm_pci_dtor(struct nvkm_subdev *subdev)
static const struct nvkm_subdev_func static const struct nvkm_subdev_func
nvkm_pci_func = { nvkm_pci_func = {
.dtor = nvkm_pci_dtor, .dtor = nvkm_pci_dtor,
.preinit = nvkm_pci_preinit,
.init = nvkm_pci_init, .init = nvkm_pci_init,
.fini = nvkm_pci_fini, .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->pdev = device->func->pci(device)->pdev;
pci->irq = -1; pci->irq = -1;
if (device->type == NVKM_DEVICE_AGP)
nvkm_agp_ctor(pci);
switch (pci->pdev->device & 0x0ff0) { switch (pci->pdev->device & 0x0ff0) {
case 0x00f0: case 0x00f0:
case 0x02e0: case 0x02e0: