summaryrefslogtreecommitdiff
path: root/shared-core
diff options
context:
space:
mode:
Diffstat (limited to 'shared-core')
-rw-r--r--shared-core/nouveau_dma.c168
-rw-r--r--shared-core/nouveau_dma.h98
2 files changed, 266 insertions, 0 deletions
diff --git a/shared-core/nouveau_dma.c b/shared-core/nouveau_dma.c
new file mode 100644
index 00000000..ce5b6299
--- /dev/null
+++ b/shared-core/nouveau_dma.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * 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 (including the
+ * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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 "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+
+#define SKIPS 8
+
+int
+nouveau_dma_channel_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_drm_channel *dchan = &dev_priv->channel;
+ struct nouveau_gpuobj *gpuobj = NULL;
+ int grclass, ret, i;
+
+ DRM_DEBUG("\n");
+
+ /* Allocate channel */
+ ret = nouveau_fifo_alloc(dev, &dchan->chan, (struct drm_file *)-2,
+ NvDmaFB, NvDmaTT);
+ if (ret) {
+ DRM_ERROR("Error allocating GPU channel: %d\n", ret);
+ return ret;
+ }
+ DRM_DEBUG("Using FIFO channel %d\n", dchan->chan->id);
+
+ /* Map push buffer */
+ drm_core_ioremap(dchan->chan->pushbuf_mem->map, dev);
+ if (!dchan->chan->pushbuf_mem->map->handle) {
+ DRM_ERROR("Failed to ioremap push buffer\n");
+ return -EINVAL;
+ }
+ dchan->pushbuf = (void*)dchan->chan->pushbuf_mem->map->handle;
+
+ /* Initialise DMA vars */
+ dchan->max = (dchan->chan->pushbuf_mem->size >> 2) - 2;
+ dchan->put = dchan->chan->pushbuf_base >> 2;
+ dchan->cur = dchan->put;
+ dchan->free = dchan->max - dchan->cur;
+
+ /* Insert NOPS for SKIPS */
+ dchan->free -= SKIPS;
+ dchan->push_free = SKIPS;
+ for (i=0; i<SKIPS; i++)
+ OUT_RING(0);
+
+ /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier */
+ if ((ret = nouveau_notifier_alloc(dchan->chan, NvNotify0, 1,
+ &dchan->notify0_offset))) {
+ DRM_ERROR("Error allocating NvNotify0: %d\n", ret);
+ return ret;
+ }
+
+ /* We use NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
+ if (dev_priv->card_type < NV_50) grclass = NV_MEMORY_TO_MEMORY_FORMAT;
+ else grclass = NV50_MEMORY_TO_MEMORY_FORMAT;
+ if ((ret = nouveau_gpuobj_gr_new(dchan->chan, grclass, &gpuobj))) {
+ DRM_ERROR("Error creating NvM2MF: %d\n", ret);
+ return ret;
+ }
+
+ if ((ret = nouveau_gpuobj_ref_add(dev, dchan->chan, NvM2MF,
+ gpuobj, NULL))) {
+ DRM_ERROR("Error referencing NvM2MF: %d\n", ret);
+ return ret;
+ }
+ dchan->m2mf_dma_source = NvDmaFB;
+ dchan->m2mf_dma_destin = NvDmaFB;
+
+ BEGIN_RING(NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
+ OUT_RING (NvM2MF);
+ BEGIN_RING(NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_SET_DMA_NOTIFY, 1);
+ OUT_RING (NvNotify0);
+ BEGIN_RING(NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_SET_DMA_SOURCE, 2);
+ OUT_RING (dchan->m2mf_dma_source);
+ OUT_RING (dchan->m2mf_dma_destin);
+ FIRE_RING();
+
+ return 0;
+}
+
+void
+nouveau_dma_channel_takedown(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_drm_channel *dchan = &dev_priv->channel;
+
+ DRM_DEBUG("\n");
+
+ if (dchan->chan) {
+ nouveau_fifo_free(dchan->chan);
+ dchan->chan = NULL;
+ }
+}
+
+#define RING_SKIPS 8
+
+#define READ_GET() ((NV_READ(NV03_FIFO_REGS_DMAGET(dchan->chan->id)) - \
+ dchan->chan->pushbuf_base) >> 2)
+#define WRITE_PUT(val) do { \
+ NV_WRITE(NV03_FIFO_REGS_DMAPUT(dchan->chan->id), \
+ ((val) << 2) + dchan->chan->pushbuf_base); \
+} while(0)
+
+int
+nouveau_dma_wait(struct drm_device *dev, int size)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_drm_channel *dchan = &dev_priv->channel;
+ uint32_t get;
+
+ while (dchan->free < size) {
+ get = READ_GET();
+
+ if (dchan->put >= get) {
+ dchan->free = dchan->max - dchan->cur;
+
+ if (dchan->free < size) {
+ dchan->push_free = 1;
+ OUT_RING(0x20000000|dchan->chan->pushbuf_base);
+ if (get <= RING_SKIPS) {
+ /*corner case - will be idle*/
+ if (dchan->put <= RING_SKIPS)
+ WRITE_PUT(RING_SKIPS + 1);
+
+ do {
+ get = READ_GET();
+ } while (get <= RING_SKIPS);
+ }
+
+ WRITE_PUT(RING_SKIPS);
+ dchan->cur = dchan->put = RING_SKIPS;
+ dchan->free = get - (RING_SKIPS + 1);
+ }
+ } else {
+ dchan->free = get - dchan->cur - 1;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/shared-core/nouveau_dma.h b/shared-core/nouveau_dma.h
new file mode 100644
index 00000000..5e51c1c4
--- /dev/null
+++ b/shared-core/nouveau_dma.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * 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 (including the
+ * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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.
+ *
+ */
+
+#ifndef __NOUVEAU_DMA_H__
+#define __NOUVEAU_DMA_H__
+
+typedef enum {
+ NvSubM2MF = 0,
+} nouveau_subchannel_id_t;
+
+typedef enum {
+ NvM2MF = 0x80039001,
+ NvDmaFB = 0x8003d001,
+ NvDmaTT = 0x8003d002,
+ NvNotify0 = 0x8003d003
+} nouveau_object_handle_t;
+
+#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039
+#define NV_MEMORY_TO_MEMORY_FORMAT_NAME 0x00000000
+#define NV_MEMORY_TO_MEMORY_FORMAT_SET_REF 0x00000050
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOP 0x00000100
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE 0x00000000
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE_LE_AWAKEN 0x00000001
+#define NV_MEMORY_TO_MEMORY_FORMAT_SET_DMA_NOTIFY 0x00000180
+#define NV_MEMORY_TO_MEMORY_FORMAT_SET_DMA_SOURCE 0x00000184
+#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
+
+#define NV50_MEMORY_TO_MEMORY_FORMAT 0x00005039
+#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK200 0x00000200
+#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK21C 0x0000021c
+#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN_HIGH 0x00000238
+#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_OUT_HIGH 0x0000023c
+
+#define BEGIN_RING(subc, mthd, cnt) do { \
+ int push_size = (cnt) + 1; \
+ if (dchan->push_free) { \
+ DRM_ERROR("prior packet incomplete: %d\n", dchan->push_free); \
+ break; \
+ } \
+ if (dchan->free < push_size) { \
+ if (nouveau_dma_wait(dev, push_size)) { \
+ DRM_ERROR("FIFO timeout\n"); \
+ break; \
+ } \
+ } \
+ dchan->free -= push_size; \
+ dchan->push_free = push_size; \
+ OUT_RING(((cnt)<<18) | ((subc)<<15) | mthd); \
+} while(0)
+
+#define OUT_RING(data) do { \
+ if (dchan->push_free == 0) { \
+ DRM_ERROR("no space left in packet\n"); \
+ break; \
+ } \
+ dchan->pushbuf[dchan->cur++] = (data); \
+ dchan->push_free--; \
+} while(0)
+
+#define FIRE_RING() do { \
+ if (dchan->push_free) { \
+ DRM_ERROR("packet incomplete: %d\n", dchan->push_free); \
+ break; \
+ } \
+ if (dchan->cur != dchan->put) { \
+ DRM_MEMORYBARRIER(); \
+ dchan->put = dchan->cur; \
+ NV_WRITE(NV03_FIFO_REGS_DMAPUT(dchan->chan->id), \
+ (dchan->put<<2)); \
+ } \
+} while(0)
+
+#endif
+