18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2014 Red Hat Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Authors: Ben Skeggs <bskeggs@redhat.com>
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <nvif/object.h>
268c2ecf20Sopenharmony_ci#include <nvif/client.h>
278c2ecf20Sopenharmony_ci#include <nvif/driver.h>
288c2ecf20Sopenharmony_ci#include <nvif/ioctl.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciint
318c2ecf20Sopenharmony_cinvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct nvif_client *client = object->client;
348c2ecf20Sopenharmony_ci	union {
358c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 v0;
368c2ecf20Sopenharmony_ci	} *args = data;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (size >= sizeof(*args) && args->v0.version == 0) {
398c2ecf20Sopenharmony_ci		if (object != &client->object)
408c2ecf20Sopenharmony_ci			args->v0.object = nvif_handle(object);
418c2ecf20Sopenharmony_ci		else
428c2ecf20Sopenharmony_ci			args->v0.object = 0;
438c2ecf20Sopenharmony_ci		args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY;
448c2ecf20Sopenharmony_ci	} else
458c2ecf20Sopenharmony_ci		return -ENOSYS;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	return client->driver->ioctl(client->object.priv, client->super,
488c2ecf20Sopenharmony_ci				     data, size, hack);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_civoid
528c2ecf20Sopenharmony_cinvif_object_sclass_put(struct nvif_sclass **psclass)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	kfree(*psclass);
558c2ecf20Sopenharmony_ci	*psclass = NULL;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ciint
598c2ecf20Sopenharmony_cinvif_object_sclass_get(struct nvif_object *object, struct nvif_sclass **psclass)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct {
628c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
638c2ecf20Sopenharmony_ci		struct nvif_ioctl_sclass_v0 sclass;
648c2ecf20Sopenharmony_ci	} *args = NULL;
658c2ecf20Sopenharmony_ci	int ret, cnt = 0, i;
668c2ecf20Sopenharmony_ci	u32 size;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	while (1) {
698c2ecf20Sopenharmony_ci		size = sizeof(*args) + cnt * sizeof(args->sclass.oclass[0]);
708c2ecf20Sopenharmony_ci		if (!(args = kmalloc(size, GFP_KERNEL)))
718c2ecf20Sopenharmony_ci			return -ENOMEM;
728c2ecf20Sopenharmony_ci		args->ioctl.version = 0;
738c2ecf20Sopenharmony_ci		args->ioctl.type = NVIF_IOCTL_V0_SCLASS;
748c2ecf20Sopenharmony_ci		args->sclass.version = 0;
758c2ecf20Sopenharmony_ci		args->sclass.count = cnt;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		ret = nvif_object_ioctl(object, args, size, NULL);
788c2ecf20Sopenharmony_ci		if (ret == 0 && args->sclass.count <= cnt)
798c2ecf20Sopenharmony_ci			break;
808c2ecf20Sopenharmony_ci		cnt = args->sclass.count;
818c2ecf20Sopenharmony_ci		kfree(args);
828c2ecf20Sopenharmony_ci		if (ret != 0)
838c2ecf20Sopenharmony_ci			return ret;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	*psclass = kcalloc(args->sclass.count, sizeof(**psclass), GFP_KERNEL);
878c2ecf20Sopenharmony_ci	if (*psclass) {
888c2ecf20Sopenharmony_ci		for (i = 0; i < args->sclass.count; i++) {
898c2ecf20Sopenharmony_ci			(*psclass)[i].oclass = args->sclass.oclass[i].oclass;
908c2ecf20Sopenharmony_ci			(*psclass)[i].minver = args->sclass.oclass[i].minver;
918c2ecf20Sopenharmony_ci			(*psclass)[i].maxver = args->sclass.oclass[i].maxver;
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci		ret = args->sclass.count;
948c2ecf20Sopenharmony_ci	} else {
958c2ecf20Sopenharmony_ci		ret = -ENOMEM;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	kfree(args);
998c2ecf20Sopenharmony_ci	return ret;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ciu32
1038c2ecf20Sopenharmony_cinvif_object_rd(struct nvif_object *object, int size, u64 addr)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct {
1068c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
1078c2ecf20Sopenharmony_ci		struct nvif_ioctl_rd_v0 rd;
1088c2ecf20Sopenharmony_ci	} args = {
1098c2ecf20Sopenharmony_ci		.ioctl.type = NVIF_IOCTL_V0_RD,
1108c2ecf20Sopenharmony_ci		.rd.size = size,
1118c2ecf20Sopenharmony_ci		.rd.addr = addr,
1128c2ecf20Sopenharmony_ci	};
1138c2ecf20Sopenharmony_ci	int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
1148c2ecf20Sopenharmony_ci	if (ret) {
1158c2ecf20Sopenharmony_ci		/*XXX: warn? */
1168c2ecf20Sopenharmony_ci		return 0;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	return args.rd.data;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_civoid
1228c2ecf20Sopenharmony_cinvif_object_wr(struct nvif_object *object, int size, u64 addr, u32 data)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct {
1258c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
1268c2ecf20Sopenharmony_ci		struct nvif_ioctl_wr_v0 wr;
1278c2ecf20Sopenharmony_ci	} args = {
1288c2ecf20Sopenharmony_ci		.ioctl.type = NVIF_IOCTL_V0_WR,
1298c2ecf20Sopenharmony_ci		.wr.size = size,
1308c2ecf20Sopenharmony_ci		.wr.addr = addr,
1318c2ecf20Sopenharmony_ci		.wr.data = data,
1328c2ecf20Sopenharmony_ci	};
1338c2ecf20Sopenharmony_ci	int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL);
1348c2ecf20Sopenharmony_ci	if (ret) {
1358c2ecf20Sopenharmony_ci		/*XXX: warn? */
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciint
1408c2ecf20Sopenharmony_cinvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct {
1438c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
1448c2ecf20Sopenharmony_ci		struct nvif_ioctl_mthd_v0 mthd;
1458c2ecf20Sopenharmony_ci	} *args;
1468c2ecf20Sopenharmony_ci	u8 stack[128];
1478c2ecf20Sopenharmony_ci	int ret;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (sizeof(*args) + size > sizeof(stack)) {
1508c2ecf20Sopenharmony_ci		if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL)))
1518c2ecf20Sopenharmony_ci			return -ENOMEM;
1528c2ecf20Sopenharmony_ci	} else {
1538c2ecf20Sopenharmony_ci		args = (void *)stack;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci	args->ioctl.version = 0;
1568c2ecf20Sopenharmony_ci	args->ioctl.type = NVIF_IOCTL_V0_MTHD;
1578c2ecf20Sopenharmony_ci	args->mthd.version = 0;
1588c2ecf20Sopenharmony_ci	args->mthd.method = mthd;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	memcpy(args->mthd.data, data, size);
1618c2ecf20Sopenharmony_ci	ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL);
1628c2ecf20Sopenharmony_ci	memcpy(data, args->mthd.data, size);
1638c2ecf20Sopenharmony_ci	if (args != (void *)stack)
1648c2ecf20Sopenharmony_ci		kfree(args);
1658c2ecf20Sopenharmony_ci	return ret;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_civoid
1698c2ecf20Sopenharmony_cinvif_object_unmap_handle(struct nvif_object *object)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct {
1728c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
1738c2ecf20Sopenharmony_ci		struct nvif_ioctl_unmap unmap;
1748c2ecf20Sopenharmony_ci	} args = {
1758c2ecf20Sopenharmony_ci		.ioctl.type = NVIF_IOCTL_V0_UNMAP,
1768c2ecf20Sopenharmony_ci	};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	nvif_object_ioctl(object, &args, sizeof(args), NULL);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ciint
1828c2ecf20Sopenharmony_cinvif_object_map_handle(struct nvif_object *object, void *argv, u32 argc,
1838c2ecf20Sopenharmony_ci		       u64 *handle, u64 *length)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct {
1868c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
1878c2ecf20Sopenharmony_ci		struct nvif_ioctl_map_v0 map;
1888c2ecf20Sopenharmony_ci	} *args;
1898c2ecf20Sopenharmony_ci	u32 argn = sizeof(*args) + argc;
1908c2ecf20Sopenharmony_ci	int ret, maptype;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!(args = kzalloc(argn, GFP_KERNEL)))
1938c2ecf20Sopenharmony_ci		return -ENOMEM;
1948c2ecf20Sopenharmony_ci	args->ioctl.type = NVIF_IOCTL_V0_MAP;
1958c2ecf20Sopenharmony_ci	memcpy(args->map.data, argv, argc);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = nvif_object_ioctl(object, args, argn, NULL);
1988c2ecf20Sopenharmony_ci	*handle = args->map.handle;
1998c2ecf20Sopenharmony_ci	*length = args->map.length;
2008c2ecf20Sopenharmony_ci	maptype = args->map.type;
2018c2ecf20Sopenharmony_ci	kfree(args);
2028c2ecf20Sopenharmony_ci	return ret ? ret : (maptype == NVIF_IOCTL_MAP_V0_IO);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_civoid
2068c2ecf20Sopenharmony_cinvif_object_unmap(struct nvif_object *object)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct nvif_client *client = object->client;
2098c2ecf20Sopenharmony_ci	if (object->map.ptr) {
2108c2ecf20Sopenharmony_ci		if (object->map.size) {
2118c2ecf20Sopenharmony_ci			client->driver->unmap(client, object->map.ptr,
2128c2ecf20Sopenharmony_ci						      object->map.size);
2138c2ecf20Sopenharmony_ci			object->map.size = 0;
2148c2ecf20Sopenharmony_ci		}
2158c2ecf20Sopenharmony_ci		object->map.ptr = NULL;
2168c2ecf20Sopenharmony_ci		nvif_object_unmap_handle(object);
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciint
2218c2ecf20Sopenharmony_cinvif_object_map(struct nvif_object *object, void *argv, u32 argc)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct nvif_client *client = object->client;
2248c2ecf20Sopenharmony_ci	u64 handle, length;
2258c2ecf20Sopenharmony_ci	int ret = nvif_object_map_handle(object, argv, argc, &handle, &length);
2268c2ecf20Sopenharmony_ci	if (ret >= 0) {
2278c2ecf20Sopenharmony_ci		if (ret) {
2288c2ecf20Sopenharmony_ci			object->map.ptr = client->driver->map(client,
2298c2ecf20Sopenharmony_ci							      handle,
2308c2ecf20Sopenharmony_ci							      length);
2318c2ecf20Sopenharmony_ci			if (ret = -ENOMEM, object->map.ptr) {
2328c2ecf20Sopenharmony_ci				object->map.size = length;
2338c2ecf20Sopenharmony_ci				return 0;
2348c2ecf20Sopenharmony_ci			}
2358c2ecf20Sopenharmony_ci		} else {
2368c2ecf20Sopenharmony_ci			object->map.ptr = (void *)(unsigned long)handle;
2378c2ecf20Sopenharmony_ci			return 0;
2388c2ecf20Sopenharmony_ci		}
2398c2ecf20Sopenharmony_ci		nvif_object_unmap_handle(object);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci	return ret;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_civoid
2458c2ecf20Sopenharmony_cinvif_object_dtor(struct nvif_object *object)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct {
2488c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
2498c2ecf20Sopenharmony_ci		struct nvif_ioctl_del del;
2508c2ecf20Sopenharmony_ci	} args = {
2518c2ecf20Sopenharmony_ci		.ioctl.type = NVIF_IOCTL_V0_DEL,
2528c2ecf20Sopenharmony_ci	};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (!object->client)
2558c2ecf20Sopenharmony_ci		return;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	nvif_object_unmap(object);
2588c2ecf20Sopenharmony_ci	nvif_object_ioctl(object, &args, sizeof(args), NULL);
2598c2ecf20Sopenharmony_ci	object->client = NULL;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciint
2638c2ecf20Sopenharmony_cinvif_object_ctor(struct nvif_object *parent, const char *name, u32 handle,
2648c2ecf20Sopenharmony_ci		 s32 oclass, void *data, u32 size, struct nvif_object *object)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct {
2678c2ecf20Sopenharmony_ci		struct nvif_ioctl_v0 ioctl;
2688c2ecf20Sopenharmony_ci		struct nvif_ioctl_new_v0 new;
2698c2ecf20Sopenharmony_ci	} *args;
2708c2ecf20Sopenharmony_ci	int ret = 0;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	object->client = NULL;
2738c2ecf20Sopenharmony_ci	object->name = name ? name : "nvifObject";
2748c2ecf20Sopenharmony_ci	object->handle = handle;
2758c2ecf20Sopenharmony_ci	object->oclass = oclass;
2768c2ecf20Sopenharmony_ci	object->map.ptr = NULL;
2778c2ecf20Sopenharmony_ci	object->map.size = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (parent) {
2808c2ecf20Sopenharmony_ci		if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) {
2818c2ecf20Sopenharmony_ci			nvif_object_dtor(object);
2828c2ecf20Sopenharmony_ci			return -ENOMEM;
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		object->parent = parent->parent;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		args->ioctl.version = 0;
2888c2ecf20Sopenharmony_ci		args->ioctl.type = NVIF_IOCTL_V0_NEW;
2898c2ecf20Sopenharmony_ci		args->new.version = 0;
2908c2ecf20Sopenharmony_ci		args->new.route = parent->client->route;
2918c2ecf20Sopenharmony_ci		args->new.token = nvif_handle(object);
2928c2ecf20Sopenharmony_ci		args->new.object = nvif_handle(object);
2938c2ecf20Sopenharmony_ci		args->new.handle = handle;
2948c2ecf20Sopenharmony_ci		args->new.oclass = oclass;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		memcpy(args->new.data, data, size);
2978c2ecf20Sopenharmony_ci		ret = nvif_object_ioctl(parent, args, sizeof(*args) + size,
2988c2ecf20Sopenharmony_ci					&object->priv);
2998c2ecf20Sopenharmony_ci		memcpy(data, args->new.data, size);
3008c2ecf20Sopenharmony_ci		kfree(args);
3018c2ecf20Sopenharmony_ci		if (ret == 0)
3028c2ecf20Sopenharmony_ci			object->client = parent->client;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (ret)
3068c2ecf20Sopenharmony_ci		nvif_object_dtor(object);
3078c2ecf20Sopenharmony_ci	return ret;
3088c2ecf20Sopenharmony_ci}
309