xref: /linux/drivers/gpu/drm/nouveau/nouveau_abi16.c (revision fcf3f91c34105c3551741febbfc1066aaa7f1db7)
12a259a3dSBen Skeggs /*
22a259a3dSBen Skeggs  * Copyright 2012 Red Hat Inc.
32a259a3dSBen Skeggs  *
42a259a3dSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
52a259a3dSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
62a259a3dSBen Skeggs  * to deal in the Software without restriction, including without limitation
72a259a3dSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
82a259a3dSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
92a259a3dSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
102a259a3dSBen Skeggs  *
112a259a3dSBen Skeggs  * The above copyright notice and this permission notice shall be included in
122a259a3dSBen Skeggs  * all copies or substantial portions of the Software.
132a259a3dSBen Skeggs  *
142a259a3dSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
152a259a3dSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
162a259a3dSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
172a259a3dSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
182a259a3dSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
192a259a3dSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
202a259a3dSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
212a259a3dSBen Skeggs  *
222a259a3dSBen Skeggs  */
232a259a3dSBen Skeggs 
24a4e610b5SBen Skeggs #include <nvif/client.h>
25a4e610b5SBen Skeggs #include <nvif/driver.h>
26a4e610b5SBen Skeggs #include <nvif/ioctl.h>
27fdb751efSBen Skeggs #include <nvif/class.h>
282a259a3dSBen Skeggs 
29ebb945a9SBen Skeggs #include "nouveau_drm.h"
302a259a3dSBen Skeggs #include "nouveau_dma.h"
31ebb945a9SBen Skeggs #include "nouveau_gem.h"
32ebb945a9SBen Skeggs #include "nouveau_chan.h"
332a259a3dSBen Skeggs #include "nouveau_abi16.h"
34ebb945a9SBen Skeggs 
35ebb945a9SBen Skeggs struct nouveau_abi16 *
36ebb945a9SBen Skeggs nouveau_abi16_get(struct drm_file *file_priv, struct drm_device *dev)
37ebb945a9SBen Skeggs {
38ebb945a9SBen Skeggs 	struct nouveau_cli *cli = nouveau_cli(file_priv);
39ebb945a9SBen Skeggs 	mutex_lock(&cli->mutex);
40ebb945a9SBen Skeggs 	if (!cli->abi16) {
41ebb945a9SBen Skeggs 		struct nouveau_abi16 *abi16;
42ebb945a9SBen Skeggs 		cli->abi16 = abi16 = kzalloc(sizeof(*abi16), GFP_KERNEL);
43ebb945a9SBen Skeggs 		if (cli->abi16) {
44586491e6SBen Skeggs 			struct nv_device_v0 args = {
45586491e6SBen Skeggs 				.device = ~0ULL,
46586491e6SBen Skeggs 			};
47586491e6SBen Skeggs 
48ebb945a9SBen Skeggs 			INIT_LIST_HEAD(&abi16->channels);
49ebb945a9SBen Skeggs 
50ebb945a9SBen Skeggs 			/* allocate device object targeting client's default
51ebb945a9SBen Skeggs 			 * device (ie. the one that belongs to the fd it
52ebb945a9SBen Skeggs 			 * opened)
53ebb945a9SBen Skeggs 			 */
54*fcf3f91cSBen Skeggs 			if (nvif_device_init(&cli->base.object, 0, NV_DEVICE,
55586491e6SBen Skeggs 					     &args, sizeof(args),
560ad72863SBen Skeggs 					     &abi16->device) == 0)
57ebb945a9SBen Skeggs 				return cli->abi16;
58ebb945a9SBen Skeggs 
59ebb945a9SBen Skeggs 			kfree(cli->abi16);
60ebb945a9SBen Skeggs 			cli->abi16 = NULL;
61ebb945a9SBen Skeggs 		}
62ebb945a9SBen Skeggs 
63ebb945a9SBen Skeggs 		mutex_unlock(&cli->mutex);
64ebb945a9SBen Skeggs 	}
65ebb945a9SBen Skeggs 	return cli->abi16;
66ebb945a9SBen Skeggs }
67ebb945a9SBen Skeggs 
68ebb945a9SBen Skeggs int
69ebb945a9SBen Skeggs nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret)
70ebb945a9SBen Skeggs {
71a01ca78cSBen Skeggs 	struct nouveau_cli *cli = (void *)abi16->device.object.client;
72ebb945a9SBen Skeggs 	mutex_unlock(&cli->mutex);
73ebb945a9SBen Skeggs 	return ret;
74ebb945a9SBen Skeggs }
75ebb945a9SBen Skeggs 
76f58ddf95SBen Skeggs s32
77ebb945a9SBen Skeggs nouveau_abi16_swclass(struct nouveau_drm *drm)
78ebb945a9SBen Skeggs {
79967e7bdeSBen Skeggs 	switch (drm->device.info.family) {
80967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_TNT:
81f58ddf95SBen Skeggs 		return NVIF_IOCTL_NEW_V0_SW_NV04;
82967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_CELSIUS:
83967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_KELVIN:
84967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_RANKINE:
85967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_CURIE:
86f58ddf95SBen Skeggs 		return NVIF_IOCTL_NEW_V0_SW_NV10;
87967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_TESLA:
88f58ddf95SBen Skeggs 		return NVIF_IOCTL_NEW_V0_SW_NV50;
89967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_FERMI:
90967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_KEPLER:
91967e7bdeSBen Skeggs 	case NV_DEVICE_INFO_V0_MAXWELL:
92f58ddf95SBen Skeggs 		return NVIF_IOCTL_NEW_V0_SW_GF100;
93ebb945a9SBen Skeggs 	}
94ebb945a9SBen Skeggs 
95ebb945a9SBen Skeggs 	return 0x0000;
96ebb945a9SBen Skeggs }
97ebb945a9SBen Skeggs 
98ebb945a9SBen Skeggs static void
99ebb945a9SBen Skeggs nouveau_abi16_ntfy_fini(struct nouveau_abi16_chan *chan,
100ebb945a9SBen Skeggs 			struct nouveau_abi16_ntfy *ntfy)
101ebb945a9SBen Skeggs {
102a01ca78cSBen Skeggs 	nvif_object_fini(&ntfy->object);
103be83cd4eSBen Skeggs 	nvkm_mm_free(&chan->heap, &ntfy->node);
104ebb945a9SBen Skeggs 	list_del(&ntfy->head);
105ebb945a9SBen Skeggs 	kfree(ntfy);
106ebb945a9SBen Skeggs }
107ebb945a9SBen Skeggs 
108ebb945a9SBen Skeggs static void
109ebb945a9SBen Skeggs nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
110ebb945a9SBen Skeggs 			struct nouveau_abi16_chan *chan)
111ebb945a9SBen Skeggs {
112ebb945a9SBen Skeggs 	struct nouveau_abi16_ntfy *ntfy, *temp;
113ebb945a9SBen Skeggs 
1142b77c1c0SMarcin Slusarz 	/* wait for all activity to stop before releasing notify object, which
1152b77c1c0SMarcin Slusarz 	 * may be still in use */
1162b77c1c0SMarcin Slusarz 	if (chan->chan && chan->ntfy)
1172b77c1c0SMarcin Slusarz 		nouveau_channel_idle(chan->chan);
1182b77c1c0SMarcin Slusarz 
119ebb945a9SBen Skeggs 	/* cleanup notifier state */
120ebb945a9SBen Skeggs 	list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {
121ebb945a9SBen Skeggs 		nouveau_abi16_ntfy_fini(chan, ntfy);
122ebb945a9SBen Skeggs 	}
123ebb945a9SBen Skeggs 
124ebb945a9SBen Skeggs 	if (chan->ntfy) {
125ebb945a9SBen Skeggs 		nouveau_bo_vma_del(chan->ntfy, &chan->ntfy_vma);
126198c14a0SMaarten Lankhorst 		nouveau_bo_unpin(chan->ntfy);
12755fb74adSDavid Herrmann 		drm_gem_object_unreference_unlocked(&chan->ntfy->gem);
128ebb945a9SBen Skeggs 	}
129ebb945a9SBen Skeggs 
130ebb945a9SBen Skeggs 	if (chan->heap.block_size)
131be83cd4eSBen Skeggs 		nvkm_mm_fini(&chan->heap);
132ebb945a9SBen Skeggs 
133ebb945a9SBen Skeggs 	/* destroy channel object, all children will be killed too */
134ebb945a9SBen Skeggs 	if (chan->chan) {
135fbd58ebdSBen Skeggs 		nouveau_channel_idle(chan->chan);
136ebb945a9SBen Skeggs 		nouveau_channel_del(&chan->chan);
137ebb945a9SBen Skeggs 	}
138ebb945a9SBen Skeggs 
139ebb945a9SBen Skeggs 	list_del(&chan->head);
140ebb945a9SBen Skeggs 	kfree(chan);
141ebb945a9SBen Skeggs }
142ebb945a9SBen Skeggs 
143ebb945a9SBen Skeggs void
144ebb945a9SBen Skeggs nouveau_abi16_fini(struct nouveau_abi16 *abi16)
145ebb945a9SBen Skeggs {
146a01ca78cSBen Skeggs 	struct nouveau_cli *cli = (void *)abi16->device.object.client;
147ebb945a9SBen Skeggs 	struct nouveau_abi16_chan *chan, *temp;
148ebb945a9SBen Skeggs 
149ebb945a9SBen Skeggs 	/* cleanup channels */
150ebb945a9SBen Skeggs 	list_for_each_entry_safe(chan, temp, &abi16->channels, head) {
151ebb945a9SBen Skeggs 		nouveau_abi16_chan_fini(abi16, chan);
152ebb945a9SBen Skeggs 	}
153ebb945a9SBen Skeggs 
154ebb945a9SBen Skeggs 	/* destroy the device object */
1550ad72863SBen Skeggs 	nvif_device_fini(&abi16->device);
156ebb945a9SBen Skeggs 
157ebb945a9SBen Skeggs 	kfree(cli->abi16);
158ebb945a9SBen Skeggs 	cli->abi16 = NULL;
159ebb945a9SBen Skeggs }
1602a259a3dSBen Skeggs 
1612a259a3dSBen Skeggs int
1622a259a3dSBen Skeggs nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS)
1632a259a3dSBen Skeggs {
164fa2bade9SBen Skeggs 	struct nouveau_cli *cli = nouveau_cli(file_priv);
165ebb945a9SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
166967e7bdeSBen Skeggs 	struct nvif_device *device = &drm->device;
167be83cd4eSBen Skeggs 	struct nvkm_gr *gr = nvxx_gr(device);
1682a259a3dSBen Skeggs 	struct drm_nouveau_getparam *getparam = data;
1692a259a3dSBen Skeggs 
1702a259a3dSBen Skeggs 	switch (getparam->param) {
1712a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_CHIPSET_ID:
172967e7bdeSBen Skeggs 		getparam->value = device->info.chipset;
1732a259a3dSBen Skeggs 		break;
1742a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_PCI_VENDOR:
17526c9e8efSBen Skeggs 		if (nvxx_device(device)->func->pci)
176ffbab09bSVille Syrjälä 			getparam->value = dev->pdev->vendor;
177420b9469SAlexandre Courbot 		else
178420b9469SAlexandre Courbot 			getparam->value = 0;
1792a259a3dSBen Skeggs 		break;
1802a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_PCI_DEVICE:
18126c9e8efSBen Skeggs 		if (nvxx_device(device)->func->pci)
182ffbab09bSVille Syrjälä 			getparam->value = dev->pdev->device;
183420b9469SAlexandre Courbot 		else
184420b9469SAlexandre Courbot 			getparam->value = 0;
1852a259a3dSBen Skeggs 		break;
1862a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_BUS_TYPE:
18726c9e8efSBen Skeggs 		if (!nvxx_device(device)->func->pci)
188420b9469SAlexandre Courbot 			getparam->value = 3;
189420b9469SAlexandre Courbot 		else
1902a259a3dSBen Skeggs 		if (drm_pci_device_is_agp(dev))
1912a259a3dSBen Skeggs 			getparam->value = 0;
1922a259a3dSBen Skeggs 		else
1932a259a3dSBen Skeggs 		if (!pci_is_pcie(dev->pdev))
1942a259a3dSBen Skeggs 			getparam->value = 1;
1952a259a3dSBen Skeggs 		else
1962a259a3dSBen Skeggs 			getparam->value = 2;
1972a259a3dSBen Skeggs 		break;
1982a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_FB_SIZE:
199ebb945a9SBen Skeggs 		getparam->value = drm->gem.vram_available;
2002a259a3dSBen Skeggs 		break;
2012a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_AGP_SIZE:
202ebb945a9SBen Skeggs 		getparam->value = drm->gem.gart_available;
2032a259a3dSBen Skeggs 		break;
2042a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_VM_VRAM_BASE:
2052a259a3dSBen Skeggs 		getparam->value = 0; /* deprecated */
2062a259a3dSBen Skeggs 		break;
2072a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_PTIMER_TIME:
20856f67dc1SBen Skeggs 		getparam->value = nvif_device_time(device);
2092a259a3dSBen Skeggs 		break;
2102a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_HAS_BO_USAGE:
2112a259a3dSBen Skeggs 		getparam->value = 1;
2122a259a3dSBen Skeggs 		break;
2132a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
2142a259a3dSBen Skeggs 		getparam->value = 1;
2152a259a3dSBen Skeggs 		break;
2162a259a3dSBen Skeggs 	case NOUVEAU_GETPARAM_GRAPH_UNITS:
217c85ee6caSBen Skeggs 		getparam->value = nvkm_gr_units(gr);
2182a259a3dSBen Skeggs 		break;
2192a259a3dSBen Skeggs 	default:
2209ad97edeSBen Skeggs 		NV_PRINTK(dbg, cli, "unknown parameter %lld\n", getparam->param);
2212a259a3dSBen Skeggs 		return -EINVAL;
2222a259a3dSBen Skeggs 	}
2232a259a3dSBen Skeggs 
2242a259a3dSBen Skeggs 	return 0;
2252a259a3dSBen Skeggs }
2262a259a3dSBen Skeggs 
2272a259a3dSBen Skeggs int
2282a259a3dSBen Skeggs nouveau_abi16_ioctl_setparam(ABI16_IOCTL_ARGS)
2292a259a3dSBen Skeggs {
2302a259a3dSBen Skeggs 	return -EINVAL;
2312a259a3dSBen Skeggs }
2322a259a3dSBen Skeggs 
2332a259a3dSBen Skeggs int
2342a259a3dSBen Skeggs nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS)
2352a259a3dSBen Skeggs {
2362a259a3dSBen Skeggs 	struct drm_nouveau_channel_alloc *init = data;
237ebb945a9SBen Skeggs 	struct nouveau_cli *cli = nouveau_cli(file_priv);
238ebb945a9SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
239ebb945a9SBen Skeggs 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
240ebb945a9SBen Skeggs 	struct nouveau_abi16_chan *chan;
241967e7bdeSBen Skeggs 	struct nvif_device *device;
2422a259a3dSBen Skeggs 	int ret;
2432a259a3dSBen Skeggs 
244ebb945a9SBen Skeggs 	if (unlikely(!abi16))
245ebb945a9SBen Skeggs 		return -ENOMEM;
246bf7e438bSMarcin Slusarz 
247bf7e438bSMarcin Slusarz 	if (!drm->channel)
248bf7e438bSMarcin Slusarz 		return nouveau_abi16_put(abi16, -ENODEV);
249bf7e438bSMarcin Slusarz 
250967e7bdeSBen Skeggs 	device = &abi16->device;
251ebb945a9SBen Skeggs 
25249469800SBen Skeggs 	/* hack to allow channel engine type specification on kepler */
253967e7bdeSBen Skeggs 	if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
25449469800SBen Skeggs 		if (init->fb_ctxdma_handle != ~0)
255bbf8906bSBen Skeggs 			init->fb_ctxdma_handle = KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR;
25649469800SBen Skeggs 		else
25749469800SBen Skeggs 			init->fb_ctxdma_handle = init->tt_ctxdma_handle;
25849469800SBen Skeggs 
25949469800SBen Skeggs 		/* allow flips to be executed if this is a graphics channel */
26049469800SBen Skeggs 		init->tt_ctxdma_handle = 0;
261bbf8906bSBen Skeggs 		if (init->fb_ctxdma_handle == KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR)
26249469800SBen Skeggs 			init->tt_ctxdma_handle = 1;
26349469800SBen Skeggs 	}
26449469800SBen Skeggs 
26549469800SBen Skeggs 	if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
26649469800SBen Skeggs 		return nouveau_abi16_put(abi16, -EINVAL);
26749469800SBen Skeggs 
268ebb945a9SBen Skeggs 	/* allocate "abi16 channel" data and make up a handle for it */
269ebb945a9SBen Skeggs 	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
270ebb945a9SBen Skeggs 	if (!chan)
271ebb945a9SBen Skeggs 		return nouveau_abi16_put(abi16, -ENOMEM);
272ebb945a9SBen Skeggs 
273ebb945a9SBen Skeggs 	INIT_LIST_HEAD(&chan->notifiers);
274ebb945a9SBen Skeggs 	list_add(&chan->head, &abi16->channels);
275ebb945a9SBen Skeggs 
276ebb945a9SBen Skeggs 	/* create channel object and initialise dma and fence management */
277*fcf3f91cSBen Skeggs 	ret = nouveau_channel_new(drm, device, init->fb_ctxdma_handle,
278ebb945a9SBen Skeggs 				  init->tt_ctxdma_handle, &chan->chan);
2792a259a3dSBen Skeggs 	if (ret)
280ebb945a9SBen Skeggs 		goto done;
2812a259a3dSBen Skeggs 
282*fcf3f91cSBen Skeggs 	init->channel = chan->chan->chid;
283*fcf3f91cSBen Skeggs 
284967e7bdeSBen Skeggs 	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA)
2852a259a3dSBen Skeggs 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
2862a259a3dSBen Skeggs 					NOUVEAU_GEM_DOMAIN_GART;
287ebb945a9SBen Skeggs 	else
288ebb945a9SBen Skeggs 	if (chan->chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM)
2892a259a3dSBen Skeggs 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
2902a259a3dSBen Skeggs 	else
2912a259a3dSBen Skeggs 		init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
2922a259a3dSBen Skeggs 
293967e7bdeSBen Skeggs 	if (device->info.family < NV_DEVICE_INFO_V0_CELSIUS) {
2942a259a3dSBen Skeggs 		init->subchan[0].handle = 0x00000000;
2952a259a3dSBen Skeggs 		init->subchan[0].grclass = 0x0000;
296f45f55c4SBen Skeggs 		init->subchan[1].handle = chan->chan->nvsw.handle;
297ebb945a9SBen Skeggs 		init->subchan[1].grclass = 0x506e;
2982a259a3dSBen Skeggs 		init->nr_subchan = 2;
2992a259a3dSBen Skeggs 	}
3002a259a3dSBen Skeggs 
3012a259a3dSBen Skeggs 	/* Named memory object area */
302ebb945a9SBen Skeggs 	ret = nouveau_gem_new(dev, PAGE_SIZE, 0, NOUVEAU_GEM_DOMAIN_GART,
303ebb945a9SBen Skeggs 			      0, 0, &chan->ntfy);
3042a259a3dSBen Skeggs 	if (ret == 0)
305ad76b3f7SBen Skeggs 		ret = nouveau_bo_pin(chan->ntfy, TTM_PL_FLAG_TT, false);
306ebb945a9SBen Skeggs 	if (ret)
307ebb945a9SBen Skeggs 		goto done;
308ebb945a9SBen Skeggs 
309967e7bdeSBen Skeggs 	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
3100ad72863SBen Skeggs 		ret = nouveau_bo_vma_add(chan->ntfy, cli->vm,
311ebb945a9SBen Skeggs 					&chan->ntfy_vma);
312ebb945a9SBen Skeggs 		if (ret)
313ebb945a9SBen Skeggs 			goto done;
3142a259a3dSBen Skeggs 	}
3152a259a3dSBen Skeggs 
31655fb74adSDavid Herrmann 	ret = drm_gem_handle_create(file_priv, &chan->ntfy->gem,
317ebb945a9SBen Skeggs 				    &init->notifier_handle);
318ebb945a9SBen Skeggs 	if (ret)
319ebb945a9SBen Skeggs 		goto done;
320ebb945a9SBen Skeggs 
321be83cd4eSBen Skeggs 	ret = nvkm_mm_init(&chan->heap, 0, PAGE_SIZE, 1);
322ebb945a9SBen Skeggs done:
323ebb945a9SBen Skeggs 	if (ret)
324ebb945a9SBen Skeggs 		nouveau_abi16_chan_fini(abi16, chan);
325ebb945a9SBen Skeggs 	return nouveau_abi16_put(abi16, ret);
326ebb945a9SBen Skeggs }
327ebb945a9SBen Skeggs 
328a4e610b5SBen Skeggs static struct nouveau_abi16_chan *
329a4e610b5SBen Skeggs nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel)
330a4e610b5SBen Skeggs {
331a4e610b5SBen Skeggs 	struct nouveau_abi16_chan *chan;
332a4e610b5SBen Skeggs 
333a4e610b5SBen Skeggs 	list_for_each_entry(chan, &abi16->channels, head) {
334*fcf3f91cSBen Skeggs 		if (chan->chan->chid == channel)
335a4e610b5SBen Skeggs 			return chan;
336a4e610b5SBen Skeggs 	}
337a4e610b5SBen Skeggs 
338a4e610b5SBen Skeggs 	return NULL;
339a4e610b5SBen Skeggs }
340ebb945a9SBen Skeggs 
3412a259a3dSBen Skeggs int
3422a259a3dSBen Skeggs nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS)
3432a259a3dSBen Skeggs {
3442a259a3dSBen Skeggs 	struct drm_nouveau_channel_free *req = data;
345ebb945a9SBen Skeggs 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
346ebb945a9SBen Skeggs 	struct nouveau_abi16_chan *chan;
3472a259a3dSBen Skeggs 
348ebb945a9SBen Skeggs 	if (unlikely(!abi16))
349ebb945a9SBen Skeggs 		return -ENOMEM;
3502a259a3dSBen Skeggs 
351a4e610b5SBen Skeggs 	chan = nouveau_abi16_chan(abi16, req->channel);
352a4e610b5SBen Skeggs 	if (!chan)
353a4e610b5SBen Skeggs 		return nouveau_abi16_put(abi16, -ENOENT);
354ebb945a9SBen Skeggs 	nouveau_abi16_chan_fini(abi16, chan);
355ebb945a9SBen Skeggs 	return nouveau_abi16_put(abi16, 0);
356ebb945a9SBen Skeggs }
3572a259a3dSBen Skeggs 
3582a259a3dSBen Skeggs int
3592a259a3dSBen Skeggs nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
3602a259a3dSBen Skeggs {
3612a259a3dSBen Skeggs 	struct drm_nouveau_grobj_alloc *init = data;
362ebb945a9SBen Skeggs 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
363a01ca78cSBen Skeggs 	struct nouveau_abi16_chan *chan;
364a01ca78cSBen Skeggs 	struct nouveau_abi16_ntfy *ntfy;
365a4e610b5SBen Skeggs 	struct nvif_client *client;
36641a63406SBen Skeggs 	struct nvif_sclass *sclass;
367f58ddf95SBen Skeggs 	s32 oclass = 0;
368f58ddf95SBen Skeggs 	int ret, i;
3692a259a3dSBen Skeggs 
370ebb945a9SBen Skeggs 	if (unlikely(!abi16))
371ebb945a9SBen Skeggs 		return -ENOMEM;
372ebb945a9SBen Skeggs 
3732a259a3dSBen Skeggs 	if (init->handle == ~0)
374ebb945a9SBen Skeggs 		return nouveau_abi16_put(abi16, -EINVAL);
375a01ca78cSBen Skeggs 	client = abi16->device.object.client;
3762a259a3dSBen Skeggs 
377a01ca78cSBen Skeggs 	chan = nouveau_abi16_chan(abi16, init->channel);
378a01ca78cSBen Skeggs 	if (!chan)
379a01ca78cSBen Skeggs 		return nouveau_abi16_put(abi16, -ENOENT);
380a01ca78cSBen Skeggs 
38141a63406SBen Skeggs 	ret = nvif_object_sclass_get(&chan->chan->user, &sclass);
382f58ddf95SBen Skeggs 	if (ret < 0)
383f58ddf95SBen Skeggs 		return nouveau_abi16_put(abi16, ret);
384f58ddf95SBen Skeggs 
385f58ddf95SBen Skeggs 	if ((init->class & 0x00ff) == 0x006e) {
386f58ddf95SBen Skeggs 		/* nvsw: compatibility with older 0x*6e class identifier */
387f58ddf95SBen Skeggs 		for (i = 0; !oclass && i < ret; i++) {
38841a63406SBen Skeggs 			switch (sclass[i].oclass) {
389f58ddf95SBen Skeggs 			case NVIF_IOCTL_NEW_V0_SW_NV04:
390f58ddf95SBen Skeggs 			case NVIF_IOCTL_NEW_V0_SW_NV10:
391f58ddf95SBen Skeggs 			case NVIF_IOCTL_NEW_V0_SW_NV50:
392f58ddf95SBen Skeggs 			case NVIF_IOCTL_NEW_V0_SW_GF100:
39341a63406SBen Skeggs 				oclass = sclass[i].oclass;
394f58ddf95SBen Skeggs 				break;
395f58ddf95SBen Skeggs 			default:
396f58ddf95SBen Skeggs 				break;
397f58ddf95SBen Skeggs 			}
398f58ddf95SBen Skeggs 		}
399f58ddf95SBen Skeggs 	} else
400f58ddf95SBen Skeggs 	if ((init->class & 0x00ff) == 0x00b1) {
401f58ddf95SBen Skeggs 		/* msvld: compatibility with incorrect version exposure */
402f58ddf95SBen Skeggs 		for (i = 0; i < ret; i++) {
40341a63406SBen Skeggs 			if ((sclass[i].oclass & 0x00ff) == 0x00b1) {
40441a63406SBen Skeggs 				oclass = sclass[i].oclass;
405f58ddf95SBen Skeggs 				break;
406f58ddf95SBen Skeggs 			}
407f58ddf95SBen Skeggs 		}
408f58ddf95SBen Skeggs 	} else
409f58ddf95SBen Skeggs 	if ((init->class & 0x00ff) == 0x00b2) { /* mspdec */
410f58ddf95SBen Skeggs 		/* mspdec: compatibility with incorrect version exposure */
411f58ddf95SBen Skeggs 		for (i = 0; i < ret; i++) {
41241a63406SBen Skeggs 			if ((sclass[i].oclass & 0x00ff) == 0x00b2) {
41341a63406SBen Skeggs 				oclass = sclass[i].oclass;
414f58ddf95SBen Skeggs 				break;
415f58ddf95SBen Skeggs 			}
416f58ddf95SBen Skeggs 		}
417f58ddf95SBen Skeggs 	} else
418f58ddf95SBen Skeggs 	if ((init->class & 0x00ff) == 0x00b3) { /* msppp */
419f58ddf95SBen Skeggs 		/* msppp: compatibility with incorrect version exposure */
420f58ddf95SBen Skeggs 		for (i = 0; i < ret; i++) {
42141a63406SBen Skeggs 			if ((sclass[i].oclass & 0x00ff) == 0x00b3) {
42241a63406SBen Skeggs 				oclass = sclass[i].oclass;
423f58ddf95SBen Skeggs 				break;
424f58ddf95SBen Skeggs 			}
425f58ddf95SBen Skeggs 		}
426f58ddf95SBen Skeggs 	} else {
427f58ddf95SBen Skeggs 		oclass = init->class;
428f58ddf95SBen Skeggs 	}
429f58ddf95SBen Skeggs 
43041a63406SBen Skeggs 	nvif_object_sclass_put(&sclass);
431f58ddf95SBen Skeggs 	if (!oclass)
432f58ddf95SBen Skeggs 		return nouveau_abi16_put(abi16, -EINVAL);
433f58ddf95SBen Skeggs 
434a01ca78cSBen Skeggs 	ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
435a01ca78cSBen Skeggs 	if (!ntfy)
436a01ca78cSBen Skeggs 		return nouveau_abi16_put(abi16, -ENOMEM);
437a01ca78cSBen Skeggs 
438a01ca78cSBen Skeggs 	list_add(&ntfy->head, &chan->notifiers);
439a01ca78cSBen Skeggs 
440a01ca78cSBen Skeggs 	client->route = NVDRM_OBJECT_ABI16;
441f58ddf95SBen Skeggs 	ret = nvif_object_init(&chan->chan->user, init->handle, oclass,
442a01ca78cSBen Skeggs 			       NULL, 0, &ntfy->object);
443a01ca78cSBen Skeggs 	client->route = NVDRM_OBJECT_NVIF;
444a01ca78cSBen Skeggs 
445a01ca78cSBen Skeggs 	if (ret)
446a01ca78cSBen Skeggs 		nouveau_abi16_ntfy_fini(chan, ntfy);
447ebb945a9SBen Skeggs 	return nouveau_abi16_put(abi16, ret);
4482a259a3dSBen Skeggs }
4492a259a3dSBen Skeggs 
4502a259a3dSBen Skeggs int
4512a259a3dSBen Skeggs nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)
4522a259a3dSBen Skeggs {
453ebb945a9SBen Skeggs 	struct drm_nouveau_notifierobj_alloc *info = data;
454ebb945a9SBen Skeggs 	struct nouveau_drm *drm = nouveau_drm(dev);
455ebb945a9SBen Skeggs 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
456a4e610b5SBen Skeggs 	struct nouveau_abi16_chan *chan;
457ebb945a9SBen Skeggs 	struct nouveau_abi16_ntfy *ntfy;
458967e7bdeSBen Skeggs 	struct nvif_device *device = &abi16->device;
4593bdda04fSBen Skeggs 	struct nvif_client *client;
460a01ca78cSBen Skeggs 	struct nv_dma_v0 args = {};
4612a259a3dSBen Skeggs 	int ret;
4622a259a3dSBen Skeggs 
463ebb945a9SBen Skeggs 	if (unlikely(!abi16))
464ebb945a9SBen Skeggs 		return -ENOMEM;
465ebb945a9SBen Skeggs 
4662a259a3dSBen Skeggs 	/* completely unnecessary for these chipsets... */
467967e7bdeSBen Skeggs 	if (unlikely(device->info.family >= NV_DEVICE_INFO_V0_FERMI))
468ebb945a9SBen Skeggs 		return nouveau_abi16_put(abi16, -EINVAL);
469a01ca78cSBen Skeggs 	client = abi16->device.object.client;
4702a259a3dSBen Skeggs 
471a4e610b5SBen Skeggs 	chan = nouveau_abi16_chan(abi16, info->channel);
472ebb945a9SBen Skeggs 	if (!chan)
473ebb945a9SBen Skeggs 		return nouveau_abi16_put(abi16, -ENOENT);
474ebb945a9SBen Skeggs 
475ebb945a9SBen Skeggs 	ntfy = kzalloc(sizeof(*ntfy), GFP_KERNEL);
476ebb945a9SBen Skeggs 	if (!ntfy)
477ebb945a9SBen Skeggs 		return nouveau_abi16_put(abi16, -ENOMEM);
478ebb945a9SBen Skeggs 
479ebb945a9SBen Skeggs 	list_add(&ntfy->head, &chan->notifiers);
480ebb945a9SBen Skeggs 
481be83cd4eSBen Skeggs 	ret = nvkm_mm_head(&chan->heap, 0, 1, info->size, info->size, 1,
482ebb945a9SBen Skeggs 			   &ntfy->node);
483ebb945a9SBen Skeggs 	if (ret)
484ebb945a9SBen Skeggs 		goto done;
485ebb945a9SBen Skeggs 
486a01ca78cSBen Skeggs 	args.start = ntfy->node->offset;
487a01ca78cSBen Skeggs 	args.limit = ntfy->node->offset + ntfy->node->length - 1;
488967e7bdeSBen Skeggs 	if (device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
489a01ca78cSBen Skeggs 		args.target = NV_DMA_V0_TARGET_VM;
490a01ca78cSBen Skeggs 		args.access = NV_DMA_V0_ACCESS_VM;
491a01ca78cSBen Skeggs 		args.start += chan->ntfy_vma.offset;
492a01ca78cSBen Skeggs 		args.limit += chan->ntfy_vma.offset;
493ebb945a9SBen Skeggs 	} else
494340b0e7cSBen Skeggs 	if (drm->agp.bridge) {
495a01ca78cSBen Skeggs 		args.target = NV_DMA_V0_TARGET_AGP;
496a01ca78cSBen Skeggs 		args.access = NV_DMA_V0_ACCESS_RDWR;
497a01ca78cSBen Skeggs 		args.start += drm->agp.base + chan->ntfy->bo.offset;
498a01ca78cSBen Skeggs 		args.limit += drm->agp.base + chan->ntfy->bo.offset;
499ebb945a9SBen Skeggs 	} else {
500a01ca78cSBen Skeggs 		args.target = NV_DMA_V0_TARGET_VM;
501a01ca78cSBen Skeggs 		args.access = NV_DMA_V0_ACCESS_RDWR;
502a01ca78cSBen Skeggs 		args.start += chan->ntfy->bo.offset;
503a01ca78cSBen Skeggs 		args.limit += chan->ntfy->bo.offset;
504ebb945a9SBen Skeggs 	}
505ebb945a9SBen Skeggs 
506a01ca78cSBen Skeggs 	client->route = NVDRM_OBJECT_ABI16;
507a01ca78cSBen Skeggs 	client->super = true;
508a01ca78cSBen Skeggs 	ret = nvif_object_init(&chan->chan->user, info->handle,
509a01ca78cSBen Skeggs 			       NV_DMA_IN_MEMORY, &args, sizeof(args),
510a01ca78cSBen Skeggs 			       &ntfy->object);
5113bdda04fSBen Skeggs 	client->super = false;
512a01ca78cSBen Skeggs 	client->route = NVDRM_OBJECT_NVIF;
513ebb945a9SBen Skeggs 	if (ret)
514ebb945a9SBen Skeggs 		goto done;
515ebb945a9SBen Skeggs 
516c1ccaa64SBob Gleitsmann 	info->offset = ntfy->node->offset;
517ebb945a9SBen Skeggs done:
518ebb945a9SBen Skeggs 	if (ret)
519ebb945a9SBen Skeggs 		nouveau_abi16_ntfy_fini(chan, ntfy);
520ebb945a9SBen Skeggs 	return nouveau_abi16_put(abi16, ret);
5212a259a3dSBen Skeggs }
5222a259a3dSBen Skeggs 
5232a259a3dSBen Skeggs int
5242a259a3dSBen Skeggs nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)
5252a259a3dSBen Skeggs {
526ebb945a9SBen Skeggs 	struct drm_nouveau_gpuobj_free *fini = data;
527ebb945a9SBen Skeggs 	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev);
528a4e610b5SBen Skeggs 	struct nouveau_abi16_chan *chan;
529ebb945a9SBen Skeggs 	struct nouveau_abi16_ntfy *ntfy;
530a01ca78cSBen Skeggs 	int ret = -ENOENT;
5312a259a3dSBen Skeggs 
532ebb945a9SBen Skeggs 	if (unlikely(!abi16))
533ebb945a9SBen Skeggs 		return -ENOMEM;
5342a259a3dSBen Skeggs 
535a4e610b5SBen Skeggs 	chan = nouveau_abi16_chan(abi16, fini->channel);
536ebb945a9SBen Skeggs 	if (!chan)
537a01ca78cSBen Skeggs 		return nouveau_abi16_put(abi16, -EINVAL);
538ebb945a9SBen Skeggs 
539ebb945a9SBen Skeggs 	/* synchronize with the user channel and destroy the gpu object */
540ebb945a9SBen Skeggs 	nouveau_channel_idle(chan->chan);
541ebb945a9SBen Skeggs 
542ebb945a9SBen Skeggs 	list_for_each_entry(ntfy, &chan->notifiers, head) {
543a01ca78cSBen Skeggs 		if (ntfy->object.handle == fini->handle) {
544a01ca78cSBen Skeggs 			nouveau_abi16_ntfy_fini(chan, ntfy);
545a01ca78cSBen Skeggs 			ret = 0;
546ebb945a9SBen Skeggs 			break;
547ebb945a9SBen Skeggs 		}
548ebb945a9SBen Skeggs 	}
549ebb945a9SBen Skeggs 
550a01ca78cSBen Skeggs 	return nouveau_abi16_put(abi16, ret);
5512a259a3dSBen Skeggs }
552