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