aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nouveau_dma.c
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2009-12-11 04:24:15 -0500
committerDave Airlie <airlied@redhat.com>2009-12-11 06:29:34 -0500
commit6ee738610f41b59733f63718f0bdbcba7d3a3f12 (patch)
treeeccb9f07671998c50a1bc606a54cd6f82ba43e0a /drivers/gpu/drm/nouveau/nouveau_dma.c
parentd1ede145cea25c5b6d2ebb19b167af14e374bb45 (diff)
drm/nouveau: Add DRM driver for NVIDIA GPUs
This adds a drm/kms staging non-API stable driver for GPUs from NVIDIA. This driver is a KMS-based driver and requires a compatible nouveau userspace libdrm and nouveau X.org driver. This driver requires firmware files not available in this kernel tree, interested parties can find them via the nouveau project git archive. This driver is reverse engineered, and is in no way supported by nVidia. Support for nearly the complete range of nvidia hw from nv04->g80 (nv50) is available, and the kms driver should support driving nearly all output types (displayport is under development still) along with supporting suspend/resume. This work is all from the upstream nouveau project found at nouveau.freedesktop.org. The original authors list from nouveau git tree is: Anssi Hannula <anssi.hannula@iki.fi> Ben Skeggs <bskeggs@redhat.com> Francisco Jerez <currojerez@riseup.net> Maarten Maathuis <madman2003@gmail.com> Marcin Koƛcielnicki <koriakin@0x04.net> Matthew Garrett <mjg@redhat.com> Matt Parnell <mparnell@gmail.com> Patrice Mandin <patmandin@gmail.com> Pekka Paalanen <pq@iki.fi> Xavier Chantry <shiningxc@gmail.com> along with project founder Stephane Marchesin <marchesin@icps.u-strasbg.fr> Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_dma.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
new file mode 100644
index 000000000000..703553687b20
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -0,0 +1,206 @@
1/*
2 * Copyright (C) 2007 Ben Skeggs.
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 */
26
27#include "drmP.h"
28#include "drm.h"
29#include "nouveau_drv.h"
30#include "nouveau_dma.h"
31
32int
33nouveau_dma_init(struct nouveau_channel *chan)
34{
35 struct drm_device *dev = chan->dev;
36 struct drm_nouveau_private *dev_priv = dev->dev_private;
37 struct nouveau_gpuobj *m2mf = NULL;
38 int ret, i;
39
40 /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
41 ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ?
42 0x0039 : 0x5039, &m2mf);
43 if (ret)
44 return ret;
45
46 ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL);
47 if (ret)
48 return ret;
49
50 /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
51 ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
52 if (ret)
53 return ret;
54
55 /* Map push buffer */
56 ret = nouveau_bo_map(chan->pushbuf_bo);
57 if (ret)
58 return ret;
59
60 /* Map M2MF notifier object - fbcon. */
61 if (drm_core_check_feature(dev, DRIVER_MODESET)) {
62 ret = nouveau_bo_map(chan->notifier_bo);
63 if (ret)
64 return ret;
65 }
66
67 /* Initialise DMA vars */
68 chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
69 chan->dma.put = 0;
70 chan->dma.cur = chan->dma.put;
71 chan->dma.free = chan->dma.max - chan->dma.cur;
72
73 /* Insert NOPS for NOUVEAU_DMA_SKIPS */
74 ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
75 if (ret)
76 return ret;
77
78 for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
79 OUT_RING(chan, 0);
80
81 /* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
82 ret = RING_SPACE(chan, 4);
83 if (ret)
84 return ret;
85 BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
86 OUT_RING(chan, NvM2MF);
87 BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
88 OUT_RING(chan, NvNotify0);
89
90 /* Sit back and pray the channel works.. */
91 FIRE_RING(chan);
92
93 return 0;
94}
95
96void
97OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
98{
99 bool is_iomem;
100 u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem);
101 mem = &mem[chan->dma.cur];
102 if (is_iomem)
103 memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4);
104 else
105 memcpy(mem, data, nr_dwords * 4);
106 chan->dma.cur += nr_dwords;
107}
108
109static inline bool
110READ_GET(struct nouveau_channel *chan, uint32_t *get)
111{
112 uint32_t val;
113
114 val = nvchan_rd32(chan, chan->user_get);
115 if (val < chan->pushbuf_base ||
116 val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) {
117 /* meaningless to dma_wait() except to know whether the
118 * GPU has stalled or not
119 */
120 *get = val;
121 return false;
122 }
123
124 *get = (val - chan->pushbuf_base) >> 2;
125 return true;
126}
127
128int
129nouveau_dma_wait(struct nouveau_channel *chan, int size)
130{
131 uint32_t get, prev_get = 0, cnt = 0;
132 bool get_valid;
133
134 while (chan->dma.free < size) {
135 /* reset counter as long as GET is still advancing, this is
136 * to avoid misdetecting a GPU lockup if the GPU happens to
137 * just be processing an operation that takes a long time
138 */
139 get_valid = READ_GET(chan, &get);
140 if (get != prev_get) {
141 prev_get = get;
142 cnt = 0;
143 }
144
145 if ((++cnt & 0xff) == 0) {
146 DRM_UDELAY(1);
147 if (cnt > 100000)
148 return -EBUSY;
149 }
150
151 /* loop until we have a usable GET pointer. the value
152 * we read from the GPU may be outside the main ring if
153 * PFIFO is processing a buffer called from the main ring,
154 * discard these values until something sensible is seen.
155 *
156 * the other case we discard GET is while the GPU is fetching
157 * from the SKIPS area, so the code below doesn't have to deal
158 * with some fun corner cases.
159 */
160 if (!get_valid || get < NOUVEAU_DMA_SKIPS)
161 continue;
162
163 if (get <= chan->dma.cur) {
164 /* engine is fetching behind us, or is completely
165 * idle (GET == PUT) so we have free space up until
166 * the end of the push buffer
167 *
168 * we can only hit that path once per call due to
169 * looping back to the beginning of the push buffer,
170 * we'll hit the fetching-ahead-of-us path from that
171 * point on.
172 *
173 * the *one* exception to that rule is if we read
174 * GET==PUT, in which case the below conditional will
175 * always succeed and break us out of the wait loop.
176 */
177 chan->dma.free = chan->dma.max - chan->dma.cur;
178 if (chan->dma.free >= size)
179 break;
180
181 /* not enough space left at the end of the push buffer,
182 * instruct the GPU to jump back to the start right
183 * after processing the currently pending commands.
184 */
185 OUT_RING(chan, chan->pushbuf_base | 0x20000000);
186 WRITE_PUT(NOUVEAU_DMA_SKIPS);
187
188 /* we're now submitting commands at the start of
189 * the push buffer.
190 */
191 chan->dma.cur =
192 chan->dma.put = NOUVEAU_DMA_SKIPS;
193 }
194
195 /* engine fetching ahead of us, we have space up until the
196 * current GET pointer. the "- 1" is to ensure there's
197 * space left to emit a jump back to the beginning of the
198 * push buffer if we require it. we can never get GET == PUT
199 * here, so this is safe.
200 */
201 chan->dma.free = get - chan->dma.cur - 1;
202 }
203
204 return 0;
205}
206