1/*	$NetBSD: nouveau_nvkm_core_engine.c,v 1.5 2021/12/19 11:34:44 riastradh Exp $	*/
2
3/*
4 * Copyright 2012 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26#include <sys/cdefs.h>
27__KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_core_engine.c,v 1.5 2021/12/19 11:34:44 riastradh Exp $");
28
29#include <core/engine.h>
30#include <core/device.h>
31#include <core/option.h>
32
33#include <subdev/fb.h>
34
35bool
36nvkm_engine_chsw_load(struct nvkm_engine *engine)
37{
38	if (engine->func->chsw_load)
39		return engine->func->chsw_load(engine);
40	return false;
41}
42
43void
44nvkm_engine_unref(struct nvkm_engine **pengine)
45{
46	struct nvkm_engine *engine = *pengine;
47	if (engine) {
48		mutex_lock(&engine->subdev.mutex);
49		if (--engine->usecount == 0)
50			nvkm_subdev_fini(&engine->subdev, false);
51		mutex_unlock(&engine->subdev.mutex);
52		*pengine = NULL;
53	}
54}
55
56struct nvkm_engine *
57nvkm_engine_ref(struct nvkm_engine *engine)
58{
59	if (engine) {
60		mutex_lock(&engine->subdev.mutex);
61		if (++engine->usecount == 1) {
62			int ret = nvkm_subdev_init(&engine->subdev);
63			if (ret) {
64				engine->usecount--;
65				mutex_unlock(&engine->subdev.mutex);
66				return ERR_PTR(ret);
67			}
68		}
69		mutex_unlock(&engine->subdev.mutex);
70	}
71	return engine;
72}
73
74void
75nvkm_engine_tile(struct nvkm_engine *engine, int region)
76{
77	struct nvkm_fb *fb = engine->subdev.device->fb;
78	if (engine->func->tile)
79		engine->func->tile(engine, region, &fb->tile.region[region]);
80}
81
82static void
83nvkm_engine_intr(struct nvkm_subdev *subdev)
84{
85	struct nvkm_engine *engine = nvkm_engine(subdev);
86	if (engine->func->intr)
87		engine->func->intr(engine);
88}
89
90static int
91nvkm_engine_info(struct nvkm_subdev *subdev, u64 mthd, u64 *data)
92{
93	struct nvkm_engine *engine = nvkm_engine(subdev);
94	if (engine->func->info) {
95		if (!IS_ERR((engine = nvkm_engine_ref(engine)))) {
96			int ret = engine->func->info(engine, mthd, data);
97			nvkm_engine_unref(&engine);
98			return ret;
99		}
100		return PTR_ERR(engine);
101	}
102	return -ENOSYS;
103}
104
105static int
106nvkm_engine_fini(struct nvkm_subdev *subdev, bool suspend)
107{
108	struct nvkm_engine *engine = nvkm_engine(subdev);
109	if (engine->func->fini)
110		return engine->func->fini(engine, suspend);
111	return 0;
112}
113
114static int
115nvkm_engine_init(struct nvkm_subdev *subdev)
116{
117	struct nvkm_engine *engine = nvkm_engine(subdev);
118	struct nvkm_fb *fb = subdev->device->fb;
119	int ret = 0, i;
120	s64 time;
121
122	if (!engine->usecount) {
123		nvkm_trace(subdev, "init skipped, engine has no users\n");
124		return ret;
125	}
126
127	if (engine->func->oneinit && !engine->subdev.oneinit) {
128		nvkm_trace(subdev, "one-time init running...\n");
129		time = ktime_to_us(ktime_get());
130		ret = engine->func->oneinit(engine);
131		if (ret) {
132			nvkm_trace(subdev, "one-time init failed, %d\n", ret);
133			return ret;
134		}
135
136		engine->subdev.oneinit = true;
137		time = ktime_to_us(ktime_get()) - time;
138		nvkm_trace(subdev, "one-time init completed in %"PRId64"us\n", time);
139	}
140
141	if (engine->func->init)
142		ret = engine->func->init(engine);
143
144	for (i = 0; fb && i < fb->tile.regions; i++)
145		nvkm_engine_tile(engine, i);
146	return ret;
147}
148
149static int
150nvkm_engine_preinit(struct nvkm_subdev *subdev)
151{
152	struct nvkm_engine *engine = nvkm_engine(subdev);
153	if (engine->func->preinit)
154		engine->func->preinit(engine);
155	return 0;
156}
157
158static void *
159nvkm_engine_dtor(struct nvkm_subdev *subdev)
160{
161	struct nvkm_engine *engine = nvkm_engine(subdev);
162	spin_lock_destroy(&engine->lock);
163	if (engine->func->dtor)
164		return engine->func->dtor(engine);
165	return engine;
166}
167
168static const struct nvkm_subdev_func
169nvkm_engine_func = {
170	.dtor = nvkm_engine_dtor,
171	.preinit = nvkm_engine_preinit,
172	.init = nvkm_engine_init,
173	.fini = nvkm_engine_fini,
174	.info = nvkm_engine_info,
175	.intr = nvkm_engine_intr,
176};
177
178int
179nvkm_engine_ctor(const struct nvkm_engine_func *func,
180		 struct nvkm_device *device, int index, bool enable,
181		 struct nvkm_engine *engine)
182{
183	nvkm_subdev_ctor(&nvkm_engine_func, device, index, &engine->subdev);
184	engine->func = func;
185
186	if (!nvkm_boolopt(device->cfgopt, nvkm_subdev_name[index], enable)) {
187		nvkm_debug(&engine->subdev, "disabled\n");
188		return -ENODEV;
189	}
190
191	spin_lock_init(&engine->lock);
192	return 0;
193}
194
195int
196nvkm_engine_new_(const struct nvkm_engine_func *func,
197		 struct nvkm_device *device, int index, bool enable,
198		 struct nvkm_engine **pengine)
199{
200	if (!(*pengine = kzalloc(sizeof(**pengine), GFP_KERNEL)))
201		return -ENOMEM;
202	return nvkm_engine_ctor(func, device, index, enable, *pengine);
203}
204