summaryrefslogtreecommitdiff
path: root/shared-core/nouveau_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared-core/nouveau_irq.c')
-rw-r--r--shared-core/nouveau_irq.c151
1 files changed, 104 insertions, 47 deletions
diff --git a/shared-core/nouveau_irq.c b/shared-core/nouveau_irq.c
index ec158d82..2a3d8a0b 100644
--- a/shared-core/nouveau_irq.c
+++ b/shared-core/nouveau_irq.c
@@ -71,8 +71,9 @@ nouveau_fifo_irq_handler(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->Engine;
- uint32_t status;
+ uint32_t status, reassign;
+ reassign = NV_READ(NV03_PFIFO_CACHES) & 1;
while ((status = NV_READ(NV03_PFIFO_INTR_0))) {
uint32_t chid, get;
@@ -119,9 +120,10 @@ nouveau_fifo_irq_handler(struct drm_device *dev)
if (status) {
DRM_INFO("Unhandled PFIFO_INTR - 0x%08x\n", status);
NV_WRITE(NV03_PFIFO_INTR_0, status);
+ NV_WRITE(NV03_PMC_INTR_EN_0, 0);
}
- NV_WRITE(NV03_PFIFO_CACHES, 1);
+ NV_WRITE(NV03_PFIFO_CACHES, reassign);
}
NV_WRITE(NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING);
@@ -189,55 +191,54 @@ nouveau_print_bitfield_names(uint32_t value,
}
static int
-nouveau_graph_trapped_channel(struct drm_device *dev, int *channel_ret)
+nouveau_graph_chid_from_grctx(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_engine *engine = &dev_priv->Engine;
- int channel;
+ uint32_t inst;
+ int i;
- if (dev_priv->card_type < NV_10) {
- channel = (NV_READ(NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0xf;
- } else if (dev_priv->card_type < NV_40) {
- channel = (NV_READ(NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
- } else
- if (dev_priv->card_type < NV_50) {
- uint32_t cur_grctx = (NV_READ(0x40032C) & 0xfffff) << 4;
+ if (dev_priv->card_type < NV_40)
+ return dev_priv->Engine.fifo.channels;
+ else
+ if (dev_priv->card_type < NV_50)
+ inst = (NV_READ(0x40032c) & 0xfffff) << 4;
+ else
+ inst = NV_READ(0x40032c) & 0xfffff;
- /* 0x400704 *sometimes* contains a sensible channel ID, but
- * mostly not.. for now lookup which channel owns the active
- * PGRAPH context. Probably a better way, but this'll do
- * for now.
- */
- for (channel = 0; channel < 32; channel++) {
- if (dev_priv->fifos[channel] == NULL)
- continue;
- if (cur_grctx ==
- dev_priv->fifos[channel]->ramin_grctx->instance)
- break;
- }
- if (channel == 32) {
- DRM_ERROR("AIII, unable to determine active channel "
- "from PGRAPH context 0x%08x\n", cur_grctx);
- return -EINVAL;
- }
- } else {
- uint32_t cur_grctx = (NV_READ(0x40032C) & 0xfffff) << 12;
+ for (i = 0; i < dev_priv->Engine.fifo.channels; i++) {
+ struct nouveau_channel *chan = dev_priv->fifos[i];
- for (channel = 0; channel < 128; channel++) {
- if (dev_priv->fifos[channel] == NULL)
- continue;
- if (cur_grctx ==
- dev_priv->fifos[channel]->ramin_grctx->instance)
+ if (!chan || !chan->ramin_grctx)
+ continue;
+
+ if (dev_priv->card_type < NV_50) {
+ if (inst == chan->ramin_grctx->instance)
+ break;
+ } else {
+ if (inst == INSTANCE_RD(chan->ramin_grctx->gpuobj, 0))
break;
- }
- if (channel == 128) {
- DRM_ERROR("AIII, unable to determine active channel "
- "from PGRAPH context 0x%08x\n", cur_grctx);
- return -EINVAL;
}
}
- if (channel > engine->fifo.channels || !dev_priv->fifos[channel]) {
+ return i;
+}
+
+static int
+nouveau_graph_trapped_channel(struct drm_device *dev, int *channel_ret)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_engine *engine = &dev_priv->Engine;
+ int channel;
+
+ if (dev_priv->card_type < NV_10)
+ channel = (NV_READ(NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0xf;
+ else
+ if (dev_priv->card_type < NV_40)
+ channel = (NV_READ(NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
+ else
+ channel = nouveau_graph_chid_from_grctx(dev);
+
+ if (channel >= engine->fifo.channels || !dev_priv->fifos[channel]) {
DRM_ERROR("AIII, invalid/inactive channel id %d\n", channel);
return -EINVAL;
}
@@ -251,6 +252,7 @@ struct nouveau_pgraph_trap {
int class;
int subc, mthd, size;
uint32_t data, data2;
+ uint32_t nsource, nstatus;
};
static void
@@ -260,6 +262,12 @@ nouveau_graph_trap_info(struct drm_device *dev,
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t address;
+ trap->nsource = trap->nstatus = 0;
+ if (dev_priv->card_type < NV_50) {
+ trap->nsource = NV_READ(NV03_PGRAPH_NSOURCE);
+ trap->nstatus = NV_READ(NV03_PGRAPH_NSTATUS);
+ }
+
if (nouveau_graph_trapped_channel(dev, &trap->channel))
trap->channel = -1;
address = NV_READ(NV04_PGRAPH_TRAPPED_ADDR);
@@ -289,10 +297,7 @@ nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id,
struct nouveau_pgraph_trap *trap)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t nsource, nstatus;
-
- nsource = NV_READ(NV03_PGRAPH_NSOURCE);
- nstatus = NV_READ(NV03_PGRAPH_NSTATUS);
+ uint32_t nsource = trap->nsource, nstatus = trap->nstatus;
DRM_INFO("%s - nSource:", id);
nouveau_print_bitfield_names(nsource, nouveau_nsource_names,
@@ -347,6 +352,7 @@ nouveau_pgraph_intr_error(struct drm_device *dev, uint32_t nsource)
int unhandled = 0;
nouveau_graph_trap_info(dev, &trap);
+ trap.nsource = nsource;
if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
if (trap.channel >= 0 && trap.mthd == 0x0150) {
@@ -433,6 +439,53 @@ nouveau_pgraph_irq_handler(struct drm_device *dev)
}
static void
+nv50_pgraph_irq_handler(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t status;
+
+ status = NV_READ(NV03_PGRAPH_INTR);
+
+ if (status & 0x00000020) {
+ nouveau_pgraph_intr_error(dev,
+ NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD);
+
+ status &= ~0x00000020;
+ NV_WRITE(NV03_PGRAPH_INTR, 0x00000020);
+ }
+
+ if (status & 0x00100000) {
+ nouveau_pgraph_intr_error(dev,
+ NV03_PGRAPH_NSOURCE_DATA_ERROR);
+
+ status &= ~0x00100000;
+ NV_WRITE(NV03_PGRAPH_INTR, 0x00100000);
+ }
+
+ if (status & 0x00200000) {
+ nouveau_pgraph_intr_error(dev,
+ NV03_PGRAPH_NSOURCE_PROTECTION_ERROR);
+
+ status &= ~0x00200000;
+ NV_WRITE(NV03_PGRAPH_INTR, 0x00200000);
+ }
+
+ if (status) {
+ DRM_INFO("Unhandled PGRAPH_INTR - 0x%08x\n", status);
+ NV_WRITE(NV03_PGRAPH_INTR, status);
+ }
+
+ {
+ const int isb = (1 << 16) | (1 << 0);
+
+ if ((NV_READ(0x400500) & isb) != isb)
+ NV_WRITE(0x400500, NV_READ(0x400500) | isb);
+ }
+
+ NV_WRITE(NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
+}
+
+static void
nouveau_crtc_irq_handler(struct drm_device *dev, int crtc)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -485,7 +538,11 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
}
if (status & NV_PMC_INTR_0_PGRAPH_PENDING) {
- nouveau_pgraph_irq_handler(dev);
+ if (dev_priv->card_type >= NV_50)
+ nv50_pgraph_irq_handler(dev);
+ else
+ nouveau_pgraph_irq_handler(dev);
+
status &= ~NV_PMC_INTR_0_PGRAPH_PENDING;
}