1/*
2 * Copyright 2012 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#include "channv50.h"
25
26#include <core/client.h>
27#include <core/ramht.h>
28#include <subdev/mmu.h>
29#include <subdev/timer.h>
30
31#include <nvif/class.h>
32
33int
34g84_fifo_chan_ntfy(struct nvkm_fifo_chan *chan, u32 type,
35		   struct nvkm_event **pevent)
36{
37	switch (type) {
38	case G82_CHANNEL_DMA_V0_NTFY_UEVENT:
39		*pevent = &chan->fifo->uevent;
40		return 0;
41	default:
42		break;
43	}
44	return -EINVAL;
45}
46
47static int
48g84_fifo_chan_engine(struct nvkm_engine *engine)
49{
50	switch (engine->subdev.index) {
51	case NVKM_ENGINE_GR    : return 0;
52	case NVKM_ENGINE_MPEG  :
53	case NVKM_ENGINE_MSPPP : return 1;
54	case NVKM_ENGINE_CE0   : return 2;
55	case NVKM_ENGINE_VP    :
56	case NVKM_ENGINE_MSPDEC: return 3;
57	case NVKM_ENGINE_CIPHER:
58	case NVKM_ENGINE_SEC   : return 4;
59	case NVKM_ENGINE_BSP   :
60	case NVKM_ENGINE_MSVLD : return 5;
61	default:
62		WARN_ON(1);
63		return 0;
64	}
65}
66
67static int
68g84_fifo_chan_engine_addr(struct nvkm_engine *engine)
69{
70	switch (engine->subdev.index) {
71	case NVKM_ENGINE_DMAOBJ:
72	case NVKM_ENGINE_SW    : return -1;
73	case NVKM_ENGINE_GR    : return 0x0020;
74	case NVKM_ENGINE_VP    :
75	case NVKM_ENGINE_MSPDEC: return 0x0040;
76	case NVKM_ENGINE_MPEG  :
77	case NVKM_ENGINE_MSPPP : return 0x0060;
78	case NVKM_ENGINE_BSP   :
79	case NVKM_ENGINE_MSVLD : return 0x0080;
80	case NVKM_ENGINE_CIPHER:
81	case NVKM_ENGINE_SEC   : return 0x00a0;
82	case NVKM_ENGINE_CE0   : return 0x00c0;
83	default:
84		WARN_ON(1);
85		return -1;
86	}
87}
88
89static int
90g84_fifo_chan_engine_fini(struct nvkm_fifo_chan *base,
91			  struct nvkm_engine *engine, bool suspend)
92{
93	struct nv50_fifo_chan *chan = nv50_fifo_chan(base);
94	struct nv50_fifo *fifo = chan->fifo;
95	struct nvkm_subdev *subdev = &fifo->base.engine.subdev;
96	struct nvkm_device *device = subdev->device;
97	u32 engn, save;
98	int offset;
99	bool done;
100
101	offset = g84_fifo_chan_engine_addr(engine);
102	if (offset < 0)
103		return 0;
104
105	engn = g84_fifo_chan_engine(engine);
106	save = nvkm_mask(device, 0x002520, 0x0000003f, 1 << engn);
107	nvkm_wr32(device, 0x0032fc, chan->base.inst->addr >> 12);
108	done = nvkm_msec(device, 2000,
109		if (nvkm_rd32(device, 0x0032fc) != 0xffffffff)
110			break;
111	) >= 0;
112	nvkm_wr32(device, 0x002520, save);
113	if (!done) {
114		nvkm_error(subdev, "channel %d [%s] unload timeout\n",
115			   chan->base.chid, chan->base.object.client->name);
116		if (suspend)
117			return -EBUSY;
118	}
119
120	nvkm_kmap(chan->eng);
121	nvkm_wo32(chan->eng, offset + 0x00, 0x00000000);
122	nvkm_wo32(chan->eng, offset + 0x04, 0x00000000);
123	nvkm_wo32(chan->eng, offset + 0x08, 0x00000000);
124	nvkm_wo32(chan->eng, offset + 0x0c, 0x00000000);
125	nvkm_wo32(chan->eng, offset + 0x10, 0x00000000);
126	nvkm_wo32(chan->eng, offset + 0x14, 0x00000000);
127	nvkm_done(chan->eng);
128	return 0;
129}
130
131
132int
133g84_fifo_chan_engine_init(struct nvkm_fifo_chan *base,
134			  struct nvkm_engine *engine)
135{
136	struct nv50_fifo_chan *chan = nv50_fifo_chan(base);
137	struct nvkm_gpuobj *engn = chan->engn[engine->subdev.index];
138	u64 limit, start;
139	int offset;
140
141	offset = g84_fifo_chan_engine_addr(engine);
142	if (offset < 0)
143		return 0;
144	limit = engn->addr + engn->size - 1;
145	start = engn->addr;
146
147	nvkm_kmap(chan->eng);
148	nvkm_wo32(chan->eng, offset + 0x00, 0x00190000);
149	nvkm_wo32(chan->eng, offset + 0x04, lower_32_bits(limit));
150	nvkm_wo32(chan->eng, offset + 0x08, lower_32_bits(start));
151	nvkm_wo32(chan->eng, offset + 0x0c, upper_32_bits(limit) << 24 |
152					    upper_32_bits(start));
153	nvkm_wo32(chan->eng, offset + 0x10, 0x00000000);
154	nvkm_wo32(chan->eng, offset + 0x14, 0x00000000);
155	nvkm_done(chan->eng);
156	return 0;
157}
158
159static int
160g84_fifo_chan_engine_ctor(struct nvkm_fifo_chan *base,
161			  struct nvkm_engine *engine,
162			  struct nvkm_object *object)
163{
164	struct nv50_fifo_chan *chan = nv50_fifo_chan(base);
165	int engn = engine->subdev.index;
166
167	if (g84_fifo_chan_engine_addr(engine) < 0)
168		return 0;
169
170	return nvkm_object_bind(object, NULL, 0, &chan->engn[engn]);
171}
172
173int
174g84_fifo_chan_object_ctor(struct nvkm_fifo_chan *base,
175			  struct nvkm_object *object)
176{
177	struct nv50_fifo_chan *chan = nv50_fifo_chan(base);
178	u32 handle = object->handle;
179	u32 context;
180
181	switch (object->engine->subdev.index) {
182	case NVKM_ENGINE_DMAOBJ:
183	case NVKM_ENGINE_SW    : context = 0x00000000; break;
184	case NVKM_ENGINE_GR    : context = 0x00100000; break;
185	case NVKM_ENGINE_MPEG  :
186	case NVKM_ENGINE_MSPPP : context = 0x00200000; break;
187	case NVKM_ENGINE_ME    :
188	case NVKM_ENGINE_CE0   : context = 0x00300000; break;
189	case NVKM_ENGINE_VP    :
190	case NVKM_ENGINE_MSPDEC: context = 0x00400000; break;
191	case NVKM_ENGINE_CIPHER:
192	case NVKM_ENGINE_SEC   :
193	case NVKM_ENGINE_VIC   : context = 0x00500000; break;
194	case NVKM_ENGINE_BSP   :
195	case NVKM_ENGINE_MSVLD : context = 0x00600000; break;
196	default:
197		WARN_ON(1);
198		return -EINVAL;
199	}
200
201	return nvkm_ramht_insert(chan->ramht, object, 0, 4, handle, context);
202}
203
204static void
205g84_fifo_chan_init(struct nvkm_fifo_chan *base)
206{
207	struct nv50_fifo_chan *chan = nv50_fifo_chan(base);
208	struct nv50_fifo *fifo = chan->fifo;
209	struct nvkm_device *device = fifo->base.engine.subdev.device;
210	u64 addr = chan->ramfc->addr >> 8;
211	u32 chid = chan->base.chid;
212
213	nvkm_wr32(device, 0x002600 + (chid * 4), 0x80000000 | addr);
214	nv50_fifo_runlist_update(fifo);
215}
216
217static const struct nvkm_fifo_chan_func
218g84_fifo_chan_func = {
219	.dtor = nv50_fifo_chan_dtor,
220	.init = g84_fifo_chan_init,
221	.fini = nv50_fifo_chan_fini,
222	.ntfy = g84_fifo_chan_ntfy,
223	.engine_ctor = g84_fifo_chan_engine_ctor,
224	.engine_dtor = nv50_fifo_chan_engine_dtor,
225	.engine_init = g84_fifo_chan_engine_init,
226	.engine_fini = g84_fifo_chan_engine_fini,
227	.object_ctor = g84_fifo_chan_object_ctor,
228	.object_dtor = nv50_fifo_chan_object_dtor,
229};
230
231int
232g84_fifo_chan_ctor(struct nv50_fifo *fifo, u64 vm, u64 push,
233		   const struct nvkm_oclass *oclass,
234		   struct nv50_fifo_chan *chan)
235{
236	struct nvkm_device *device = fifo->base.engine.subdev.device;
237	int ret;
238
239	ret = nvkm_fifo_chan_ctor(&g84_fifo_chan_func, &fifo->base,
240				  0x10000, 0x1000, false, vm, push,
241				  (1ULL << NVKM_ENGINE_BSP) |
242				  (1ULL << NVKM_ENGINE_CE0) |
243				  (1ULL << NVKM_ENGINE_CIPHER) |
244				  (1ULL << NVKM_ENGINE_DMAOBJ) |
245				  (1ULL << NVKM_ENGINE_GR) |
246				  (1ULL << NVKM_ENGINE_ME) |
247				  (1ULL << NVKM_ENGINE_MPEG) |
248				  (1ULL << NVKM_ENGINE_MSPDEC) |
249				  (1ULL << NVKM_ENGINE_MSPPP) |
250				  (1ULL << NVKM_ENGINE_MSVLD) |
251				  (1ULL << NVKM_ENGINE_SEC) |
252				  (1ULL << NVKM_ENGINE_SW) |
253				  (1ULL << NVKM_ENGINE_VIC) |
254				  (1ULL << NVKM_ENGINE_VP),
255				  0, 0xc00000, 0x2000, oclass, &chan->base);
256	chan->fifo = fifo;
257	if (ret)
258		return ret;
259
260	ret = nvkm_gpuobj_new(device, 0x0200, 0, true, chan->base.inst,
261			      &chan->eng);
262	if (ret)
263		return ret;
264
265	ret = nvkm_gpuobj_new(device, 0x4000, 0, false, chan->base.inst,
266			      &chan->pgd);
267	if (ret)
268		return ret;
269
270	ret = nvkm_gpuobj_new(device, 0x1000, 0x400, true, chan->base.inst,
271			      &chan->cache);
272	if (ret)
273		return ret;
274
275	ret = nvkm_gpuobj_new(device, 0x100, 0x100, true, chan->base.inst,
276			      &chan->ramfc);
277	if (ret)
278		return ret;
279
280	ret = nvkm_ramht_new(device, 0x8000, 16, chan->base.inst, &chan->ramht);
281	if (ret)
282		return ret;
283
284	return nvkm_vm_ref(chan->base.vm, &chan->vm, chan->pgd);
285}
286