1 /* $NetBSD: drm_agp_hook.c,v 1.6 2021/12/19 09:52:00 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.6 2021/12/19 09:52:00 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 45 static 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 52 void 53 drm_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 62 void 63 drm_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 72 int 73 drm_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 90 int 91 drm_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 108 static const struct drm_agp_hooks * 109 drm_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 126 static void 127 drm_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 138 struct drm_agp_head * 139 drm_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 155 void 156 drm_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 167 void 168 drm_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 176 int 177 drm_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 185 int 186 drm_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 194 int 195 drm_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 203 int 204 drm_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 212 int 213 drm_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 221 int 222 drm_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 230 int 231 drm_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 239 int 240 drm_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) \ 249 int \ 250 NAME(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 258 DEFINE_AGP_HOOK_IOCTL(drm_agp_acquire_ioctl, agph_acquire_ioctl) 259 DEFINE_AGP_HOOK_IOCTL(drm_agp_release_ioctl, agph_release_ioctl) 260 DEFINE_AGP_HOOK_IOCTL(drm_agp_enable_ioctl, agph_enable_ioctl) 261 DEFINE_AGP_HOOK_IOCTL(drm_agp_info_ioctl, agph_info_ioctl) 262 DEFINE_AGP_HOOK_IOCTL(drm_agp_alloc_ioctl, agph_alloc_ioctl) 263 DEFINE_AGP_HOOK_IOCTL(drm_agp_free_ioctl, agph_free_ioctl) 264 DEFINE_AGP_HOOK_IOCTL(drm_agp_bind_ioctl, agph_bind_ioctl) 265 DEFINE_AGP_HOOK_IOCTL(drm_agp_unbind_ioctl, agph_unbind_ioctl) 266 267 void __pci_iomem * 268 drm_agp_borrow(struct drm_device *dev, unsigned bar, bus_size_t size) 269 { 270 const struct drm_agp_hooks *hooks; 271 void __pci_iomem *iomem; 272 273 if ((hooks = drm_agp_hooks_acquire()) == NULL) 274 return NULL; 275 iomem = hooks->agph_borrow(dev, bar, size); 276 drm_agp_hooks_release(hooks); 277 278 return iomem; 279 } 280 281 void 282 drm_agp_flush(void) 283 { 284 const struct drm_agp_hooks *hooks; 285 286 if ((hooks = drm_agp_hooks_acquire()) == NULL) 287 return; 288 hooks->agph_flush(); 289 drm_agp_hooks_release(hooks); 290 } 291