diff options
-rw-r--r-- | shared-core/nouveau_drm.h | 2 | ||||
-rw-r--r-- | shared-core/nouveau_drv.h | 13 | ||||
-rw-r--r-- | shared-core/nouveau_fifo.c | 4 | ||||
-rw-r--r-- | shared-core/nouveau_state.c | 170 | ||||
-rw-r--r-- | shared-core/nv20_graph.c | 32 |
5 files changed, 206 insertions, 15 deletions
diff --git a/shared-core/nouveau_drm.h b/shared-core/nouveau_drm.h index 4b5869ad..a99c6156 100644 --- a/shared-core/nouveau_drm.h +++ b/shared-core/nouveau_drm.h @@ -178,5 +178,7 @@ struct drm_nouveau_sarea { #define DRM_NOUVEAU_MEM_ALLOC 0x08 #define DRM_NOUVEAU_MEM_FREE 0x09 #define DRM_NOUVEAU_MEM_TILE 0x0a +#define DRM_NOUVEAU_SUSPEND 0x0b +#define DRM_NOUVEAU_RESUME 0x0c #endif /* __NOUVEAU_DRM_H__ */ diff --git a/shared-core/nouveau_drv.h b/shared-core/nouveau_drv.h index cd5f9cf8..a97e3e99 100644 --- a/shared-core/nouveau_drv.h +++ b/shared-core/nouveau_drv.h @@ -310,6 +310,14 @@ struct drm_nouveau_private { struct nouveau_config config; struct list_head gpuobj_list; + + struct nouveau_suspend_resume { + uint32_t fifo_mode; + uint32_t graph_ctx_control; + uint32_t graph_state; + uint32_t *ramin_copy; + uint64_t ramin_size; + } susres; }; #define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do { \ @@ -344,6 +352,10 @@ extern void nouveau_wait_for_idle(struct drm_device *); extern int nouveau_card_init(struct drm_device *); extern int nouveau_ioctl_card_init(struct drm_device *, void *data, struct drm_file *); +extern int nouveau_ioctl_suspend(struct drm_device *, void *data, + struct drm_file *); +extern int nouveau_ioctl_resume(struct drm_device *, void *data, + struct drm_file *); /* nouveau_mem.c */ extern int nouveau_mem_init_heap(struct mem_block **, uint64_t start, @@ -391,6 +403,7 @@ extern int nouveau_fifo_alloc(struct drm_device *dev, struct mem_block *pushbuf, uint32_t fb_ctxdma, uint32_t tt_ctxdma); extern void nouveau_fifo_free(struct nouveau_channel *); +extern int nouveau_channel_idle(struct nouveau_channel *chan); /* nouveau_object.c */ extern int nouveau_gpuobj_early_init(struct drm_device *); diff --git a/shared-core/nouveau_fifo.c b/shared-core/nouveau_fifo.c index 5ec2bc3e..92ea8fc2 100644 --- a/shared-core/nouveau_fifo.c +++ b/shared-core/nouveau_fifo.c @@ -390,7 +390,7 @@ nouveau_fifo_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, return 0; } -static int +int nouveau_channel_idle(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; @@ -594,6 +594,8 @@ struct drm_ioctl_desc nouveau_ioctls[] = { DRM_IOCTL_DEF(DRM_NOUVEAU_MEM_ALLOC, nouveau_ioctl_mem_alloc, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_MEM_FREE, nouveau_ioctl_mem_free, DRM_AUTH), DRM_IOCTL_DEF(DRM_NOUVEAU_MEM_TILE, nouveau_ioctl_mem_tile, DRM_AUTH), + DRM_IOCTL_DEF(DRM_NOUVEAU_SUSPEND, nouveau_ioctl_suspend, DRM_AUTH), + DRM_IOCTL_DEF(DRM_NOUVEAU_RESUME, nouveau_ioctl_resume, DRM_AUTH), }; int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls); diff --git a/shared-core/nouveau_state.c b/shared-core/nouveau_state.c index d9c6efe7..85220e6e 100644 --- a/shared-core/nouveau_state.c +++ b/shared-core/nouveau_state.c @@ -1,5 +1,6 @@ /* * Copyright 2005 Stephane Marchesin + * Copyright 2008 Stuart Bennett * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -698,3 +699,172 @@ void nouveau_wait_for_idle(struct drm_device *dev) } } } + +static int nouveau_suspend(struct drm_device *dev) +{ + struct mem_block *p; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_suspend_resume *susres = &dev_priv->susres; + struct nouveau_engine *engine = &dev_priv->Engine; + int i; + + drm_free(susres->ramin_copy, susres->ramin_size, DRM_MEM_DRIVER); + susres->ramin_size = 0; + list_for_each(p, dev_priv->ramin_heap) + if (p->file_priv && (p->start + p->size) > susres->ramin_size) + susres->ramin_size = p->start + p->size; + if (!(susres->ramin_copy = drm_alloc(susres->ramin_size, DRM_MEM_DRIVER))) { + DRM_ERROR("Couldn't alloc RAMIN backing for suspend\n"); + return -ENOMEM; + } + + for (i = 0; i < engine->fifo.channels; i++) { + uint64_t t_start = engine->timer.read(dev); + + if (dev_priv->fifos[i] == NULL) + continue; + + /* Give the channel a chance to idle, wait 2s (hopefully) */ + while (!nouveau_channel_idle(dev_priv->fifos[i])) + if (engine->timer.read(dev) - t_start > 2000000000ULL) { + DRM_ERROR("Failed to idle channel %d before" + "suspend.", dev_priv->fifos[i]->id); + return -EBUSY; + } + } + nouveau_wait_for_idle(dev); + + NV_WRITE(NV04_PGRAPH_FIFO, 0); + /* disable the fifo caches */ + NV_WRITE(NV03_PFIFO_CACHES, 0x00000000); + NV_WRITE(NV04_PFIFO_CACHE1_DMA_PUSH, + NV_READ(NV04_PFIFO_CACHE1_DMA_PUSH) & ~1); + NV_WRITE(NV03_PFIFO_CACHE1_PUSH0, 0x00000000); + NV_WRITE(NV04_PFIFO_CACHE1_PULL0, 0x00000000); + + susres->fifo_mode = NV_READ(NV04_PFIFO_MODE); + + if (dev_priv->card_type >= NV_10) { + susres->graph_state = NV_READ(NV10_PGRAPH_STATE); + susres->graph_ctx_control = NV_READ(NV10_PGRAPH_CTX_CONTROL); + } else { + susres->graph_state = NV_READ(NV04_PGRAPH_STATE); + susres->graph_ctx_control = NV_READ(NV04_PGRAPH_CTX_CONTROL); + } + + engine->fifo.save_context(dev_priv->fifos[engine->fifo.channel_id(dev)]); + engine->graph.save_context(dev_priv->fifos[engine->fifo.channel_id(dev)]); + nouveau_wait_for_idle(dev); + + for (i = 0; i < susres->ramin_size / 4; i++) + susres->ramin_copy[i] = NV_RI32(i << 2); + + /* reenable the fifo caches */ + NV_WRITE(NV04_PFIFO_CACHE1_DMA_PUSH, + NV_READ(NV04_PFIFO_CACHE1_DMA_PUSH) | 1); + NV_WRITE(NV03_PFIFO_CACHE1_PUSH0, 0x00000001); + NV_WRITE(NV04_PFIFO_CACHE1_PULL0, 0x00000001); + NV_WRITE(NV03_PFIFO_CACHES, 0x00000001); + NV_WRITE(NV04_PGRAPH_FIFO, 1); + + return 0; +} + +static int nouveau_resume(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_suspend_resume *susres = &dev_priv->susres; + struct nouveau_engine *engine = &dev_priv->Engine; + int i; + + if (!susres->ramin_copy) + return -EINVAL; + + DRM_DEBUG("Doing resume\n"); + + if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) { + struct drm_agp_info info; + struct drm_agp_mode mode; + + /* agp bridge drivers don't re-enable agp on resume. lame. */ + if ((i = drm_agp_info(dev, &info))) { + DRM_ERROR("Unable to get AGP info: %d\n", i); + return i; + } + mode.mode = info.mode; + if ((i = drm_agp_enable(dev, mode))) { + DRM_ERROR("Unable to enable AGP: %d\n", i); + return i; + } + } + + for (i = 0; i < susres->ramin_size / 4; i++) + NV_WI32(i << 2, susres->ramin_copy[i]); + + engine->mc.init(dev); + engine->timer.init(dev); + engine->fb.init(dev); + engine->graph.init(dev); + engine->fifo.init(dev); + + NV_WRITE(NV04_PGRAPH_FIFO, 0); + /* disable the fifo caches */ + NV_WRITE(NV03_PFIFO_CACHES, 0x00000000); + NV_WRITE(NV04_PFIFO_CACHE1_DMA_PUSH, + NV_READ(NV04_PFIFO_CACHE1_DMA_PUSH) & ~1); + NV_WRITE(NV03_PFIFO_CACHE1_PUSH0, 0x00000000); + NV_WRITE(NV04_PFIFO_CACHE1_PULL0, 0x00000000); + + /* PMC power cycling PFIFO in init clobbers some of the stuff stored in + * PRAMIN (such as NV04_PFIFO_CACHE1_DMA_INSTANCE). this is unhelpful + */ + for (i = 0; i < susres->ramin_size / 4; i++) + NV_WI32(i << 2, susres->ramin_copy[i]); + + engine->fifo.load_context(dev_priv->fifos[0]); + NV_WRITE(NV04_PFIFO_MODE, susres->fifo_mode); + + engine->graph.load_context(dev_priv->fifos[0]); + nouveau_wait_for_idle(dev); + + if (dev_priv->card_type >= NV_10) { + NV_WRITE(NV10_PGRAPH_STATE, susres->graph_state); + NV_WRITE(NV10_PGRAPH_CTX_CONTROL, susres->graph_ctx_control); + } else { + NV_WRITE(NV04_PGRAPH_STATE, susres->graph_state); + NV_WRITE(NV04_PGRAPH_CTX_CONTROL, susres->graph_ctx_control); + } + + /* reenable the fifo caches */ + NV_WRITE(NV04_PFIFO_CACHE1_DMA_PUSH, + NV_READ(NV04_PFIFO_CACHE1_DMA_PUSH) | 1); + NV_WRITE(NV03_PFIFO_CACHE1_PUSH0, 0x00000001); + NV_WRITE(NV04_PFIFO_CACHE1_PULL0, 0x00000001); + NV_WRITE(NV03_PFIFO_CACHES, 0x00000001); + NV_WRITE(NV04_PGRAPH_FIFO, 0x1); + + if (dev->irq_enabled) + nouveau_irq_postinstall(dev); + + drm_free(susres->ramin_copy, susres->ramin_size, DRM_MEM_DRIVER); + susres->ramin_copy = NULL; + susres->ramin_size = 0; + + return 0; +} + +int nouveau_ioctl_suspend(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + NOUVEAU_CHECK_INITIALISED_WITH_RETURN; + + return nouveau_suspend(dev); +} + +int nouveau_ioctl_resume(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + NOUVEAU_CHECK_INITIALISED_WITH_RETURN; + + return nouveau_resume(dev); +} diff --git a/shared-core/nv20_graph.c b/shared-core/nv20_graph.c index ad73ea91..d862debb 100644 --- a/shared-core/nv20_graph.c +++ b/shared-core/nv20_graph.c @@ -694,13 +694,15 @@ int nv20_graph_init(struct drm_device *dev) { NV_WRITE(NV03_PMC_ENABLE, NV_READ(NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); - /* Create Context Pointer Table */ - dev_priv->ctx_table_size = 32 * 4; - if ((ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, - dev_priv->ctx_table_size, 16, - NVOBJ_FLAG_ZERO_ALLOC, - &dev_priv->ctx_table))) - return ret; + if (!dev_priv->ctx_table) { + /* Create Context Pointer Table */ + dev_priv->ctx_table_size = 32 * 4; + if ((ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, + dev_priv->ctx_table_size, 16, + NVOBJ_FLAG_ZERO_ALLOC, + &dev_priv->ctx_table))) + return ret; + } NV_WRITE(NV20_PGRAPH_CHANNEL_CTX_TABLE, dev_priv->ctx_table->instance >> 4); @@ -812,13 +814,15 @@ int nv30_graph_init(struct drm_device *dev) NV_WRITE(NV03_PMC_ENABLE, NV_READ(NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); - /* Create Context Pointer Table */ - dev_priv->ctx_table_size = 32 * 4; - if ((ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, - dev_priv->ctx_table_size, 16, - NVOBJ_FLAG_ZERO_ALLOC, - &dev_priv->ctx_table))) - return ret; + if (!dev_priv->ctx_table) { + /* Create Context Pointer Table */ + dev_priv->ctx_table_size = 32 * 4; + if ((ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, + dev_priv->ctx_table_size, 16, + NVOBJ_FLAG_ZERO_ALLOC, + &dev_priv->ctx_table))) + return ret; + } NV_WRITE(NV20_PGRAPH_CHANNEL_CTX_TABLE, dev_priv->ctx_table->instance >> 4); |