nouveau: fifo and graphics engine suspend and resume for nv04-nv4x
Corresponding DDX patch at http://people.freedesktop.org/~stuart/nv0x-nv4x_suspend/main
parent
41b83a9958
commit
0da66c27fa
|
@ -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__ */
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue