blob: 6339a3d00363386fb000c387e5debe03702e9b96 [file] [log] [blame]
Ben Skeggs26f6d882011-07-04 16:25:18 +10001/*
2 * Copyright 2011 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
Ben Skeggs51beb422011-07-05 10:33:08 +100025#include <linux/dma-mapping.h>
Ben Skeggs26f6d882011-07-04 16:25:18 +100026#include "drmP.h"
27
28#include "nouveau_drv.h"
29#include "nouveau_connector.h"
30#include "nouveau_encoder.h"
31#include "nouveau_crtc.h"
32
Ben Skeggsefd272a2011-07-05 11:58:58 +100033#define MEM_SYNC 0xe0000001
34#define MEM_VRAM 0xe0010000
35
Ben Skeggs26f6d882011-07-04 16:25:18 +100036struct nvd0_display {
37 struct nouveau_gpuobj *mem;
Ben Skeggs51beb422011-07-05 10:33:08 +100038 struct {
39 dma_addr_t handle;
40 u32 *ptr;
41 } evo[1];
Ben Skeggs26f6d882011-07-04 16:25:18 +100042};
43
44static struct nvd0_display *
45nvd0_display(struct drm_device *dev)
46{
47 struct drm_nouveau_private *dev_priv = dev->dev_private;
48 return dev_priv->engine.display.priv;
49}
50
Ben Skeggs51beb422011-07-05 10:33:08 +100051static int
52evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data)
53{
54 int ret = 0;
55 nv_mask(dev, 0x610700 + (id * 0x10), 0x00000001, 0x00000001);
56 nv_wr32(dev, 0x610704 + (id * 0x10), data);
57 nv_mask(dev, 0x610704 + (id * 0x10), 0x80000ffc, 0x80000000 | mthd);
58 if (!nv_wait(dev, 0x610704 + (id * 0x10), 0x80000000, 0x00000000))
59 ret = -EBUSY;
60 nv_mask(dev, 0x610700 + (id * 0x10), 0x00000001, 0x00000000);
61 return ret;
62}
63
64static u32 *
65evo_wait(struct drm_device *dev, int id, int nr)
66{
67 struct nvd0_display *disp = nvd0_display(dev);
68 u32 put = nv_rd32(dev, 0x640000 + (id * 0x1000)) / 4;
69
70 if (put + nr >= (PAGE_SIZE / 4)) {
71 disp->evo[id].ptr[put] = 0x20000000;
72
73 nv_wr32(dev, 0x640000 + (id * 0x1000), 0x00000000);
74 if (!nv_wait(dev, 0x640004 + (id * 0x1000), ~0, 0x00000000)) {
75 NV_ERROR(dev, "evo %d dma stalled\n", id);
76 return NULL;
77 }
78
79 put = 0;
80 }
81
82 return disp->evo[id].ptr + put;
83}
84
85static void
86evo_kick(u32 *push, struct drm_device *dev, int id)
87{
88 struct nvd0_display *disp = nvd0_display(dev);
89 nv_wr32(dev, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2);
90}
91
92#define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m))
93#define evo_data(p,d) *((p)++) = (d)
94
Ben Skeggs26f6d882011-07-04 16:25:18 +100095/******************************************************************************
96 * DAC
97 *****************************************************************************/
98
99/******************************************************************************
100 * SOR
101 *****************************************************************************/
102
103/******************************************************************************
104 * IRQ
105 *****************************************************************************/
Ben Skeggs46005222011-07-05 11:01:13 +1000106static void
107nvd0_display_intr(struct drm_device *dev)
108{
109 u32 intr = nv_rd32(dev, 0x610088);
110
111 if (intr & 0x00000002) {
112 u32 stat = nv_rd32(dev, 0x61009c);
113 int chid = ffs(stat) - 1;
114 if (chid >= 0) {
115 u32 mthd = nv_rd32(dev, 0x6101f0 + (chid * 12));
116 u32 data = nv_rd32(dev, 0x6101f4 + (chid * 12));
117 u32 unkn = nv_rd32(dev, 0x6101f8 + (chid * 12));
118
119 NV_INFO(dev, "EvoCh: chid %d mthd 0x%04x data 0x%08x "
120 "0x%08x 0x%08x\n",
121 chid, (mthd & 0x0000ffc), data, mthd, unkn);
122 nv_wr32(dev, 0x61009c, (1 << chid));
123 nv_wr32(dev, 0x6101f0 + (chid * 12), 0x90000000);
124 }
125
126 intr &= ~0x00000002;
127 }
128
129 if (intr & 0x01000000) {
130 u32 stat = nv_rd32(dev, 0x6100bc);
131 nv_wr32(dev, 0x6100bc, stat);
132 intr &= ~0x01000000;
133 }
134
135 if (intr & 0x02000000) {
136 u32 stat = nv_rd32(dev, 0x6108bc);
137 nv_wr32(dev, 0x6108bc, stat);
138 intr &= ~0x02000000;
139 }
140
141 if (intr)
142 NV_INFO(dev, "PDISP: unknown intr 0x%08x\n", intr);
143}
Ben Skeggs26f6d882011-07-04 16:25:18 +1000144
145/******************************************************************************
146 * Init
147 *****************************************************************************/
148static void
149nvd0_display_fini(struct drm_device *dev)
150{
151 int i;
152
153 /* fini cursors */
154 for (i = 14; i >= 13; i--) {
155 if (!(nv_rd32(dev, 0x610490 + (i * 0x10)) & 0x00000001))
156 continue;
157
158 nv_mask(dev, 0x610490 + (i * 0x10), 0x00000001, 0x00000000);
159 nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00000000);
160 nv_mask(dev, 0x610090, 1 << i, 0x00000000);
161 nv_mask(dev, 0x6100a0, 1 << i, 0x00000000);
162 }
163
164 /* fini master */
165 if (nv_rd32(dev, 0x610490) & 0x00000010) {
166 nv_mask(dev, 0x610490, 0x00000010, 0x00000000);
167 nv_mask(dev, 0x610490, 0x00000003, 0x00000000);
168 nv_wait(dev, 0x610490, 0x80000000, 0x00000000);
169 nv_mask(dev, 0x610090, 0x00000001, 0x00000000);
170 nv_mask(dev, 0x6100a0, 0x00000001, 0x00000000);
171 }
172}
173
174int
175nvd0_display_init(struct drm_device *dev)
176{
177 struct nvd0_display *disp = nvd0_display(dev);
Ben Skeggsefd272a2011-07-05 11:58:58 +1000178 u32 *push;
Ben Skeggs26f6d882011-07-04 16:25:18 +1000179 int i;
180
181 if (nv_rd32(dev, 0x6100ac) & 0x00000100) {
182 nv_wr32(dev, 0x6100ac, 0x00000100);
183 nv_mask(dev, 0x6194e8, 0x00000001, 0x00000000);
184 if (!nv_wait(dev, 0x6194e8, 0x00000002, 0x00000000)) {
185 NV_ERROR(dev, "PDISP: 0x6194e8 0x%08x\n",
186 nv_rd32(dev, 0x6194e8));
187 return -EBUSY;
188 }
189 }
190
191 nv_wr32(dev, 0x610010, (disp->mem->vinst >> 8) | 9);
192
193 /* init master */
Ben Skeggs51beb422011-07-05 10:33:08 +1000194 nv_wr32(dev, 0x610494, (disp->evo[0].handle >> 8) | 3);
Ben Skeggs26f6d882011-07-04 16:25:18 +1000195 nv_wr32(dev, 0x610498, 0x00010000);
Ben Skeggsefd272a2011-07-05 11:58:58 +1000196 nv_wr32(dev, 0x61049c, 0x00000001);
Ben Skeggs26f6d882011-07-04 16:25:18 +1000197 nv_mask(dev, 0x610490, 0x00000010, 0x00000010);
198 nv_wr32(dev, 0x640000, 0x00000000);
199 nv_wr32(dev, 0x610490, 0x01000013);
200 if (!nv_wait(dev, 0x610490, 0x80000000, 0x00000000)) {
201 NV_ERROR(dev, "PDISP: master 0x%08x\n",
202 nv_rd32(dev, 0x610490));
203 return -EBUSY;
204 }
205 nv_mask(dev, 0x610090, 0x00000001, 0x00000001);
206 nv_mask(dev, 0x6100a0, 0x00000001, 0x00000001);
207
208 /* init cursors */
209 for (i = 13; i <= 14; i++) {
210 nv_wr32(dev, 0x610490 + (i * 0x10), 0x00000001);
211 if (!nv_wait(dev, 0x610490 + (i * 0x10), 0x00010000, 0x00010000)) {
212 NV_ERROR(dev, "PDISP: curs%d 0x%08x\n", i,
213 nv_rd32(dev, 0x610490 + (i * 0x10)));
214 return -EBUSY;
215 }
216
217 nv_mask(dev, 0x610090, 1 << i, 1 << i);
218 nv_mask(dev, 0x6100a0, 1 << i, 1 << i);
219 }
220
Ben Skeggsefd272a2011-07-05 11:58:58 +1000221 push = evo_wait(dev, 0, 32);
222 if (!push)
223 return -EBUSY;
224 evo_mthd(push, 0x0088, 1);
225 evo_data(push, MEM_SYNC);
226 evo_mthd(push, 0x0084, 1);
227 evo_data(push, 0x00000000);
228 evo_mthd(push, 0x0084, 1);
229 evo_data(push, 0x80000000);
230 evo_mthd(push, 0x008c, 1);
231 evo_data(push, 0x00000000);
232 evo_kick(push, dev, 0);
233
Ben Skeggs26f6d882011-07-04 16:25:18 +1000234 return 0;
235}
236
237void
238nvd0_display_destroy(struct drm_device *dev)
239{
240 struct drm_nouveau_private *dev_priv = dev->dev_private;
241 struct nvd0_display *disp = nvd0_display(dev);
Ben Skeggs51beb422011-07-05 10:33:08 +1000242 struct pci_dev *pdev = dev->pdev;
Ben Skeggs26f6d882011-07-04 16:25:18 +1000243
244 nvd0_display_fini(dev);
245
Ben Skeggs51beb422011-07-05 10:33:08 +1000246 pci_free_consistent(pdev, PAGE_SIZE, disp->evo[0].ptr, disp->evo[0].handle);
Ben Skeggs26f6d882011-07-04 16:25:18 +1000247 nouveau_gpuobj_ref(NULL, &disp->mem);
Ben Skeggs46005222011-07-05 11:01:13 +1000248 nouveau_irq_unregister(dev, 26);
Ben Skeggs51beb422011-07-05 10:33:08 +1000249
250 dev_priv->engine.display.priv = NULL;
Ben Skeggs26f6d882011-07-04 16:25:18 +1000251 kfree(disp);
252}
253
254int
255nvd0_display_create(struct drm_device *dev)
256{
257 struct drm_nouveau_private *dev_priv = dev->dev_private;
Ben Skeggsefd272a2011-07-05 11:58:58 +1000258 struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
Ben Skeggs51beb422011-07-05 10:33:08 +1000259 struct pci_dev *pdev = dev->pdev;
Ben Skeggs26f6d882011-07-04 16:25:18 +1000260 struct nvd0_display *disp;
261 int ret;
262
263 disp = kzalloc(sizeof(*disp), GFP_KERNEL);
264 if (!disp)
265 return -ENOMEM;
266 dev_priv->engine.display.priv = disp;
267
Ben Skeggs46005222011-07-05 11:01:13 +1000268 /* setup interrupt handling */
269 nouveau_irq_register(dev, 26, nvd0_display_intr);
270
Ben Skeggs51beb422011-07-05 10:33:08 +1000271 /* hash table and dma objects for the memory areas we care about */
Ben Skeggsefd272a2011-07-05 11:58:58 +1000272 ret = nouveau_gpuobj_new(dev, NULL, 0x4000, 0x10000,
273 NVOBJ_FLAG_ZERO_ALLOC, &disp->mem);
Ben Skeggs26f6d882011-07-04 16:25:18 +1000274 if (ret)
275 goto out;
276
Ben Skeggsefd272a2011-07-05 11:58:58 +1000277 nv_wo32(disp->mem, 0x1000, 0x00000049);
278 nv_wo32(disp->mem, 0x1004, (disp->mem->vinst + 0x2000) >> 8);
279 nv_wo32(disp->mem, 0x1008, (disp->mem->vinst + 0x2fff) >> 8);
280 nv_wo32(disp->mem, 0x100c, 0x00000000);
281 nv_wo32(disp->mem, 0x1010, 0x00000000);
282 nv_wo32(disp->mem, 0x1014, 0x00000000);
283 nv_wo32(disp->mem, 0x0000, MEM_SYNC);
284 nv_wo32(disp->mem, 0x0004, (0x1000 << 9) | 0x00000001);
285
286 nv_wo32(disp->mem, 0x1020, 0x00000009);
287 nv_wo32(disp->mem, 0x1024, 0x00000000);
288 nv_wo32(disp->mem, 0x1028, (dev_priv->vram_size - 1) >> 8);
289 nv_wo32(disp->mem, 0x102c, 0x00000000);
290 nv_wo32(disp->mem, 0x1030, 0x00000000);
291 nv_wo32(disp->mem, 0x1034, 0x00000000);
292 nv_wo32(disp->mem, 0x0008, MEM_VRAM);
293 nv_wo32(disp->mem, 0x000c, (0x1020 << 9) | 0x00000001);
294
295 pinstmem->flush(dev);
296
Ben Skeggs51beb422011-07-05 10:33:08 +1000297 /* push buffers for evo channels */
298 disp->evo[0].ptr =
299 pci_alloc_consistent(pdev, PAGE_SIZE, &disp->evo[0].handle);
300 if (!disp->evo[0].ptr) {
301 ret = -ENOMEM;
302 goto out;
303 }
304
Ben Skeggs26f6d882011-07-04 16:25:18 +1000305 ret = nvd0_display_init(dev);
306 if (ret)
307 goto out;
308
309out:
310 if (ret)
311 nvd0_display_destroy(dev);
312 return ret;
313}