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