1/*	$NetBSD: drm_agp_hook.c,v 1.7 2022/07/19 22:24:34 riastradh Exp $	*/
2
3/*-
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: drm_agp_hook.c,v 1.7 2022/07/19 22:24:34 riastradh Exp $");
34
35#include <sys/types.h>
36#include <sys/condvar.h>
37#include <sys/errno.h>
38#include <sys/mutex.h>
39#include <sys/once.h>
40
41#include <drm/drm_agpsupport.h>
42#include <drm/drm_drv.h>
43#include "../dist/drm/drm_internal.h"
44
45static struct {
46	kmutex_t			lock;
47	kcondvar_t			cv;
48	unsigned			refcnt; /* at most one per device */
49	const struct drm_agp_hooks	*hooks;
50} agp_hooks __cacheline_aligned;
51
52void
53drm_agp_hooks_init(void)
54{
55
56	mutex_init(&agp_hooks.lock, MUTEX_DEFAULT, IPL_NONE);
57	cv_init(&agp_hooks.cv, "agphooks");
58	agp_hooks.refcnt = 0;
59	agp_hooks.hooks = NULL;
60}
61
62void
63drm_agp_hooks_fini(void)
64{
65
66	KASSERT(agp_hooks.hooks == NULL);
67	KASSERT(agp_hooks.refcnt == 0);
68	cv_destroy(&agp_hooks.cv);
69	mutex_destroy(&agp_hooks.lock);
70}
71
72int
73drm_agp_register(const struct drm_agp_hooks *hooks)
74{
75	int error = 0;
76
77	mutex_enter(&agp_hooks.lock);
78	if (agp_hooks.refcnt) {
79		KASSERT(agp_hooks.hooks);
80		error = EBUSY;
81	} else {
82		agp_hooks.refcnt++;
83		agp_hooks.hooks = hooks;
84	}
85	mutex_exit(&agp_hooks.lock);
86
87	return error;
88}
89
90int
91drm_agp_deregister(const struct drm_agp_hooks *hooks)
92{
93	int error = 0;
94
95	mutex_enter(&agp_hooks.lock);
96	KASSERT(agp_hooks.hooks == hooks);
97	if (agp_hooks.refcnt > 1) {
98		error = EBUSY;
99	} else {
100		agp_hooks.refcnt = 0;
101		agp_hooks.hooks = NULL;
102	}
103	mutex_exit(&agp_hooks.lock);
104
105	return error;
106}
107
108static const struct drm_agp_hooks *
109drm_agp_hooks_acquire(void)
110{
111	const struct drm_agp_hooks *hooks;
112
113	mutex_enter(&agp_hooks.lock);
114	if (agp_hooks.refcnt == 0) {
115		hooks = NULL;
116	} else {
117		KASSERT(agp_hooks.refcnt < UINT_MAX);
118		agp_hooks.refcnt++;
119		hooks = agp_hooks.hooks;
120	}
121	mutex_exit(&agp_hooks.lock);
122
123	return hooks;
124}
125
126static void
127drm_agp_hooks_release(const struct drm_agp_hooks *hooks)
128{
129
130	mutex_enter(&agp_hooks.lock);
131	KASSERT(agp_hooks.hooks == hooks);
132	KASSERT(agp_hooks.refcnt);
133	if (--agp_hooks.refcnt == 0)
134		cv_broadcast(&agp_hooks.cv);
135	mutex_exit(&agp_hooks.lock);
136}
137
138struct drm_agp_head *
139drm_agp_init(struct drm_device *dev)
140{
141	const struct drm_agp_hooks *hooks;
142	struct drm_agp_head *agp;
143
144	if ((hooks = drm_agp_hooks_acquire()) == NULL)
145		return NULL;
146	agp = hooks->agph_init(dev);
147	if (agp == NULL)
148		drm_agp_hooks_release(hooks);
149	else
150		agp->hooks = hooks;
151
152	return agp;
153}
154
155void
156drm_agp_fini(struct drm_device *dev)
157{
158
159	if (dev->agp == NULL)
160		return;
161	dev->agp->hooks->agph_clear(dev);
162	drm_agp_hooks_release(dev->agp->hooks);
163	kfree(dev->agp);
164	dev->agp = NULL;
165}
166
167void
168drm_legacy_agp_clear(struct drm_device *dev)
169{
170
171	if (dev->agp == NULL)
172		return;
173	dev->agp->hooks->agph_clear(dev);
174}
175
176int
177drm_agp_acquire(struct drm_device *dev)
178{
179
180	if (dev->agp == NULL)
181		return -ENODEV;
182	return dev->agp->hooks->agph_acquire(dev);
183}
184
185int
186drm_agp_release(struct drm_device *dev)
187{
188
189	if (dev->agp == NULL)
190		return -EINVAL;
191	return dev->agp->hooks->agph_release(dev);
192}
193
194int
195drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode)
196{
197
198	if (dev->agp == NULL)
199		return -EINVAL;
200	return dev->agp->hooks->agph_enable(dev, mode);
201}
202
203int
204drm_agp_info(struct drm_device *dev, struct drm_agp_info *info)
205{
206
207	if (dev->agp == NULL)
208		return -EINVAL;
209	return dev->agp->hooks->agph_info(dev, info);
210}
211
212int
213drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
214{
215
216	if (dev->agp == NULL)
217		return -EINVAL;
218	return dev->agp->hooks->agph_alloc(dev, request);
219}
220
221int
222drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request)
223{
224
225	if (dev->agp == NULL)
226		return -EINVAL;
227	return dev->agp->hooks->agph_free(dev, request);
228}
229
230int
231drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request)
232{
233
234	if (dev->agp == NULL)
235		return -EINVAL;
236	return dev->agp->hooks->agph_bind(dev, request);
237}
238
239int
240drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request)
241{
242
243	if (dev->agp == NULL)
244		return -EINVAL;
245	return dev->agp->hooks->agph_unbind(dev, request);
246}
247
248#define	DEFINE_AGP_HOOK_IOCTL(NAME, FIELD)				      \
249int									      \
250NAME(struct drm_device *dev, void *data, struct drm_file *file)		      \
251{									      \
252									      \
253	if (dev->agp == NULL)						      \
254		return -ENODEV;						      \
255	return dev->agp->hooks->FIELD(dev, data, file);			      \
256}
257
258DEFINE_AGP_HOOK_IOCTL(drm_agp_acquire_ioctl, agph_acquire_ioctl)
259DEFINE_AGP_HOOK_IOCTL(drm_agp_release_ioctl, agph_release_ioctl)
260DEFINE_AGP_HOOK_IOCTL(drm_agp_enable_ioctl, agph_enable_ioctl)
261DEFINE_AGP_HOOK_IOCTL(drm_agp_info_ioctl, agph_info_ioctl)
262DEFINE_AGP_HOOK_IOCTL(drm_agp_alloc_ioctl, agph_alloc_ioctl)
263DEFINE_AGP_HOOK_IOCTL(drm_agp_free_ioctl, agph_free_ioctl)
264DEFINE_AGP_HOOK_IOCTL(drm_agp_bind_ioctl, agph_bind_ioctl)
265DEFINE_AGP_HOOK_IOCTL(drm_agp_unbind_ioctl, agph_unbind_ioctl)
266
267void
268drm_agp_flush(void)
269{
270	const struct drm_agp_hooks *hooks;
271
272	if ((hooks = drm_agp_hooks_acquire()) == NULL)
273		return;
274	hooks->agph_flush();
275	drm_agp_hooks_release(hooks);
276}
277