xref: /netbsd-src/sys/external/bsd/drm2/drm/drm_agp_hook.c (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
1 /*	$NetBSD: drm_agp_hook.c,v 1.3 2018/08/30 22:39:54 mrg 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.3 2018/08/30 22:39:54 mrg 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/drmP.h>
42 #include <drm/drm_internal.h>
43 
44 static struct {
45 	kmutex_t			lock;
46 	kcondvar_t			cv;
47 	unsigned			refcnt; /* at most one per device */
48 	const struct drm_agp_hooks	*hooks;
49 } agp_hooks __cacheline_aligned;
50 
51 void
52 drm_agp_hooks_init(void)
53 {
54 
55 	mutex_init(&agp_hooks.lock, MUTEX_DEFAULT, IPL_NONE);
56 	cv_init(&agp_hooks.cv, "agphooks");
57 	agp_hooks.refcnt = 0;
58 	agp_hooks.hooks = NULL;
59 }
60 
61 void
62 drm_agp_hooks_fini(void)
63 {
64 
65 	KASSERT(agp_hooks.hooks == NULL);
66 	KASSERT(agp_hooks.refcnt == 0);
67 	cv_destroy(&agp_hooks.cv);
68 	mutex_destroy(&agp_hooks.lock);
69 }
70 
71 int
72 drm_agp_register(const struct drm_agp_hooks *hooks)
73 {
74 	int error = 0;
75 
76 	mutex_enter(&agp_hooks.lock);
77 	if (agp_hooks.refcnt) {
78 		KASSERT(agp_hooks.hooks);
79 		error = EBUSY;
80 	} else {
81 		agp_hooks.refcnt++;
82 		agp_hooks.hooks = hooks;
83 	}
84 	mutex_exit(&agp_hooks.lock);
85 
86 	return error;
87 }
88 
89 int
90 drm_agp_deregister(const struct drm_agp_hooks *hooks)
91 {
92 	int error = 0;
93 
94 	mutex_enter(&agp_hooks.lock);
95 	KASSERT(agp_hooks.hooks == hooks);
96 	if (agp_hooks.refcnt > 1) {
97 		error = EBUSY;
98 	} else {
99 		agp_hooks.refcnt = 0;
100 		agp_hooks.hooks = NULL;
101 	}
102 	mutex_exit(&agp_hooks.lock);
103 
104 	return error;
105 }
106 
107 static const struct drm_agp_hooks *
108 drm_agp_hooks_acquire(void)
109 {
110 	const struct drm_agp_hooks *hooks;
111 
112 	mutex_enter(&agp_hooks.lock);
113 	if (agp_hooks.refcnt == 0) {
114 		hooks = NULL;
115 	} else {
116 		KASSERT(agp_hooks.refcnt < UINT_MAX);
117 		agp_hooks.refcnt++;
118 		hooks = agp_hooks.hooks;
119 	}
120 	mutex_exit(&agp_hooks.lock);
121 
122 	return hooks;
123 }
124 
125 static void
126 drm_agp_hooks_release(const struct drm_agp_hooks *hooks)
127 {
128 
129 	mutex_enter(&agp_hooks.lock);
130 	KASSERT(agp_hooks.hooks == hooks);
131 	KASSERT(agp_hooks.refcnt);
132 	if (--agp_hooks.refcnt == 0)
133 		cv_broadcast(&agp_hooks.cv);
134 	mutex_exit(&agp_hooks.lock);
135 }
136 
137 struct drm_agp_head *
138 drm_agp_init(struct drm_device *dev)
139 {
140 	const struct drm_agp_hooks *hooks;
141 	struct drm_agp_head *agp;
142 
143 	if ((hooks = drm_agp_hooks_acquire()) == NULL)
144 		return NULL;
145 	agp = hooks->agph_init(dev);
146 	if (agp == NULL)
147 		drm_agp_hooks_release(hooks);
148 	else
149 		agp->hooks = hooks;
150 
151 	return agp;
152 }
153 
154 void
155 drm_agp_fini(struct drm_device *dev)
156 {
157 
158 	if (dev->agp == NULL)
159 		return;
160 	dev->agp->hooks->agph_clear(dev);
161 	drm_agp_hooks_release(dev->agp->hooks);
162 	kfree(dev->agp);
163 	dev->agp = NULL;
164 }
165 
166 void
167 drm_agp_clear(struct drm_device *dev)
168 {
169 
170 	if (dev->agp == NULL)
171 		return;
172 	dev->agp->hooks->agph_clear(dev);
173 }
174 
175 int
176 drm_agp_acquire(struct drm_device *dev)
177 {
178 
179 	if (dev->agp == NULL)
180 		return -ENODEV;
181 	return dev->agp->hooks->agph_acquire(dev);
182 }
183 
184 int
185 drm_agp_release(struct drm_device *dev)
186 {
187 
188 	if (dev->agp == NULL)
189 		return -EINVAL;
190 	return dev->agp->hooks->agph_release(dev);
191 }
192 
193 int
194 drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode)
195 {
196 
197 	if (dev->agp == NULL)
198 		return -EINVAL;
199 	return dev->agp->hooks->agph_enable(dev, mode);
200 }
201 
202 int
203 drm_agp_info(struct drm_device *dev, struct drm_agp_info *info)
204 {
205 
206 	if (dev->agp == NULL)
207 		return -EINVAL;
208 	return dev->agp->hooks->agph_info(dev, info);
209 }
210 
211 int
212 drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request)
213 {
214 
215 	if (dev->agp == NULL)
216 		return -EINVAL;
217 	return dev->agp->hooks->agph_alloc(dev, request);
218 }
219 
220 int
221 drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request)
222 {
223 
224 	if (dev->agp == NULL)
225 		return -EINVAL;
226 	return dev->agp->hooks->agph_free(dev, request);
227 }
228 
229 int
230 drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request)
231 {
232 
233 	if (dev->agp == NULL)
234 		return -EINVAL;
235 	return dev->agp->hooks->agph_bind(dev, request);
236 }
237 
238 int
239 drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request)
240 {
241 
242 	if (dev->agp == NULL)
243 		return -EINVAL;
244 	return dev->agp->hooks->agph_unbind(dev, request);
245 }
246 
247 #define	DEFINE_AGP_HOOK_IOCTL(NAME, FIELD)				      \
248 int									      \
249 NAME(struct drm_device *dev, void *data, struct drm_file *file)		      \
250 {									      \
251 									      \
252 	if (dev->agp == NULL)						      \
253 		return -ENODEV;						      \
254 	return dev->agp->hooks->FIELD(dev, data, file);			      \
255 }
256 
257 DEFINE_AGP_HOOK_IOCTL(drm_agp_acquire_ioctl, agph_acquire_ioctl)
258 DEFINE_AGP_HOOK_IOCTL(drm_agp_release_ioctl, agph_release_ioctl)
259 DEFINE_AGP_HOOK_IOCTL(drm_agp_enable_ioctl, agph_enable_ioctl)
260 DEFINE_AGP_HOOK_IOCTL(drm_agp_info_ioctl, agph_info_ioctl)
261 DEFINE_AGP_HOOK_IOCTL(drm_agp_alloc_ioctl, agph_alloc_ioctl)
262 DEFINE_AGP_HOOK_IOCTL(drm_agp_free_ioctl, agph_free_ioctl)
263 DEFINE_AGP_HOOK_IOCTL(drm_agp_bind_ioctl, agph_bind_ioctl)
264 DEFINE_AGP_HOOK_IOCTL(drm_agp_unbind_ioctl, agph_unbind_ioctl)
265 
266 void __pci_iomem *
267 drm_agp_borrow(struct drm_device *dev, unsigned bar, bus_size_t size)
268 {
269 	const struct drm_agp_hooks *hooks;
270 	void __pci_iomem *iomem;
271 
272 	if ((hooks = drm_agp_hooks_acquire()) == NULL)
273 		return NULL;
274 	iomem = hooks->agph_borrow(dev, bar, size);
275 	drm_agp_hooks_release(hooks);
276 
277 	return iomem;
278 }
279 
280 void
281 drm_agp_flush(void)
282 {
283 	const struct drm_agp_hooks *hooks;
284 
285 	if ((hooks = drm_agp_hooks_acquire()) == NULL)
286 		return;
287 	hooks->agph_flush();
288 	drm_agp_hooks_release(hooks);
289 }
290