1 1.14 riastrad /* $NetBSD: i915_pci_autoconf.c,v 1.14 2022/10/15 15:20:06 riastradh Exp $ */ 2 1.1 riastrad 3 1.1 riastrad /*- 4 1.1 riastrad * Copyright (c) 2013 The NetBSD Foundation, Inc. 5 1.1 riastrad * All rights reserved. 6 1.1 riastrad * 7 1.1 riastrad * This code is derived from software contributed to The NetBSD Foundation 8 1.1 riastrad * by Taylor R. Campbell. 9 1.1 riastrad * 10 1.1 riastrad * Redistribution and use in source and binary forms, with or without 11 1.1 riastrad * modification, are permitted provided that the following conditions 12 1.1 riastrad * are met: 13 1.1 riastrad * 1. Redistributions of source code must retain the above copyright 14 1.1 riastrad * notice, this list of conditions and the following disclaimer. 15 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 riastrad * notice, this list of conditions and the following disclaimer in the 17 1.1 riastrad * documentation and/or other materials provided with the distribution. 18 1.1 riastrad * 19 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE. 30 1.1 riastrad */ 31 1.1 riastrad 32 1.1 riastrad #include <sys/cdefs.h> 33 1.14 riastrad __KERNEL_RCSID(0, "$NetBSD: i915_pci_autoconf.c,v 1.14 2022/10/15 15:20:06 riastradh Exp $"); 34 1.1 riastrad 35 1.1 riastrad #include <sys/types.h> 36 1.10 riastrad #include <sys/atomic.h> 37 1.1 riastrad #include <sys/queue.h> 38 1.1 riastrad #include <sys/systm.h> 39 1.1 riastrad #include <sys/queue.h> 40 1.1 riastrad #include <sys/workqueue.h> 41 1.1 riastrad 42 1.14 riastrad #include <drm/drm_ioctl.h> 43 1.5 riastrad #include <drm/drm_pci.h> 44 1.5 riastrad 45 1.1 riastrad #include "i915_drv.h" 46 1.1 riastrad #include "i915_pci.h" 47 1.1 riastrad 48 1.3 riastrad struct drm_device; 49 1.3 riastrad 50 1.1 riastrad SIMPLEQ_HEAD(i915drmkms_task_head, i915drmkms_task); 51 1.1 riastrad 52 1.1 riastrad struct i915drmkms_softc { 53 1.1 riastrad device_t sc_dev; 54 1.1 riastrad struct pci_attach_args sc_pa; 55 1.10 riastrad struct lwp *sc_task_thread; 56 1.10 riastrad struct i915drmkms_task_head sc_tasks; 57 1.10 riastrad struct workqueue *sc_task_wq; 58 1.1 riastrad struct drm_device *sc_drm_dev; 59 1.1 riastrad struct pci_dev sc_pci_dev; 60 1.1 riastrad }; 61 1.1 riastrad 62 1.6 riastrad static const struct pci_device_id * 63 1.1 riastrad i915drmkms_pci_lookup(const struct pci_attach_args *); 64 1.1 riastrad 65 1.1 riastrad static int i915drmkms_match(device_t, cfdata_t, void *); 66 1.1 riastrad static void i915drmkms_attach(device_t, device_t, void *); 67 1.1 riastrad static void i915drmkms_attach_real(device_t); 68 1.1 riastrad static int i915drmkms_detach(device_t, int); 69 1.1 riastrad 70 1.1 riastrad static bool i915drmkms_suspend(device_t, const pmf_qual_t *); 71 1.1 riastrad static bool i915drmkms_resume(device_t, const pmf_qual_t *); 72 1.1 riastrad 73 1.1 riastrad static void i915drmkms_task_work(struct work *, void *); 74 1.1 riastrad 75 1.1 riastrad CFATTACH_DECL_NEW(i915drmkms, sizeof(struct i915drmkms_softc), 76 1.1 riastrad i915drmkms_match, i915drmkms_attach, i915drmkms_detach, NULL); 77 1.1 riastrad 78 1.9 riastrad /* XXX Kludge to get these from i915_pci.c. */ 79 1.1 riastrad extern const struct pci_device_id *const i915_device_ids; 80 1.1 riastrad extern const size_t i915_n_device_ids; 81 1.1 riastrad 82 1.6 riastrad static const struct pci_device_id * 83 1.1 riastrad i915drmkms_pci_lookup(const struct pci_attach_args *pa) 84 1.1 riastrad { 85 1.1 riastrad size_t i; 86 1.1 riastrad 87 1.1 riastrad /* Attach only at function 0 to work around Intel lossage. */ 88 1.1 riastrad if (pa->pa_function != 0) 89 1.1 riastrad return NULL; 90 1.1 riastrad 91 1.1 riastrad /* We're interested only in Intel products. */ 92 1.1 riastrad if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) 93 1.1 riastrad return NULL; 94 1.1 riastrad 95 1.1 riastrad /* We're interested only in Intel display devices. */ 96 1.1 riastrad if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY) 97 1.1 riastrad return NULL; 98 1.1 riastrad 99 1.1 riastrad for (i = 0; i < i915_n_device_ids; i++) 100 1.1 riastrad if (PCI_PRODUCT(pa->pa_id) == i915_device_ids[i].device) 101 1.1 riastrad break; 102 1.1 riastrad 103 1.1 riastrad /* Did we find it? */ 104 1.1 riastrad if (i == i915_n_device_ids) 105 1.1 riastrad return NULL; 106 1.1 riastrad 107 1.6 riastrad const struct pci_device_id *ent = &i915_device_ids[i]; 108 1.11 riastrad const struct intel_device_info *const info = 109 1.11 riastrad (struct intel_device_info *)ent->driver_data; 110 1.1 riastrad 111 1.5 riastrad if (info->require_force_probe) { 112 1.1 riastrad printf("i915drmkms: preliminary hardware support disabled\n"); 113 1.1 riastrad return NULL; 114 1.1 riastrad } 115 1.1 riastrad 116 1.6 riastrad return ent; 117 1.1 riastrad } 118 1.1 riastrad 119 1.1 riastrad static int 120 1.1 riastrad i915drmkms_match(device_t parent, cfdata_t match, void *aux) 121 1.1 riastrad { 122 1.1 riastrad extern int i915drmkms_guarantee_initialized(void); 123 1.1 riastrad const struct pci_attach_args *const pa = aux; 124 1.1 riastrad int error; 125 1.1 riastrad 126 1.1 riastrad error = i915drmkms_guarantee_initialized(); 127 1.1 riastrad if (error) { 128 1.1 riastrad aprint_error("i915drmkms: failed to initialize: %d\n", error); 129 1.1 riastrad return 0; 130 1.1 riastrad } 131 1.1 riastrad 132 1.1 riastrad if (i915drmkms_pci_lookup(pa) == NULL) 133 1.1 riastrad return 0; 134 1.1 riastrad 135 1.1 riastrad return 6; /* XXX Beat genfb_pci... */ 136 1.1 riastrad } 137 1.1 riastrad 138 1.1 riastrad static void 139 1.1 riastrad i915drmkms_attach(device_t parent, device_t self, void *aux) 140 1.1 riastrad { 141 1.1 riastrad struct i915drmkms_softc *const sc = device_private(self); 142 1.1 riastrad const struct pci_attach_args *const pa = aux; 143 1.10 riastrad int error; 144 1.1 riastrad 145 1.1 riastrad pci_aprint_devinfo(pa, NULL); 146 1.1 riastrad 147 1.10 riastrad /* Initialize the Linux PCI device descriptor. */ 148 1.10 riastrad linux_pci_dev_init(&sc->sc_pci_dev, self, parent, pa, 0); 149 1.1 riastrad 150 1.1 riastrad sc->sc_dev = self; 151 1.1 riastrad sc->sc_pa = *pa; 152 1.10 riastrad sc->sc_task_thread = NULL; 153 1.10 riastrad SIMPLEQ_INIT(&sc->sc_tasks); 154 1.10 riastrad error = workqueue_create(&sc->sc_task_wq, "intelfb", 155 1.10 riastrad &i915drmkms_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE); 156 1.10 riastrad if (error) { 157 1.10 riastrad aprint_error_dev(self, "unable to create workqueue: %d\n", 158 1.10 riastrad error); 159 1.10 riastrad sc->sc_task_wq = NULL; 160 1.10 riastrad return; 161 1.10 riastrad } 162 1.1 riastrad 163 1.10 riastrad /* 164 1.10 riastrad * Defer the remainder of initialization until we have mounted 165 1.10 riastrad * the root file system and can load firmware images. 166 1.10 riastrad */ 167 1.1 riastrad config_mountroot(self, &i915drmkms_attach_real); 168 1.1 riastrad } 169 1.1 riastrad 170 1.1 riastrad static void 171 1.1 riastrad i915drmkms_attach_real(device_t self) 172 1.1 riastrad { 173 1.1 riastrad struct i915drmkms_softc *const sc = device_private(self); 174 1.1 riastrad struct pci_attach_args *const pa = &sc->sc_pa; 175 1.6 riastrad const struct pci_device_id *ent = i915drmkms_pci_lookup(pa); 176 1.11 riastrad const struct intel_device_info *const info __diagused = 177 1.11 riastrad (struct intel_device_info *)ent->driver_data; 178 1.1 riastrad int error; 179 1.1 riastrad 180 1.1 riastrad KASSERT(info != NULL); 181 1.1 riastrad 182 1.10 riastrad /* 183 1.10 riastrad * Cause any tasks issued synchronously during attach to be 184 1.10 riastrad * processed at the end of this function. 185 1.10 riastrad */ 186 1.10 riastrad sc->sc_task_thread = curlwp; 187 1.1 riastrad 188 1.10 riastrad /* Attach the drm driver. */ 189 1.4 riastrad /* XXX errno Linux->NetBSD */ 190 1.6 riastrad error = -i915_driver_probe(&sc->sc_pci_dev, ent); 191 1.4 riastrad if (error) { 192 1.4 riastrad aprint_error_dev(self, "unable to register drm: %d\n", error); 193 1.4 riastrad return; 194 1.4 riastrad } 195 1.8 riastrad sc->sc_drm_dev = pci_get_drvdata(&sc->sc_pci_dev); 196 1.1 riastrad 197 1.10 riastrad /* 198 1.10 riastrad * Now that the drm driver is attached, we can safely suspend 199 1.10 riastrad * and resume. 200 1.10 riastrad */ 201 1.10 riastrad if (!pmf_device_register(self, &i915drmkms_suspend, 202 1.10 riastrad &i915drmkms_resume)) 203 1.10 riastrad aprint_error_dev(self, "unable to establish power handler\n"); 204 1.10 riastrad 205 1.10 riastrad /* 206 1.10 riastrad * Process asynchronous tasks queued synchronously during 207 1.10 riastrad * attach. This will be for display detection to attach a 208 1.10 riastrad * framebuffer, so we have the opportunity for a console device 209 1.10 riastrad * to attach before autoconf has completed, in time for init(8) 210 1.10 riastrad * to find that console without panicking. 211 1.10 riastrad */ 212 1.10 riastrad while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) { 213 1.1 riastrad struct i915drmkms_task *const task = 214 1.10 riastrad SIMPLEQ_FIRST(&sc->sc_tasks); 215 1.1 riastrad 216 1.10 riastrad SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, ift_u.queue); 217 1.1 riastrad (*task->ift_fn)(task); 218 1.1 riastrad } 219 1.1 riastrad 220 1.10 riastrad /* Cause any subesquent tasks to be processed by the workqueue. */ 221 1.10 riastrad atomic_store_relaxed(&sc->sc_task_thread, NULL); 222 1.1 riastrad } 223 1.1 riastrad 224 1.1 riastrad static int 225 1.1 riastrad i915drmkms_detach(device_t self, int flags) 226 1.1 riastrad { 227 1.1 riastrad struct i915drmkms_softc *const sc = device_private(self); 228 1.1 riastrad int error; 229 1.1 riastrad 230 1.1 riastrad /* XXX Check for in-use before tearing it all down... */ 231 1.1 riastrad error = config_detach_children(self, flags); 232 1.1 riastrad if (error) 233 1.1 riastrad return error; 234 1.1 riastrad 235 1.10 riastrad KASSERT(sc->sc_task_thread == NULL); 236 1.10 riastrad KASSERT(SIMPLEQ_EMPTY(&sc->sc_tasks)); 237 1.10 riastrad 238 1.10 riastrad pmf_device_deregister(self); 239 1.10 riastrad if (sc->sc_drm_dev) { 240 1.10 riastrad i915_driver_remove(sc->sc_drm_dev->dev_private); 241 1.10 riastrad sc->sc_drm_dev = NULL; 242 1.10 riastrad } 243 1.10 riastrad if (sc->sc_task_wq) { 244 1.10 riastrad workqueue_destroy(sc->sc_task_wq); 245 1.10 riastrad sc->sc_task_wq = NULL; 246 1.1 riastrad } 247 1.10 riastrad linux_pci_dev_destroy(&sc->sc_pci_dev); 248 1.1 riastrad 249 1.1 riastrad return 0; 250 1.1 riastrad } 251 1.1 riastrad 252 1.1 riastrad static bool 253 1.1 riastrad i915drmkms_suspend(device_t self, const pmf_qual_t *qual) 254 1.1 riastrad { 255 1.1 riastrad struct i915drmkms_softc *const sc = device_private(self); 256 1.1 riastrad struct drm_device *const dev = sc->sc_drm_dev; 257 1.1 riastrad int ret; 258 1.1 riastrad 259 1.14 riastrad drm_suspend_ioctl(dev); 260 1.14 riastrad 261 1.13 riastrad ret = i915_drm_prepare(dev); 262 1.13 riastrad if (ret) 263 1.13 riastrad return false; 264 1.1 riastrad ret = i915_drm_suspend(dev); 265 1.1 riastrad if (ret) 266 1.1 riastrad return false; 267 1.1 riastrad ret = i915_drm_suspend_late(dev, false); 268 1.1 riastrad if (ret) 269 1.1 riastrad return false; 270 1.1 riastrad 271 1.1 riastrad return true; 272 1.1 riastrad } 273 1.1 riastrad 274 1.1 riastrad static bool 275 1.1 riastrad i915drmkms_resume(device_t self, const pmf_qual_t *qual) 276 1.1 riastrad { 277 1.1 riastrad struct i915drmkms_softc *const sc = device_private(self); 278 1.1 riastrad struct drm_device *const dev = sc->sc_drm_dev; 279 1.1 riastrad int ret; 280 1.1 riastrad 281 1.1 riastrad ret = i915_drm_resume_early(dev); 282 1.1 riastrad if (ret) 283 1.14 riastrad goto out; 284 1.1 riastrad ret = i915_drm_resume(dev); 285 1.1 riastrad if (ret) 286 1.14 riastrad goto out; 287 1.1 riastrad 288 1.14 riastrad out: drm_resume_ioctl(dev); 289 1.14 riastrad return ret == 0; 290 1.1 riastrad } 291 1.1 riastrad 292 1.1 riastrad static void 293 1.1 riastrad i915drmkms_task_work(struct work *work, void *cookie __unused) 294 1.1 riastrad { 295 1.1 riastrad struct i915drmkms_task *const task = container_of(work, 296 1.1 riastrad struct i915drmkms_task, ift_u.work); 297 1.1 riastrad 298 1.1 riastrad (*task->ift_fn)(task); 299 1.1 riastrad } 300 1.1 riastrad 301 1.12 riastrad void 302 1.1 riastrad i915drmkms_task_schedule(device_t self, struct i915drmkms_task *task) 303 1.1 riastrad { 304 1.1 riastrad struct i915drmkms_softc *const sc = device_private(self); 305 1.1 riastrad 306 1.10 riastrad if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp) 307 1.10 riastrad SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, ift_u.queue); 308 1.10 riastrad else 309 1.10 riastrad workqueue_enqueue(sc->sc_task_wq, &task->ift_u.work, NULL); 310 1.1 riastrad } 311